角度窗口调整大小事件


232

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

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

如何从此事件对象中检索宽度和高度?

谢谢。


6
并不是真正的角度问题。看窗口对象。你可以得到它innerHeightinnerWidth属性..
Sasxa

1
@Sasxa是正确的,您只需要做console.log(event.target.innerWidth )
Pankaj Parkar '16

感谢您提供的信息Sasxa / Pankaj-我不确定这是纯JavaScript还是Typescript还是Angular事件。我在这里为自己准备了一条非常陡峭的学习曲线,感谢您的投入。
DanAbdn '16

Answers:


525
<div (window:resize)="onResize($event)"
onResize(event) {
  event.target.innerWidth;
}

或使用HostListener装饰器

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

支持的全球目标是windowdocumentbody

直到在Angular中实现https://github.com/angular/angular/issues/13248为止,性能才能更好地强制性地订阅DOM事件并使用RXJS减少事件数量,如其他一些答案所示。


15
是否有关于您使用的语法的文档: window:resize
克莱门特2016年

3
究竟。您可以使用documentwindowbody github.com/angular/angular/blob/...
冈特Zöchbauer

5
完美的答案..我相信@HostListener是更干净的方法:)但请确保首先使用import { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma

4
快速提示:如果也要在首次加载时触发,请从@ angular / core实现ngAfterViewInit。angular.io/api/core/AfterViewInit
Zymotik

6
当然,对于那些想知道带有debounceTime的
jcdsr

65

@Günter的答案是正确的。我只是想提出另一种方法。

您还可以在@Component()-decorator中添加主机绑定。您可以将事件和所需的函数调用放在主机元数据属性中,如下所示:

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

2
我已经尝试过此页面上的所有方法,但似乎它们都不起作用。是否有关于此的任何官方文档。
番茄

加载时如何获得视口高度?
Giridhar Karnik

@GiridharKarnik,你尝试过做一个window.innerWidth内部的ngOnInit,或者ngAfterViewInit方法?
约翰·

@Tomato可以在这里找到API引用。许多引用是自动生成的(我想),并且缺少示例或详细说明。一些api引用有很多示例。不过,我从文档中找不到具体的示例。也许它藏在某处:P
约翰·约翰·约翰(John John)

1
这里是它如何被使用的参考
阿南德Rockzz

52

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

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";
Jette

我在哪里可以在其中添加debounce(1000)?
Deepak Thomas

3
抱歉,您稍后才能回复:要添加防抖功能,您需要使用this.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
克里斯·斯坦利

41

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

据我所知,这永远不会在移动设备上触发。您如何通过这种方法获得初始窗口大小?
user3170530

移动设备没有window对象,就像服务器没有一样window。我对不同的移动设备配置并不熟悉,但是您应该能够轻松地调整以上代码以将其绑定到正确的全局事件侦听器
cgatian

@cgatian我是菜鸟,但这似乎是正确的答案。不幸的是,我无法进行订阅。登录该组件。您能否在答案中添加如何在组件中进行预订,以便可以看到更新?
马修·哈伍德

@cgatian我要塞一个,但这似乎行不通。该服务中的过滤器似乎很奇怪tho stackoverflow.com/q/46397531/1191635
Matthew Harwood

3
@cgatian Mobile does not have a window object....为什么您认为移动浏览器没有窗口对象?
德莱奈

31

这是一种更好的方法。基于Birowsky的答案。

第1步:angular service使用RxJS Observables创建一个。

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 <number>)。distinctUntilChanged(); Observable <number>)。distinctUntilChanged(); 看起来你在distinctUntilChanged(粘贴)连续两次
Zuriel

我没明白你的意思。
Giridhar Karnik

当您在Angular之外进行此活动时,不会触发更改检测,请相信这就是他的意思。
Mark Pieszak-Trilon.io

1
我遇到一个错误,说BehaviorSubject类型上不存在'pluck'。将代码更改为this.height $ = windowSize $ .map(x => x.height)对我有用。
马特·萨格登

@GiridharKamik您能提供一个可以同时订阅宽度和高度的解决方案吗,我们可以在1个简单订阅中同时订阅宽度和高度的方法
ghiscoding

11

我还没有看到任何人谈论MediaMatcherangular/cdk

您可以定义MediaQuery并为其附加一个侦听器-然后,如果Matcher匹配,则可以在模板(或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


6

假设<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); });

记得退订:)


5

角CDK中有一个ViewportRuler服务。它在区域之外运行,并且也可以与服务器端渲染一起使用。


2
这应该是公认的答案。具有所需的一切,而且这是角度CDK内置功能。
Deniss M.

3

基于@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

您可以向服务中添加一个函数,并在组件@jcdsr中触发它:getScreenSize(){this.onResize $ .emit({width:window.innerWidth,height:window.innerHeight}); }
DanielWaw

3

这并不完全是问题的答案,但可以帮助需要检测任何元素尺寸变化的人。

我创建了一个将resized事件添加到任何元素的库-Angular Resize Event

它在内部使用ResizeSensorCSS元素查询

用法示例

的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
Darren Street

但这不仅检测到窗口调整大小,还检测到任何元素调整大小。
马丁·沃莱克

2

我写了这个lib来查找Angular中组件边界大小的更改(调整大小),可能对其他人有帮助。您可以将其放在根组件上,其作用与调整窗口大小相同。

步骤1:导入模块

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

1

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

希望对您有所帮助!


1

下面的代码可让您观察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,我该怎么做?
Thanveer Shah

0

所有解决方案均不适用于服务器端或通用角度


0

我检查了大多数这些答案。然后决定查看有关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();
      }
    });
  }
}

希望能帮助到你

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.