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>

Stackblitz Example

使用此解决方案的好处:

  1. 避免内存泄漏

  2. 调整大小事件以高效的方式列出(在 Angular 区域之外)

  3. 它不会在调整大小时无限地删除和重新创建模板,而是只会在遇到断点时才删除和重新创建模板

上一个回答

它仅对最后一个 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()

现在,您的调整大小处理程序应该可以工作了。

Forked Stackblitz

但是

  • 您经常删除模板

  • 目前还不清楚您将如何处理所有尺寸的边界。因为满足条件 mobileSize < window.innerWidth

    ,即使是 PC 大小的手机屏幕也会可见

您可以利用 angular 中的 let directive with $implicit contextstructural 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
     });
    }
  

}

FULL DEMO

注意: 你仍然可以优化它。