Help me, please, how can I do that?
I want to apply directive to the divs, it will be show or hide content depending of it's value, for example: *ifViewportSize="'mobile'"
<div *ifViewportSize="'large'">PC</div>
<div *ifViewportSize="'small'">Mobile</div>
Directive: (rezult in console.log) https://stackblitz.com/edit/angular-ivy-wdl8ee
@Directive({
selector: '[ifViewportSize]'
})
export class IfViewportSizeDirective {
size: string;
config = {
large: 992,
medium: 768,
small: 576
};
constructor(
private elemRef: ElementRef,
private vcRef: ViewContainerRef,
private templRef: TemplateRef<any>) {
window.onresize = (event) => {
this.showElem();
};
}
@Input() set ifViewportSize(size: string) {
this.size = size;
}
ngOnInit() {
this.showElem();
}
showElem() {
console.log('size: ',this.size);
if (this.config[this.size] < window.innerWidth) {
this.vcRef.clear();
this.vcRef.createEmbeddedView(this.templRef);
}
else this.vcRef.clear();
}
}
Directive works only in the last div. Please tell me why?
Also I tried to create (right here, on stackblitz) separately directives ifMobile и ifTablet. I implemented there a function window.onresize, but again this function works only on the last div.
How can I fix it? If this is a wrong way to detect the screen size, how can I do this right? Thanks a lot!
The best solution would be not to reinvent the wheel but rather use @angular/cdk/layout functionality:
if-viewport-size.directive.ts
type Size = 'small' | 'medium' | 'large';
const config = {
small: [Breakpoints.Small, Breakpoints.XSmall],
medium: [Breakpoints.Medium],
large: [Breakpoints.Large, Breakpoints.XLarge]
};
@Directive({
selector: "[ifViewportSize]"
})
export class IfViewportSizeDirective implements OnDestroy {
private subscription = new Subscription();
@Input("ifViewportSize") set size(value: Size) {
this.subscription.unsubscribe();
this.subscription = this.observer
.observe(config[value])
.subscribe(this.updateView);
}
constructor(
private observer: BreakpointObserver,
private vcRef: ViewContainerRef,
private templateRef: TemplateRef<any>
) {}
updateView = ({ matches }: BreakpointState) => {
if (matches && !this.vcRef.length) {
this.vcRef.createEmbeddedView(this.templateRef);
} else if (!matches && this.vcRef.length) {
this.vcRef.clear();
}
};
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
Usage:
<div *ifViewportSize="'large'">PC</div>
<div *ifViewportSize="'medium'">Tablet</div>
<div *ifViewportSize="'small'">Mobile </div>
Stackblitz Example
The benefits of using this solution:
It avoids memory leak
Resize event is listed in a performant way(outside of Angular zone)
It doesn't remove and recreate template infinitely on resize but rather only once it hits breakpoint
It works only for the last div because you're listening for resize event through onresize property which is overrided be each directive.
window.onresize = (event) => {
this.showElem();
};
window.onresize = (event) => { <---- you completely replace onresize event
this.showElem();
};
...
window.onresize = (event) => { <---- one more replacement
this.showElem(); <-------------- only this handler will be executed
};
You could use @HostListener instead but it doesn't work in structural directives. So try using window.addEventListener instead:
ngOnInit() {
window.addEventListener('resize', this.showElem);
...
}
showElem = () => { // <-- note syntax here
...
}
ngOnDestroy() {
window.removeEventListener('resize', this.showElem);
}
Note: I would also consider listening to
resizeeven outside of NgZone viangZone.runOutsideAngular()
Now, your resize handler should work.
Forked Stackblitz
you're constantly removing template
it's quite unclear how you're going to handle boundaries for all your sizes. Because mobile screen will be visible even for PC size since it meets condition mobileSize < window.innerWidth
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With