Angular窗口大小调整事件

我想执行一些基于窗口重新大小事件(在加载和动态)的任务。

目前我的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);
}
}

我如何从这个事件对象检索宽度和高度?

谢谢。

420645 次浏览
<div (window:resize)="onResize($event)"
onResize(event) {
event.target.innerWidth;
}

或使用HostListener装饰:

@HostListener('window:resize', ['$event'])
onResize(event) {
event.target.innerWidth;
}

支持的全局目标为windowdocumentbody

https://github.com/angular/angular/issues/13248在Angular中实现之前,强制订阅DOM事件并使用RXJS来减少事件的数量对性能更好,就像其他一些答案中所显示的那样。

@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
}
}

这里有一个更好的方法。基于Birowsky的的答案。

步骤1:使用RxJS 可见创建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
});
}


}

充分理解响应式编程总是有助于克服困难的问题。希望这能帮助到一些人。

在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);
});
};
}

希望这对你有所帮助!

正确的方法是使用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();
}
}
}

假设<600px对你来说意味着移动,你可以使用这个可观察对象并订阅它:

首先,我们需要当前窗口大小。因此,我们创建了一个只发出一个值的可观察对象:当前窗口大小。

initial$ = Observable.of(window.innerWidth > 599 ? false : true);

然后我们需要创建另一个可观察对象,这样我们就知道窗口大小什么时候被改变了。为此,我们可以使用“fromEvent”操作符。要了解更多关于rxjs的操作符,请访问:rxjs

resize$ = Observable.fromEvent(window, 'resize').map((event: any) => {
return event.target.innerWidth > 599 ? false : true;
});

合并这两个流来接收我们的可观察对象:

mobile$ = Observable.merge(this.resize$, this.initial$).distinctUntilChanged();

现在你可以像这样订阅它:

mobile$.subscribe((event) => { console.log(event); });

记得取消订阅:)

我写了这种自由来查找Angular中组件边界大小的改变(调整大小),希望这能帮助到其他人。你可以把它放在根组件上,会做和窗口大小调整一样的事情。

步骤1:导入模块

import { BoundSensorModule } from 'angular-bound-sensor';


@NgModule({
(...)
imports: [
BoundSensorModule,
],
})
export class AppModule { }

步骤2:像下面这样添加指令

<simple-component boundSensor></simple-component>

接收边界大小的详细信息

import { HostListener } from '@angular/core';


@Component({
selector: 'simple-component'
(...)
})
class SimpleComponent {
@HostListener('resize', ['$event'])
onResize(event) {
console.log(event.detail);
}
}

基于@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) { }
}

这并不是问题的确切答案,但它可以帮助那些需要检测任何元素的大小变化的人。

我已经创建了一个库,它将resized事件添加到任何元素——Angular调整大小事件

它内部使用来自CSS元素查询ResizeSensor

示例使用

超文本标记语言

<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/cdkMediaMatcher

你可以定义一个MediaQuery并给它附加一个监听器——然后在模板(或ts)上的任何地方,如果匹配了Matcher,就可以调用stuff。 LiveExample < / p >

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

我知道很久以前就有人问过这个问题,但现在有更好的方法来做到这一点!我不确定是否有人会看到这个答案。显然你的进口:

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)
})
}

那么一定要取消订阅destroy!

ngOnDestroy() {
this.resizeSubscription$.unsubscribe()
}

下面的代码让我们观察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;
}

angular CDK中有一个ViewportRuler服务。它在zone之外运行,支持orientationchange调整。它也适用于服务器端渲染。

@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;
}
}

ViewportRuler的Stackblitz示例

这样做的好处是,它限制了更改检测周期(它只会在区域中运行回调时触发),而(window:resize)将在每次调用时触发更改检测。

我检查了大部分答案。然后决定查看Angular的布局文档。

Angular有自己的观察者来检测不同的大小,而且很容易实现到组件或服务中。

一个简单的例子是:

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();
}
});
}
}

希望能有所帮助

下面是最新版本的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
};
};

这是我创建的一个简单而干净的解决方案,这样我就可以将它注入到多个组件中。

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[] = [];

我所做的就像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>

我采取的另一种方法是

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可以在任何组件中重用

如果你想要调整大小完成后只需一个事件,最好使用RxJS和debounceTime: debounceTime:丢弃间隔时间小于指定时间的输出值。

他等待>在运行代码之前,两个事件之间间隔0.5秒。 简单地说,它在执行下一个代码之前等待大小调整完成

// RxJS v6+
import { fromEvent } from 'rxjs';
import { debounceTime, map } from 'rxjs/operators';


...


const resize$ = fromEvent(window, 'resize');
resize$
.pipe(
map((i: any) => i),
debounceTime(500) // He waits > 0.5s between 2 events emitted before running the next.
)
.subscribe((event) => {
console.log('resize is finished');
});

StackBlitz example

StackBlitz