Подія зміни розміру кутового вікна


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
Насправді не кутове питання. подивіться на предмет вікна . Ви можете отримати це innerHeightі innerWidthвластивості ..
Сакса

1
@Sasxa вірно, вам просто потрібно зробитиconsole.log(event.target.innerWidth )
Панкай Паркяр

Дякую за інформацію Сакса / Панкай - я не був впевнений, чи це просто звичайна річ з JavaScript або річ Typescript або річ Angular події. Я піднімаюсь дуже крутою кривою навчання для себе тут і ціную ваш внесок.
DanAbdn

Відповіді:


526
<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/isissue/13248 не буде реалізовано в Angular, для продуктивності краще підписуватися на події DOM в обов’язковому порядку і використовувати RXJS для зменшення кількості подій, як показано в деяких інших відповідях.


15
Чи є документація про синтаксис, який ви використовуєте: window: resize ?
Климент

3
Саме так. Ви можете використовувати document, windowі body github.com/angular/angular/blob / ...
Гюнтер Zöchbauer

5
ідеальна відповідь .. я вірю @HostListener - це більш чистий спосіб :), але переконайтесь, що імпортувати HostListener спочатку за допомогоюimport { 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()-декоратора. Ви можете розмістити виклик події та потрібної функції у властивості мета-даних мета хоста так:

@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методи?
Джон

@Tomato 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,

Де я можу додати в цьому дебютування (1000)?
Діпак Томас

3
Вибачте за пізню відповідь: щоб додати дебютацію, вам потрібно скористатисяthis.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
Chris Stanley

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

Наскільки я не можу сказати, це мобільний телефон ніколи не працює. Як ви отримуєте початковий розмір вікна при такому підході?
користувач3170530

Мобільний не має windowоб'єкта, як і сервер не має window. Я не знайомий з різними мобільними конфігураціями, але ви повинні мати можливість легко адаптувати вищевказаний код для прив’язки до правильного слухача глобальних подій
cgatian

@cgatian Я ноб, але це здається правильною відповіддю. На жаль, мені не пощастило зробити свій підписку Увійти в компонент. Чи можете ви додати до відповіді, як підписатися на це в компоненті, щоб можна було побачити оновлення?
Меттью Харвуд

@cgatian Я зроблю планкер, але це, здається, не працює. Фільтр на сервісі здається дивним, тому що stackoverflow.com/q/46397531/1191635
Меттью Харвуд

3
@cgatian Mobile does not have a window object.... чому ти вважаєш, що у мобільних браузерів немає об’єкта вікна?
Дренай

31

Ось кращий спосіб зробити це. Виходячи з відповіді Біровського .

Крок 1: Створення angular serviceз RxJS спостережуваних характеристик .

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 ('висота') як спостерігається <число>) .distinctUntilChanged (); спостерігається <число>) .distinctUntilChanged (); схоже на те, що ти вставив два рази поспіль у
різнихUntilChanged

Я тебе не зрозумів, розроби, будь ласка.
Гіріджар Карник

Виявлення змін не буде зафіксовано, коли ви робите цю подію за межами Angular, вірте, це він мав на увазі.
Марк Пешак - Trilon.io

1
У мене виникла помилка сказати, що "pluck" не існує для типу BehaviorSubject. Змінення коду на this.height $ = windowSize $ .map (x => x.height) працювало для мене.
Метт Сугден

@GiridharKamik Чи можете ви надати рішення, яке б одночасно підписалося на ширину та висоту, яке ми могли б підписати як на ширину, так і на висоту за 1 просту підписку
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


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

Існує послуга ViewportRuler у кутовій CDK. Він працює за межами зони, і працює і з рендерінгом на стороні сервера.


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 .

Приклад використання

HTML

<div (resized)="onResized($event)"></div>

TypeScript

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

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

Я хочу змінити варіабель з лічильника = 6 на лічильник = 0 при максимальній ширині 767px, як це зробити?
Дякую Шах

0

всі рішення не працюють із серверною або універсальною


0

Я перевірив більшість цих відповідей. потім вирішили перевірити кутову документацію на макет .

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

сподіваюся, що це допомагає

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.