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

Как получить ширину и высоту из этого объекта события?

Спасибо.

Ответ 1

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

или же

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

Поддерживаемые глобальные цели: window, document и body.

До тех пор, пока https://github.com/angular/angular/issues/13248 не будет реализован в Angular, для производительности лучше обязательно подписываться на события DOM и использовать RXJS для уменьшения количества событий, как показано в некоторых других ответах.

Ответ 2

@Ответ Günter верен. Я просто хотел предложить еще один метод.

Вы также можете добавить привязку хоста внутри @Component() -decorator. Вы можете поместить событие и желаемый вызов функции в свойство-metadata-хоста следующим образом:

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

Ответ 3

Правильный способ сделать это - использовать класс 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);
  }
}

Использование в компоненте так же просто, как добавление этой услуги в качестве поставщика к вашему модулю приложения, а затем импорт его в конструкторе компонента.

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

Ответ 4

Я знаю, что это было задано давно, но есть лучший способ сделать это сейчас! Я не уверен, что кто-нибудь увидит этот ответ. Очевидно, ваш импорт:

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

Ответ 5

Вот лучший способ сделать это. На основе ответа Бировского.

Шаг 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
        });
    }

}

Хорошее понимание реактивного программирования всегда поможет преодолеть сложные проблемы. Надеюсь, это поможет кому-то.

Ответ 6

Я не видел, чтобы кто-нибудь говорил о MediaMatcher angular/cdk.

Вы можете определить MediaQuery и присоединить к нему слушателя - тогда в любом месте вашего шаблона (или ts) вы можете вызывать вещи, если совпадает Matcher. 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

Ответ 7

Предполагая, что <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); });

Не забывайте отменять подписку :)

Ответ 8

Основываясь на решении @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) { }
}

Ответ 9

Это не совсем ответ на вопрос, но он может помочь кому-то, кто должен определять изменения размера для любого элемента.

Я создал библиотеку, которая добавляет resized событие к любому элементу - событие с угловым изменением размера.

Он внутренне использует ResizeSensor из запросов элементов CSS.

Пример использования

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

Ответ 10

Я написал эту библиотеку, чтобы найти один раз изменение размера границы раздела (изменение размера) в 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);
  }
}

Ответ 11

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

Я надеюсь, что эта помощь!

Ответ 12

Код ниже позволяет наблюдать за любым изменением размера для любого данного div в Angular.

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

Ответ 13

Есть сервис ViewportRuler в угловых CDK. Он работает за пределами зоны и также работает с рендерингом на стороне сервера.