角度窗口调整大小事件

Dan*_*bdn 199 javascript angular

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

目前我有我的DOM如下:

<div id="Harbour">
    <div id="Port" (window:resize)="onResize($event)" >
        <router-outlet></router-outlet>
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

事件正确触发

export class AppComponent {
   onResize(event) {
        console.log(event);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

谢谢.

Gün*_*uer 458

<div (window:resize)="onResize($event)"
Run Code Online (Sandbox Code Playgroud)
onResize(event) {
  event.target.innerWidth;
}
Run Code Online (Sandbox Code Playgroud)

要么

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

支持的全球目标是window,documentbody.

在Angular中实现https://github.com/angular/angular/issues/13248之前,性能更好地订阅DOM事件并使用RXJS来减少事件量,如其他一些答案中所示.

  • 是否有关于您使用的语法的文档:**window:resize**? (14认同)
  • 完美的答案..我相信@HostListener是更清洁的方式:)但请确保首先使用来自'@ angular/core'的`import {Component,OnInit,HostListener}导入HostListener;` (4认同)
  • 快速提示:如果想在第一次加载时触发,请从@ angular/core实现ngAfterViewInit.https://angular.io/api/core/AfterViewInit (4认同)
  • 当然,对于那些想知道如何使用debounceTime运行HostListener的人,请点击以下链接https://plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview (3认同)
  • 究竟.你可以使用`document`,`window`和`body`https://github.com/angular/angular/blob/3412aba46e387b234d72e9194e2308f1d71a62df/modules/angular2/src/platform/server/parse5_adapter.ts#L533 (2认同)

Joh*_*ohn 59

@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
   }
}
Run Code Online (Sandbox Code Playgroud)

  • 我已尝试过此页面上的所有方法,但似乎没有一种方法可行.有关于此的官方文件吗? (2认同)

cga*_*ian 40

执行此操作的正确方法是使用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);
  }
}
Run Code Online (Sandbox Code Playgroud)

在组件中使用就像将此服务作为提供程序添加到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();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @cgatian`Mobile没有窗口对象`....为什么你认为移动浏览器没有窗口对象? (2认同)

Gri*_*mer 30

这是一个更好的方法.根据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
    };
};
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

对反应式编程的充分理解将始终有助于克服困难问题.希望这有助于某人.


Chr*_*ley 30

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

import { fromEvent, Observable, Subscription } from "rxjs";
Run Code Online (Sandbox Code Playgroud)

然后在你的组件中:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

ngOnInit() {
    this.resizeObservable$ = fromEvent(window, 'resize')
    this.resizeSubscription$ = this.resizeObservable$.subscribe( evt => {
      console.log('event: ', evt)
    })
}
Run Code Online (Sandbox Code Playgroud)

然后一定要取消订阅摧毁!

ngOnDestroy() {
    this.resizeSubscription$.unsubscribe()
}
Run Code Online (Sandbox Code Playgroud)

  • 抱歉回复晚了:要添加去抖动,您需要使用 `this.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt =&gt; { console.log('event: ', evt ) })` (5认同)
  • 对于去抖,从 'rxjs/operators' 导入 { debounceTime }; (2认同)

Tot*_*ati 15

angular CDK 中有一个ViewportRuler服务。它在区域外运行,支持orientationchangeresize。它也适用于服务器端渲染。

@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;
  }
}
Run Code Online (Sandbox Code Playgroud)

ViewportRuler 的 Stackblitz 示例

  • 这应该是公认的答案。拥有所需的一切 + 这是 Angular CDK 内置功能。 (6认同)
  • 这个答案需要更多的赞成。从性能和兼容性来看,这是最好的解决方案 (2认同)

Sta*_*avm 10

我还没有看到任何人谈论MediaMatcherangular/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);
  }

}
Run Code Online (Sandbox Code Playgroud)

App.Component.Html

<div [class]="mobileQuery.matches ? 'text-red' : 'text-blue'"> I turn red on mobile mode 
</div>
Run Code Online (Sandbox Code Playgroud)

App.Component.css

.text-red { 
   color: red;
}

.text-blue {
   color: blue;
}
Run Code Online (Sandbox Code Playgroud)

来源:https://material.angular.io/components/sidenav/overview


Flo*_*zil 6

假设<600px意味着移动给你,你可以使用这个observable并订阅它:

首先,我们需要当前的窗口大小.所以我们创建一个只发出一个值的observable:当前窗口大小.

initial$ = Observable.of(window.innerWidth > 599 ? false : true);
Run Code Online (Sandbox Code Playgroud)

然后我们需要创建另一个observable,以便我们知道窗口大小何时被更改.为此,我们可以使用"fromEvent"运算符.要了解有关rxjs`s运营商的更多信息,请访问:rxjs

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

合并这两个流以接收我们的可观察量:

mobile$ = Observable.merge(this.resize$, this.initial$).distinctUntilChanged();
Run Code Online (Sandbox Code Playgroud)

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

mobile$.subscribe((event) => { console.log(event); });
Run Code Online (Sandbox Code Playgroud)

记得取消订阅:)


A. *_*rel 6

如果您只想在调整大小完成后发生一个事件,最好将 RxJS 与debounceTime结合使用: debounceTime:丢弃在输出之间花费的时间少于指定时间的发出值。

在运行代码之前,他在发出的 2 个事件之间等待了 > 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');
      });
Run Code Online (Sandbox Code Playgroud)

StackBlitz 示例

堆栈闪电战


Moh*_*mil 5

我检查了大部分答案。然后决定查看有关Layout的 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();
      }
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你

  • 很高兴知道...但只是一个注释...这是材料库 Angular 文档,而不是 Angular 本身的官方文档。有点暗示所有断点都将遵循 OOTB 材料指南。 (2认同)