Skip to content

Commit aeb1464

Browse files
author
Markus Block
committed
feat/update:
- updated logic and version for angular v20 - switched logic from ElementRef to Templates to keep element bindings
1 parent 79caefa commit aeb1464

File tree

5 files changed

+69
-85
lines changed

5 files changed

+69
-85
lines changed

README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import {NgxMarqueeComponent} from '@omnedia/ngx-marquee';
4444

4545
Use the component in your template:
4646

47+
### Version 1.\*.\* - 2.\*.\*
48+
4749
```html
4850

4951
<om-marquee
@@ -65,6 +67,35 @@ Use the component in your template:
6567
</om-marquee>
6668
```
6769

70+
### Version ^3.\*.\*
71+
72+
```html
73+
74+
<om-marquee
75+
[reverse]="true"
76+
[animationDuration]="'10s'"
77+
[marqueeGap]="'2rem'"
78+
[pauseOnHover]="true"
79+
styleClass="custom-marquee"
80+
>
81+
<ng-template>
82+
<div class="item">
83+
Item 1
84+
</div>
85+
</ng-template>
86+
<ng-template>
87+
<div class="item">
88+
Item 2
89+
</div>
90+
</ng-template>
91+
<ng-template>
92+
<div class="item">
93+
Item 3
94+
</div>
95+
</ng-template>
96+
</om-marquee>
97+
```
98+
6899
How It Works
69100

