用于检测屏幕尺寸的 Angular 指令
Posted
技术标签:
【中文标题】用于检测屏幕尺寸的 Angular 指令【英文标题】:Angular directive for detect screen size 【发布时间】:2020-11-18 21:38:02 【问题描述】:请帮帮我,我该怎么做?
我想对 div 应用指令,它将根据其值显示或隐藏内容,例如:*ifViewportSize="'mobile'"
<div *ifViewportSize="'large'">PC</div>
<div *ifViewportSize="'small'">Mobile</div>
指令:(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();
指令仅在最后一个 div 中有效。请告诉我为什么?
我还尝试创建(就在这里,在 stackblitz 上)单独的指令 ifMobile и ifTablet。 我在那里实现了一个函数window.onresize,但同样这个函数只适用于最后一个div。
我该如何解决? 如果这是检测屏幕尺寸的错误方法,我该怎么做呢? 非常感谢!
【问题讨论】:
您可以使用material.angular.io/cdk/layout/overview 找到解决方案,而不是重新发明***。使用该包,您可以监听组件中的媒体查询更改并采取相应措施 在您的案例中,移动、桌面和 PC 屏幕的界限是什么?0 - 576
- 手机,576 - 768
- 平板电脑,768 - 992
- PC?
@Jelle 但我必须在每个组件中监听 breakpointObserver。也许有没有办法不复制所有组件中的代码?
@yurzui 是的,但这是例如,我试图了解如何做这样的问题并找到最佳实践。
@kompaniietst 您始终可以在单独的服务中创建观察者。与真正想要收听它的组件共享它
【参考方案1】:
更新
最好的解决方案不是重新发明***,而是使用@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
使用此解决方案的好处:
避免内存泄漏
Resize 事件以高效的方式列出(在 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);
注意:我也会考虑收听
resize
,即使是在外面 NgZone 来自ngZone.runOutsideAngular()
现在,您的调整大小处理程序应该可以工作了。
Forked Stackblitz
但是
你一直在删除模板
目前还不清楚您将如何处理所有尺寸的边界。因为手机屏幕满足条件mobileSize < window.innerWidth
【讨论】:
非常感谢!它看起来合乎逻辑并且有效。但我能问一下,也许你知道其他更好的解决方案,比如我的? 是的,请看一下。我添加了替代选项【参考方案2】:您可以利用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
);
FULL DEMO
注意:您仍然可以优化它。
【讨论】:
我会指出这个解决方案的几个问题:1. 你永远不会从这段代码中删除监听器window.addEventListener('resize', this.checkView.bind(this));
2. 你总是在调整大小时删除模板。 3 您在 Angular 区域内运行代码,这可能会导致性能问题
如果您查看了我的最后一条评论it still can be improved
。 1) 您可以删除ngDestory
上的侦听器。 2)而不是总是删除/更新模板,逻辑可以检测何时删除/附加视图。 3) Angular zone :这可以被认为是正确地在区域之外运行并手动触发更改检测。这只是一些提醒,所以 OP 可以开始。 肯定可以优化。有时,重新发明***也可以学习新知识并根据我们的要求对其进行定制。
1.如果使用.bind
添加,则无法在 ngDestroy 中删除监听器
我认为,您没有理解 IT CAN BE IMPROVED 这句话。我不是来编写无错误或无内存泄漏的代码。如果这是目的,许多第三方工具可供选择。你只是指出并走开,但那是行不通的。那不是目的。 OP 自己尝试了一些东西,并希望拥有比他实施的更好的东西。就是这样!
我不想伤害你,我得到了句子 IT CAN BE IMPROVED 但是如果它最初写有错误,对于 OP 很难改进(我意思是addEventListener
和bind
)以上是关于用于检测屏幕尺寸的 Angular 指令的主要内容,如果未能解决你的问题,请参考以下文章
Xcode - 如何检测 iPhone 的屏幕尺寸 [关闭]