Событие изменения размера углового окна

233

Я хотел бы выполнить некоторые задачи на основе события изменения размера окна (при загрузке и динамически).

В настоящее время у меня есть 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);
    }
}

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

Спасибо.

DanAbdn
источник
6
Не совсем угловой вопрос. посмотрите на объект окна . Вы можете получить его innerHeightи innerWidthсвойства ..
Сасса
1
@Sasxa правильно, вам просто нужно сделатьconsole.log(event.target.innerWidth )
Pankaj Parkar
Спасибо за информацию Sasxa / Pankaj - я не был уверен, было ли это просто обычным явным сценарием JavaScript или Typescript или событием Angular. Я поднимаюсь очень крутой кривой обучения для себя здесь и ценю ваш вклад.
ДанАбдн

Ответы:

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

или используя декоратор HostListener :

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

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

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

Гюнтер Цохбауэр
источник
15
Есть ли документация о синтаксисе, который вы используете: window: resize ?
Климент
3
Именно. Вы можете использовать document, windowи body github.com/angular/angular/blob/...
Гюнтер Zöchbauer
5
идеальный ответ .. я верю, что @HostListener - более чистый путь :), но сначала обязательно импортируйте HostListenerimport { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma
4
Подсказка: если вы хотите запускать при первой загрузке, реализуйте ngAfterViewInit из @ angular / core. angular.io/api/core/AfterViewInit
Zymotik
7
Конечно, для тех, кто хочет знать, как работает HostListener с debounceTime, перейдите по ссылке ниже plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
jcdsr
65

@ Гюнтер ответил правильно. Я просто хотел предложить еще один метод.

Вы также можете добавить привязку к хосту внутри @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
Я попробовал каждый метод на этой странице, и кажется, что ни один из них не работает. Есть ли официальная документация по этому поводу.
помидор
как получается высота окна просмотра при загрузке?
Гиридар Карник
@GiridharKarnik, вы пытались делать window.innerWidthизнутри ngOnInitили ngAfterViewInitметоды?
Джон
@ Toto API-ссылку можно найти здесь. Многие ссылки генерируются автоматически (я полагаю) и не имеют примеров или подробных объяснений. Некоторые API-ссылки имеют много примеров. Я не мог найти конкретный пример из документации по этому поводу, хотя. Может быть, это где-то спрятано: P
Джон
1
вот ссылка на то, как его использовали
Ананд Рокц
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) в это?
Дипак Томас
3
Извините за поздний ответ: чтобы добавить debounce, вы захотите использовать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();
    }
  }
}
cgatian
источник
Насколько я могу судить, это никогда не срабатывает на мобильном телефоне. Как вы получаете начальный размер окна с этим подходом?
user3170530
Мобильный не имеет windowобъекта, как сервер не имеет window. Я не знаком с различными мобильными конфигурациями, но вы должны быть в состоянии легко адаптировать приведенный выше код для привязки к правильному слушателю глобального события
cgatian
@cgatian Я нуб, но это правильный ответ. К сожалению, мне не повезло, делая мою подписку Войдите в компонент. Не могли бы вы добавить в ответ, как подписаться на это в компоненте, чтобы можно было видеть обновления?
Мэтью Харвуд
@cgatian Я сделаю поршень, но, похоже, это не сработает. Фильтр в сервисе кажется странным, хотя stackoverflow.com/q/46397531/1191635
Мэтью Харвуд
3
@cgatian Mobile does not have a window object.... как вы думаете, почему в мобильных браузерах нет оконного объекта?
Дренай
31

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

Шаг 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') как Observable <число>). DifferentUntilChanged (); Observable <число>). DiverUntilChanged (); Похоже, что вы вставили в
другое
Я не получил вас, eloborate, пожалуйста.
Гиридхар Карник
Обнаружение изменений не сработает, когда вы проводите это мероприятие за пределами Angular, поверьте, именно это он и имел в виду.
Марк Песак - Trilon.io
1
У меня была ошибка, говорящая, что «pluck» не существует для типа BehaviorSubject. Изменение кода на this.height $ = windowSize $ .map (x => x.height) сработало для меня.
Мэтт Сагден
@GiridharKamik Можете ли вы предоставить решение, которое бы одновременно подписывалось на ширину и высоту, на которое мы могли бы подписаться как на ширину, так и на высоту в одной простой подписке
ghiscoding
11

Я не видел , чтобы кто говорить о 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

Stavm
источник
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); });

Не забудьте отписаться :)

Flosut Mözil
источник
5

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

Totati
источник
2
это должен быть принятый ответ. Имеет все необходимое + это встроенный угловой угловой CDK.
Денис М.
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 .

Он внутренне использует ResizeSensorиз CSS Element Queries .

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

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;
  }
}
Мартин Волек
источник
если вы хотите определить изменение
Даррен-стрит,
Но это не обнаруживает только изменение размеров окон, но изменение любого элемента.
Мартин Волек,
2

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

Код ниже позволяет наблюдать за любым изменением размера для любого данного 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;
}
abedfar
источник
Я хочу изменить varibale от counter = 6 до count = 0 при максимальной ширине 767px. Как мне это сделать?
Танвир Шах
0

все решения не работают со стороны сервера или в угловых универсальных

jcdsr
источник
0

Я проверил большинство из этих ответов. Затем решил проверить угловую документацию по макету .

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

Надеюсь, поможет

Mj Jameel
источник