70101
- Customizable Direction and Animation: You can set the marquee to scroll content horizontally or vertically by setting the vertical input. You can also control the speed, gap between items, reverse the scroll direction, and pause the scroll when hovered.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "@omnedia/ngx-marquee",
33
"description": "A simple component library to create an infinite scrolling marquee with your content.",
4-
"version": "2.2.0",
4+
"version": "3.0.0",
55
"peerDependencies": {
6-
"@angular/common": "^19.0.0",
7-
"@angular/core": "^19.0.0"
6+
"@angular/common": "^20.0.0",
7+
"@angular/core": "^20.0.0"
88
},
99
"dependencies": {
1010
"tslib": "^2.3.0"

src/lib/ngx-marquee.component.html

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,33 @@
66
[ngClass]="styleClass"
77
#OmMarquee
88
>
9-
<div class="om-marquee-invisible">
10-
<ng-content></ng-content>
11-
</div>
12-
<div class="om-marquee-content" [class.outOfView]="!isInView" [class.scrollable]="scrollable">
9+
<div class="om-marquee-content" [class.outOfView]="!isInView()" [class.scrollable]="scrollable">
1310
<div class="om-marquee-item-wrapper">
14-
@for (content of marqueeElements; track $index) {
15-
<div class="om-marquee-item" [innerHTML]="content"></div>
11+
@for (template of templates; track $index) {
12+
<div class="om-marquee-item">
13+
<ng-container *ngTemplateOutlet="template; context: { index: $index }"></ng-container>
14+
</div>
1615
}
1716
</div>
1817
<div class="om-marquee-item-wrapper">
19-
@for (content of marqueeElements; track $index) {
20-
<div class="om-marquee-item" [innerHTML]="content"></div>
18+
@for (template of templates; track $index) {
19+
<div class="om-marquee-item">
20+
<ng-container *ngTemplateOutlet="template; context: { index: $index }"></ng-container>
21+
</div>
2122
}
2223
</div>
2324
<div class="om-marquee-item-wrapper">
24-
@for (content of marqueeElements; track $index) {
25-
<div class="om-marquee-item" [innerHTML]="content"></div>
25+
@for (template of templates; track $index) {
26+
<div class="om-marquee-item">
27+
<ng-container *ngTemplateOutlet="template; context: { index: $index }"></ng-container>
28+
</div>
2629
}
2730
</div>
2831
<div class="om-marquee-item-wrapper">
29-
@for (content of marqueeElements; track $index) {
30-
<div class="om-marquee-item" [innerHTML]="content"></div>
32+
@for (template of templates; track $index) {
33+
<div class="om-marquee-item">
34+
<ng-container *ngTemplateOutlet="template; context: { index: $index }"></ng-container>
35+
</div>
3136
}
3237
</div>
3338
</div>

src/lib/ngx-marquee.component.scss

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@
77
width: 100%;
88
height: 100%;
99

10-
.om-marquee-invisible {
11-
width: 0;
12-
height: 0;
13-
display: none;
14-
pointer-events: none;
15-
}
16-
1710
.om-marquee-content {
1811
width: 100%;
1912
height: 100%;
@@ -35,8 +28,7 @@
3528

3629
.om-marquee-item-wrapper {
3730
flex-direction: row;
38-
animation: om-marquee-row var(--om-marquee-animation-duration) infinite
39-
linear var(--om-marquee-reverse);
31+
animation: om-marquee-row var(--om-marquee-animation-duration) infinite linear var(--om-marquee-reverse);
4032
}
4133
}
4234
}
@@ -47,8 +39,7 @@
4739

4840
.om-marquee-item-wrapper {
4941
flex-direction: column;
50-
animation: om-marquee-column var(--om-marquee-animation-duration)
51-
infinite linear var(--om-marquee-reverse);
42+
animation: om-marquee-column var(--om-marquee-animation-duration) infinite linear var(--om-marquee-reverse);
5243
}
5344
}
5445
}

src/lib/ngx-marquee.component.ts

Lines changed: 16 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { CommonModule, isPlatformBrowser } from "@angular/common";
1+
import {CommonModule, isPlatformBrowser} from "@angular/common";
22
import {
3-
AfterContentChecked,
43
AfterViewInit,
5-
ChangeDetectorRef,
4+
ChangeDetectionStrategy,
65
Component,
76
ContentChildren,
87
ElementRef,
@@ -12,27 +11,26 @@ import {
1211
OnDestroy,
1312
PLATFORM_ID,
1413
QueryList,
14+
signal,
1515
SimpleChanges,
16+
TemplateRef,
1617
ViewChild,
1718
} from "@angular/core";
18-
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
19-
import { Subject } from "rxjs";
19+
import {Subject} from "rxjs";
2020

2121
@Component({
2222
selector: "om-marquee",
2323
standalone: true,
2424
imports: [CommonModule],
2525
templateUrl: "./ngx-marquee.component.html",
2626
styleUrl: "./ngx-marquee.component.scss",
27+
changeDetection: ChangeDetectionStrategy.OnPush,
2728
})
2829
export class NgxMarqueeComponent
29-
implements AfterViewInit, AfterContentChecked, OnDestroy, OnChanges
30-
{
30+
implements AfterViewInit, OnDestroy, OnChanges {
3131
@ViewChild("OmMarquee") marqueeRef!: ElementRef<HTMLElement>;
3232

33-
@ContentChildren("OmMarqueeContent") elementRefs?: QueryList<
34-
ElementRef<HTMLElement>
35-
>;
33+
@ContentChildren(TemplateRef) templates!: QueryList<TemplateRef<any>>;
3634

3735
@Input("styleClass")
3836
styleClass?: string;
@@ -83,32 +81,23 @@ export class NgxMarqueeComponent
8381

8482
style: any = {};
8583

86-
marqueeElements: SafeHtml[] = [];
87-
88-
isInView = false;
84+
isInView = signal(false);
8985
private intersectionObserver?: IntersectionObserver;
9086

91-
private contentSnapshot: string[] = [];
92-
9387
constructor(
94-
private readonly sanitizer: DomSanitizer,
95-
private cdr: ChangeDetectorRef,
9688
@Inject(PLATFORM_ID) private platformId: object,
97-
) {}
89+
) {
90+
}
9891

9992
ngAfterViewInit(): void {
100-
this.getMarqueeContent();
101-
10293
if (isPlatformBrowser(this.platformId)) {
10394
this.intersectionObserver = new IntersectionObserver(([entry]) => {
10495
if (entry.isIntersecting) {
105-
if (!this.isInView) {
106-
this.isInView = true;
107-
this.cdr.detectChanges();
96+
if (!this.isInView()) {
97+
this.isInView.set(true);
10898
}
10999
} else if (this.isInView) {
110-
this.isInView = false;
111-
this.cdr.detectChanges();
100+
this.isInView.set(false);
112101
}
113102
});
114103
this.intersectionObserver.observe(this.marqueeRef.nativeElement);
@@ -119,24 +108,6 @@ export class NgxMarqueeComponent
119108
}
120109
}
121110

122-
ngAfterContentChecked() {
123-
if (!this.elementRefs) {
124-
return;
125-
}
126-
127-
const currentContentSnapshot = this.elementRefs.map(
128-
(ref) => ref.nativeElement.innerHTML,
129-
);
130-
131-
if (
132-
JSON.stringify(this.contentSnapshot) !==
133-
JSON.stringify(currentContentSnapshot)
134-
) {
135-
this.contentSnapshot = currentContentSnapshot;
136-
this.getMarqueeContent();
137-
}
138-
}
139-
140111
ngOnChanges(changes: SimpleChanges): void {
141112
if (changes["scrollable"] && !changes["scrollable"].firstChange) {
142113
const newVal = changes["scrollable"].currentValue;
@@ -165,20 +136,6 @@ export class NgxMarqueeComponent
165136
this.unListeners = [];
166137
}
167138

168-
private getMarqueeContent(): void {
169-
if (!this.elementRefs) {
170-
return;
171-
}
172-
173-
this.marqueeElements = this.elementRefs?.toArray().map((ref) => {
174-
return this.sanitizer.bypassSecurityTrustHtml(
175-
ref.nativeElement.outerHTML,
176-
);
177-
});
178-
179-
this.cdr.detectChanges();
180-
}
181-
182139
private initDragEvents(): void {
183140
const marqueeContent = this.marqueeRef.nativeElement.querySelector(
184141
".om-marquee-content",
@@ -226,7 +183,7 @@ export class NgxMarqueeComponent
226183

227184
const gap = parseFloat(
228185
getComputedStyle(firstWrapper).getPropertyValue("--om-marquee-gap") ||
229-
"0",
186+
"0",
230187
);
231188

232189
const limit = -(wrapperSize + gap);
@@ -257,7 +214,7 @@ export class NgxMarqueeComponent
257214

258215
const gap = parseFloat(
259216
getComputedStyle(firstWrapper).getPropertyValue("--om-marquee-gap") ||
260-
"0",
217+
"0",
261218
);
262219

263220
const totalDistance = wrapperSize + gap;

0 commit comments

Comments
 (0)