角度窗口调整大小事件
Posted
技术标签:
【中文标题】角度窗口调整大小事件【英文标题】:Angular window resize event 【发布时间】:2016-06-02 08:00:33 【问题描述】:我想根据窗口调整大小事件(加载时和动态)执行一些任务。
目前我的 DOM 如下:
<div id="Harbour">
<div id="Port" (window:resize)="onResize($event)" >
<router-outlet></router-outlet>
</div>
</div>
事件正确触发
export class AppComponent
onResize(event)
console.log(event);
如何从这个事件对象中获取宽度和高度?
谢谢。
【问题讨论】:
不是一个真正的 Angular 问题。查看window 对象。你可以得到它的innerHeight
和innerWidth
属性..
@Sasxa 是正确的,你只需要做console.log(event.target.innerWidth )
感谢 Sasxa/Pankaj 提供的信息 - 我不确定它是否只是普通的 javascript 事物、Typescript 事物或 Angular 事件事物。我在这里为自己攀登了一条非常陡峭的学习曲线,感谢您的意见。
【参考方案1】:
在 Angular CDK 中有一个 ViewportRuler 服务。它在区域外运行,支持 orientationchange 和 resize。它也适用于服务器端渲染。
@Component(
selector: 'my-app',
template: `
<p>Viewport size: width x height </p>
`
)
export class AppComponent implements OnDestroy
width: number;
height: number;
private readonly viewportChange = this.viewportRuler
.change(200)
.subscribe(() => this.ngZone.run(() => this.setSize()));
constructor(
private readonly viewportRuler: ViewportRuler,
private readonly ngZone: NgZone
)
// Change happens well, on change. The first load is not a change, so we init the values here. (You can use `startWith` operator too.)
this.setSize();
// Never forget to unsubscribe!
ngOnDestroy()
this.viewportChange.unsubscribe();
private setSize()
const width, height = this.viewportRuler.getViewportSize();
this.width = width;
this.height = height;
Stackblitz example for ViewportRuler
好处是,它限制了更改检测周期(它只会在您在区域中运行回调时触发),而(window:resize)
将在每次调用时触发更改检测。
【讨论】:
这应该是公认的答案。拥有所需的一切 + 这是 Angular CDK 内置功能。 这个答案需要更多的支持。在性能和兼容性方面,这是最好的解决方案 试过这个并让它工作....性能比上面的选项慢得多。 @pistol-pete 我添加了一个示例来展示它的高性能。【参考方案2】:我采取的另一种方法是
import Component, OnInit from '@angular/core';
import fromEvent from "rxjs";
import debounceTime, map, startWith from "rxjs/operators";
function windowSizeObserver(dTime = 300)
return fromEvent(window, 'resize').pipe(
debounceTime(dTime),
map(event =>
const window = event.target as Window;
return width: window.innerWidth, height: window.innerHeight
),
startWith(width: window.innerWidth, height: window.innerHeight)
);
@Component(
selector: 'app-root',
template: `
<h2>Window Size</h2>
<div>
<span>Height: (windowSize$ | async)?.height</span>
<span>Width: (windowSize$ | async)?.width</span>
</div>
`
)
export class WindowSizeTestComponent
windowSize$ = windowSizeObserver();
这里的windowSizeObserver
可以在任何组件中重复使用
【讨论】:
【参考方案3】:我的做法如下,很像 Johannes Hoppe 建议的:
import EventManager from '@angular/platform-browser';
import Injectable, EventEmitter from '@angular/core';
@Injectable()
export class ResizeService
public onResize$ = new EventEmitter< width: number; height: number; >();
constructor(eventManager: EventManager)
eventManager.addGlobalEventListener('window', 'resize',
event => this.onResize$.emit(
width: event.target.innerWidth,
height: event.target.innerHeight
));
getWindowSize()
this.onResize$.emit(
width: window.innerWidth,
height: window.innerHeight
);
在 app.component.ts 中:
Import ResizeService from ".shared/services/resize.service"
import Component from "@angular/core"
@Component(
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
)
export class AppComponent
windowSize: width: number, height: number;
constructor(private resizeService: ResizeService)
ngOnInit()
this.resizeService.onResize$.subscribe((value) =>
this.windowSize = value;
);
this.resizeService.getWindowSize();
然后在你的 app.component.html 中:
<router-outlet *ngIf = "windowSize?.width > 1280 && windowSize?.height > 700; else errorComponent">
</router-outlet>
<ng-template #errorComponent>
<app-error-component></app-error-component>
</ng-template>
【讨论】:
【参考方案4】:这是我创建的一个简单而干净的解决方案,因此我可以将它注入到多个组件中。
ResizeService.ts
import Injectable from '@angular/core';
import Subject from 'rxjs';
@Injectable(
providedIn: 'root'
)
export class ResizeService
constructor()
window.addEventListener('resize', (e) =>
this.onResize.next();
);
public onResize = new Subject();
使用中:
constructor(
private resizeService: ResizeService
)
this.subscriptions.push(this.resizeService.onResize.subscribe(() =>
// Do stuff
));
private subscriptions: Subscription[] = [];
【讨论】:
【参考方案5】:这是使用最新版本的 Rxjs 对上述@GiridharKamik 答案的更新。
import Injectable from '@angular/core';
import Observable, BehaviorSubject, fromEvent from 'rxjs';
import pluck, distinctUntilChanged, map from 'rxjs/operators';
@Injectable()
export class WindowService
height$: Observable<number>;
constructor()
const windowSize$ = new BehaviorSubject(getWindowSize());
this.height$ = windowSize$.pipe(pluck('height'), distinctUntilChanged());
fromEvent(window, 'resize').pipe(map(getWindowSize))
.subscribe(windowSize$);
function getWindowSize()
return
height: window.innerHeight
//you can sense other parameters here
;
;
【讨论】:
【参考方案6】:我检查了大部分答案。然后决定查看 Layout 上的 Angular 文档。
Angular 有自己的 Observer 用于检测不同的大小,并且很容易实现到组件或服务中。
一个简单的例子是:
import BreakpointObserver, Breakpoints from '@angular/cdk/layout';
@Component(...)
class MyComponent
constructor(breakpointObserver: BreakpointObserver)
breakpointObserver.observe([
Breakpoints.HandsetLandscape,
Breakpoints.HandsetPortrait
]).subscribe(result =>
if (result.matches)
this.activateHandsetLayout();
);
希望对你有帮助
【讨论】:
很高兴知道...但请注意...这是材料库 Angular 文档,而不是 Angular 本身的官方文档。有点暗示所有断点都将遵循材料指南 OOTB。【参考方案7】:所有解决方案都不适用于服务器端或角度通用
【讨论】:
【参考方案8】:<div (window:resize)="onResize($event)"
onResize(event)
event.target.innerWidth;
或使用HostListener decorator:
@HostListener('window:resize', ['$event'])
onResize(event)
event.target.innerWidth;
支持的全局目标是 window
、document
和 body
。
在 Angular 中实现 https://github.com/angular/angular/issues/13248 之前,强制订阅 DOM 事件并使用 RXJS 减少事件数量会更好,如其他一些答案所示。
【讨论】:
是否有任何关于您使用的语法的文档:window:resize? 没错。您可以使用document
、window
和body
github.com/angular/angular/blob/…
完美答案.. 我相信@HostListener 是更干净的方式:) 但请确保首先使用import Component, OnInit, HostListener from '@angular/core';
导入HostListener
快速提示:如果也想在首次加载时触发,请从 @angular/core 实现 ngAfterViewInit。 angular.io/api/core/AfterViewInit
当然,但是,对于那些想知道 HostListener 如何与 debounceTime 一起工作的人,请点击下面的链接plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview【参考方案9】:
我知道很久以前有人问过这个问题,但现在有更好的方法来做到这一点!我不确定是否有人会看到这个答案。显然你的进口:
import fromEvent, Observable, Subscription from "rxjs";
然后在你的组件中:
resizeObservable$: Observable<Event>
resizeSubscription$: Subscription
ngOnInit()
this.resizeObservable$ = fromEvent(window, 'resize')
this.resizeSubscription$ = this.resizeObservable$.subscribe( evt =>
console.log('event: ', evt)
)
那么一定要在销毁时取消订阅!
ngOnDestroy()
this.resizeSubscription$.unsubscribe()
【讨论】:
唯一对我有用的方法。谢谢!! :-) ...我只需要调整您的导入。也许我的 rxjs 更新:import fromEvent, Observable,Subscription from "rxjs";
在哪里可以添加 debounce(1000)?
抱歉回复晚了:要添加去抖动功能,您需要使用this.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => console.log('event: ', evt) )
对于去抖动,从 'rxjs/operators' 导入 debounceTime ;
尽管使用debounceTime
并减少了订阅回调,Angular 仍将对每个事件运行更改检测,因为它是在区域内触发的。您应该检查我的答案以消除此问题。【参考方案10】:
下面的代码让我们观察 Angular 中任何给定 div 的任何大小变化。
<div #observed-div>
</div>
然后在组件中:
oldWidth = 0;
oldHeight = 0;
@ViewChild('observed-div') myDiv: ElementRef;
ngAfterViewChecked()
const newWidth = this.myDiv.nativeElement.offsetWidth;
const newHeight = this.myDiv.nativeElement.offsetHeight;
if (this.oldWidth !== newWidth || this.oldHeight !== newHeight)
console.log('resized!');
this.oldWidth = newWidth;
this.oldHeight = newHeight;
【讨论】:
我想在最大宽度 767px 处将一个变量从 counter = 6 更改为 count = 0,我该怎么做?【参考方案11】:我还没有看到有人谈论 MediaMatcher
或 angular/cdk
。
您可以定义一个 MediaQuery 并将一个侦听器附加到它 - 然后如果匹配器匹配,您可以在模板(或 ts)上的任何位置调用东西。 LiveExample
App.Component.ts
import Component, ChangeDetectorRef from '@angular/core';
import MediaMatcher from '@angular/cdk/layout';
@Component(
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
)
export class AppComponent
mobileQuery: MediaQueryList;
constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher)
this.mobileQuery = media.matchMedia('(max-width: 600px)');
this._mobileQueryListener = () => changeDetectorRef.detectChanges();
this.mobileQuery.addListener(this._mobileQueryListener);
private _mobileQueryListener: () => void;
ngOnDestroy()
this.mobileQuery.removeListener(this._mobileQueryListener);
App.Component.Html
<div [class]="mobileQuery.matches ? 'text-red' : 'text-blue'"> I turn red on mobile mode
</div>
App.Component.css
.text-red
color: red;
.text-blue
color: blue;
来源:https://material.angular.io/components/sidenav/overview
【讨论】:
【参考方案12】:这不是问题的确切答案,但它可以帮助需要检测任何元素大小变化的人。
我创建了一个库,可以将resized
事件添加到任何元素 - Angular Resize Event。
它在内部使用来自CSS Element Queries 的ResizeSensor
。
示例用法
HTML
<div (resized)="onResized($event)"></div>
打字稿
@Component(...)
class MyComponent
width: number;
height: number;
onResized(event: ResizedEvent): void
this.width = event.newWidth;
this.height = event.newHeight;
【讨论】:
如果你想检测窗口调整大小,那么 Angular Material Breakpoint Observer 已经处理 material.angular.io/cdk/layout/overview 但这并不仅仅检测窗口调整大小,而是检测任何元素调整大小。【参考方案13】:基于@cgatian 的解决方案,我建议进行以下简化:
import EventManager from '@angular/platform-browser';
import Injectable, EventEmitter from '@angular/core';
@Injectable()
export class ResizeService
public onResize$ = new EventEmitter< width: number; height: number; >();
constructor(eventManager: EventManager)
eventManager.addGlobalEventListener('window', 'resize',
e => this.onResize$.emit(
width: e.target.innerWidth,
height: e.target.innerHeight
));
用法:
import Component from '@angular/core';
import ResizeService from './resize-service';
@Component(
selector: 'my-component',
template: ` rs.onResize$ | async | json `
)
export class MyComponent
constructor(private rs: ResizeService)
【讨论】:
我发现这是最好的解决方案,但是,这仅在调整窗口大小时有效,而在加载或路由器更改时无效。你知道,如何申请路由器更换、重新加载或加载吗? 可以在服务中添加一个函数并在组件@jcdsr中触发:getScreenSize() this.onResize$.emit( width: window.innerWidth, height: window.innerHeight ); 【参考方案14】:这样做的正确方法是利用EventManager 类来绑定事件。这允许您的代码在其他平台上工作,例如使用 Angular Universal 进行服务器端渲染。
import EventManager from '@angular/platform-browser';
import Observable from 'rxjs/Observable';
import Subject from 'rxjs/Subject';
import Injectable from '@angular/core';
@Injectable()
export class ResizeService
get onResize$(): Observable<Window>
return this.resizeSubject.asObservable();
private resizeSubject: Subject<Window>;
constructor(private eventManager: EventManager)
this.resizeSubject = new Subject();
this.eventManager.addGlobalEventListener('window', 'resize', this.onResize.bind(this));
private onResize(event: UIEvent)
this.resizeSubject.next(<Window>event.target);
在组件中的使用很简单,只需将此服务作为提供程序添加到您的 app.module 中,然后将其导入组件的构造函数中。
import Component, OnInit from '@angular/core';
@Component(
selector: 'my-component',
template: ``,
styles: [``]
)
export class MyComponent implements OnInit
private resizeSubscription: Subscription;
constructor(private resizeService: ResizeService)
ngOnInit()
this.resizeSubscription = this.resizeService.onResize$
.subscribe(size => console.log(size));
ngOnDestroy()
if (this.resizeSubscription)
this.resizeSubscription.unsubscribe();
【讨论】:
据我所知,这不会在移动设备上触发。您如何使用这种方法获得初始窗口大小? 移动设备没有window
对象,就像服务器没有window
一样。我不熟悉不同的移动配置,但您应该能够轻松调整上述代码以绑定到正确的全局事件侦听器
@cgatian 我是菜鸟,但这似乎是正确的答案。不幸的是,我没有运气让我的订阅登录组件。您能否在答案中添加如何在组件中订阅此内容以便可以看到更新?
@cgatian 我会做一个 plunker 但这似乎行不通。服务中的过滤器似乎很奇怪***.com/q/46397531/1191635
@cgatian Mobile does not have a window object
.... 为什么你认为移动浏览器没有窗口对象?【参考方案15】:
我写了this lib 来查找 Angular 中的组件边界大小更改(调整大小),这可能对其他人有所帮助。你可以把它放在根组件上,和调整窗口大小一样。
第一步:导入模块
import BoundSensorModule from 'angular-bound-sensor';
@NgModule(
(...)
imports: [
BoundSensorModule,
],
)
export class AppModule
第 2 步:添加如下指令
<simple-component boundSensor></simple-component>
第 3 步:接收边界尺寸详细信息
import HostListener from '@angular/core';
@Component(
selector: 'simple-component'
(...)
)
class SimpleComponent
@HostListener('resize', ['$event'])
onResize(event)
console.log(event.detail);
【讨论】:
【参考方案16】:假设
首先我们需要当前的窗口大小。所以我们创建了一个 observable,它只发出一个值:当前窗口大小。
initial$ = Observable.of(window.innerWidth > 599 ? false : true);
然后我们需要创建另一个 observable,以便我们知道窗口大小何时改变。为此,我们可以使用“fromEvent”运算符。想了解更多关于 rxjs 的操作符请访问:rxjs
resize$ = Observable.fromEvent(window, 'resize').map((event: any) =>
return event.target.innerWidth > 599 ? false : true;
);
合并这两个流以接收我们的 observable:
mobile$ = Observable.merge(this.resize$, this.initial$).distinctUntilChanged();
现在您可以像这样订阅它:
mobile$.subscribe((event) => console.log(event); );
记得退订:)
【讨论】:
【参考方案17】:在 Angular2 (2.1.0) 上,我使用 ngZone 来捕获屏幕更改事件。
看一下例子:
import Component, NgZone from '@angular/core';//import ngZone library
...
//capture screen changed inside constructor
constructor(private ngZone: NgZone)
window.onresize = (e) =>
ngZone.run(() =>
console.log(window.innerWidth);
console.log(window.innerHeight);
);
;
希望对你有所帮助!
【讨论】:
【参考方案18】:这是一个更好的方法。基于Birowsky's 的回答。
第 1 步:使用 RxJS Observables 创建一个 angular service
。
import Injectable from '@angular/core';
import Observable, BehaviorSubject from 'rxjs';
@Injectable()
export class WindowService
height$: Observable<number>;
//create more Observables as and when needed for various properties
hello: string = "Hello";
constructor()
let windowSize$ = new BehaviorSubject(getWindowSize());
this.height$ = (windowSize$.pluck('height') as Observable<number>).distinctUntilChanged();
Observable.fromEvent(window, 'resize')
.map(getWindowSize)
.subscribe(windowSize$);
function getWindowSize()
return
height: window.innerHeight
//you can sense other parameters here
;
;
第 2 步:注入上述service
并订阅在服务中创建的任何Observables
,只要您想接收窗口调整大小事件。
import Component from '@angular/core';
//import service
import WindowService from '../Services/window.service';
@Component(
selector: 'pm-app',
templateUrl: './componentTemplates/app.component.html',
providers: [WindowService]
)
export class AppComponent
constructor(private windowService: WindowService)
//subscribe to the window resize event
windowService.height$.subscribe((value:any) =>
//Do whatever you want with the value.
//You can also subscribe to other observables of the service
);
对反应式编程有充分的理解总是有助于克服困难的问题。希望这对某人有所帮助。
【讨论】:
我相信这是一个错误:this.height$ = (windowSize$.pluck('height') as Observable@Günter 的回答是正确的。我只是想提出另一种方法。
您还可以在@Component()
-decorator 中添加主机绑定。您可以将事件和所需的函数调用放在 host-metadata-property 中,如下所示:
@Component(
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
host:
'(window:resize)': 'onResize($event)'
)
export class AppComponent
onResize(event)
event.target.innerWidth; // window width
【讨论】:
我已经尝试了此页面上的所有方法,但似乎它们都不起作用。有没有这方面的官方文档。 加载时如何获取视口高度? @GiridharKarnik,您是否尝试过在ngOnInit
或ngAfterViewInit
方法中执行window.innerWidth
?
@Tomato API 参考可以在here 找到很多参考是自动生成的(我想),并且缺少示例或详细说明。一些 api-references 有很多例子。不过,我无法从文档中找到关于此的具体示例。也许它隐藏在某个地方:P
here 是其使用方式的参考以上是关于角度窗口调整大小事件的主要内容,如果未能解决你的问题,请参考以下文章