Angular 检测屏幕尺寸的指令
Angular directive for detect screen size
请帮帮我,我该怎么做?
我想将指令应用于 divs,它会根据其值显示或隐藏内容,例如:*ifViewportSize="'mobile'"
<div *ifViewportSize="'large'">PC</div>
<div *ifViewportSize="'small'">Mobile</div>
指令:(在 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();
}
}
指令仅在最后 div 有效。请告诉我为什么?
我还尝试创建(就在这里,在 stackblitz 上)单独的指令 ifMobile 和 ifTablet。
我在那里实现了一个函数 window.onresize,但是这个函数同样只在最后一个 div.
上起作用
我该如何解决?
如果这是检测屏幕尺寸的错误方法,我该怎么做呢?
非常感谢!
更新
最好的解决方案不是重新发明轮子,而是使用 @angular/cdk/layout
功能:
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();
}
}
用法:
<div *ifViewportSize="'large'">PC</div>
<div *ifViewportSize="'medium'">Tablet</div>
<div *ifViewportSize="'small'">Mobile </div>
使用此解决方案的好处:
避免内存泄漏
调整大小事件以高效的方式列出(在 Angular 区域之外)
它不会在调整大小时无限地删除和重新创建模板,而是只会在遇到断点时才删除和重新创建模板
上一个回答
它仅对最后一个 div 有效,因为您正在通过 onresize
属性 监听调整大小事件, 被每个指令覆盖.
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
};
您可以使用 @HostListener
,但它 doesn't work in structural directives。所以尝试使用 window.addEventListener
代替:
ngOnInit() {
window.addEventListener('resize', this.showElem);
...
}
showElem = () => { // <-- note syntax here
...
}
ngOnDestroy() {
window.removeEventListener('resize', this.showElem);
}
Note: I would also consider listening to resize
even outside of
NgZone via ngZone.runOutsideAngular()
现在,您的调整大小处理程序应该可以工作了。
但是
您经常删除模板
目前还不清楚您将如何处理所有尺寸的边界。因为满足条件 mobileSize < window.innerWidth
,即使是 PC 大小的手机屏幕也会可见
您可以利用 angular 中的 let directive with $implicit context
与 structural directive
一起正常使用,如下所示,
<div *ifViewportSize="let view=$implicit"> .... </div>
import { Directive, ElementRef, ViewContainerRef, TemplateRef, OnInit, Input, HostListener } from '@angular/core';
export enum VIEW{
MOBILE ,
TABLET ,
PC
}
@Directive({
selector: '[responsive]'
})
export class IfViewportSizeDirective {
view;
config = {
large: 1200,
medium: 700,
small: 500
};
constructor(private readonly viewRef: ViewContainerRef,
private readonly templateRef: TemplateRef<any>) { this.checkView();}
ngOnInit(){
window.addEventListener('resize', this.checkView.bind(this));
}
checkView() {
this.viewRef.clear();
console.log(window.innerWidth);
if((0 < window.innerWidth) && (window.innerWidth < this.config['small'] )){
this.view = VIEW.MOBILE;
} else if((this.config['small'] < window.innerWidth) && (window.innerWidth < this.config['medium'])){
this.view = VIEW.TABLET;
} else if(this.config['medium'] < window.innerWidth){
this.view = VIEW.PC;
}
this.viewRef.createEmbeddedView(this.templateRef,{
$implicit: this.view
});
}
}
注意: 你仍然可以优化它。
请帮帮我,我该怎么做?
我想将指令应用于 divs,它会根据其值显示或隐藏内容,例如:*ifViewportSize="'mobile'"
<div *ifViewportSize="'large'">PC</div>
<div *ifViewportSize="'small'">Mobile</div>
指令:(在 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();
}
}
指令仅在最后 div 有效。请告诉我为什么?
我还尝试创建(就在这里,在 stackblitz 上)单独的指令 ifMobile 和 ifTablet。 我在那里实现了一个函数 window.onresize,但是这个函数同样只在最后一个 div.
上起作用我该如何解决? 如果这是检测屏幕尺寸的错误方法,我该怎么做呢? 非常感谢!
更新
最好的解决方案不是重新发明轮子,而是使用 @angular/cdk/layout
功能:
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();
}
}
用法:
<div *ifViewportSize="'large'">PC</div>
<div *ifViewportSize="'medium'">Tablet</div>
<div *ifViewportSize="'small'">Mobile </div>
使用此解决方案的好处:
避免内存泄漏
调整大小事件以高效的方式列出(在 Angular 区域之外)
它不会在调整大小时无限地删除和重新创建模板,而是只会在遇到断点时才删除和重新创建模板
上一个回答
它仅对最后一个 div 有效,因为您正在通过 onresize
属性 监听调整大小事件, 被每个指令覆盖.
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
};
您可以使用 @HostListener
,但它 doesn't work in structural directives。所以尝试使用 window.addEventListener
代替:
ngOnInit() {
window.addEventListener('resize', this.showElem);
...
}
showElem = () => { // <-- note syntax here
...
}
ngOnDestroy() {
window.removeEventListener('resize', this.showElem);
}
Note: I would also consider listening to
resize
even outside of NgZone viangZone.runOutsideAngular()
现在,您的调整大小处理程序应该可以工作了。
但是
您经常删除模板
目前还不清楚您将如何处理所有尺寸的边界。因为满足条件
,即使是 PC 大小的手机屏幕也会可见mobileSize < window.innerWidth
您可以利用 angular 中的 let directive with $implicit context
与 structural directive
一起正常使用,如下所示,
<div *ifViewportSize="let view=$implicit"> .... </div>
import { Directive, ElementRef, ViewContainerRef, TemplateRef, OnInit, Input, HostListener } from '@angular/core';
export enum VIEW{
MOBILE ,
TABLET ,
PC
}
@Directive({
selector: '[responsive]'
})
export class IfViewportSizeDirective {
view;
config = {
large: 1200,
medium: 700,
small: 500
};
constructor(private readonly viewRef: ViewContainerRef,
private readonly templateRef: TemplateRef<any>) { this.checkView();}
ngOnInit(){
window.addEventListener('resize', this.checkView.bind(this));
}
checkView() {
this.viewRef.clear();
console.log(window.innerWidth);
if((0 < window.innerWidth) && (window.innerWidth < this.config['small'] )){
this.view = VIEW.MOBILE;
} else if((this.config['small'] < window.innerWidth) && (window.innerWidth < this.config['medium'])){
this.view = VIEW.TABLET;
} else if(this.config['medium'] < window.innerWidth){
this.view = VIEW.PC;
}
this.viewRef.createEmbeddedView(this.templateRef,{
$implicit: this.view
});
}
}
注意: 你仍然可以优化它。