Événement de redimensionnement de fenêtre angulaire


233

Je voudrais effectuer certaines tâches en fonction de l'événement de redimensionnement de la fenêtre (à la charge et dynamiquement).

Actuellement, j'ai mon DOM comme suit:

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

L'événement se déclenche correctement

export class AppComponent {
   onResize(event) {
        console.log(event);
    }
}

Comment récupérer la largeur et la hauteur de cet objet événement?

Merci.


6
Pas vraiment une question angulaire. regardez l' objet fenêtre . Vous pouvez l'obtenir innerHeightet les innerWidthpropriétés ..
Sasxa

1
@Sasxa est correct, il vous suffit de le faireconsole.log(event.target.innerWidth )
Pankaj Parkar

Merci pour l'info Sasxa / Pankaj - Je ne savais pas si c'était juste un truc javascript, un truc typographique ou un truc d'événement angulaire. J'escalade une courbe d'apprentissage très abrupte pour moi-même ici et j'apprécie votre contribution.
DanAbdn

Réponses:


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

ou en utilisant le décorateur HostListener :

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

Sont pris en charge des objectifs mondiaux window, documentet body.

Jusqu'à ce que https://github.com/angular/angular/issues/13248 soit implémenté dans Angular, il est préférable que les performances s'abonnent aux événements DOM impérativement et utilisent RXJS pour réduire la quantité d'événements comme indiqué dans certaines des autres réponses.


15
Existe-t-il une documentation sur la syntaxe que vous utilisez: window: resize ?
Clement

3
Exactement. Vous pouvez utiliser document, windowet body github.com/angular/angular/blob/...
Günter Zöchbauer

5
réponse parfaite .. je crois que @HostListener est la façon la plus propre :) mais assurez-vous d'importer d'abord HostListener en utilisantimport { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma

4
Astuce rapide: si vous souhaitez également déclencher lors de la première charge, implémentez ngAfterViewInit à partir de @ angular / core. angular.io/api/core/AfterViewInit
Zymotik

7
Bien sûr, cependant, pour ceux qui veulent savoir comment fonctionne HostListener avec debounceTime, suivez le lien ci-dessous plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
jcdsr

65

@ La réponse de Günter est correcte. Je voulais juste proposer une autre méthode.

Vous pouvez également ajouter la liaison hôte à l'intérieur du @Component()-décorateur. Vous pouvez placer l'événement et l'appel de fonction souhaité dans la propriété host-metadata-comme:

@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
J'ai essayé toutes les méthodes de cette page et il semble qu'aucune ne fonctionne. Existe-t-il une documentation officielle à ce sujet?
Tomato

comment la hauteur de la fenêtre est-elle obtenue en charge?
Giridhar Karnik

@ GiridharKarnik, avez-vous essayé de faire un window.innerWidthintérieur ngOnInitou des ngAfterViewInitméthodes?
John

@Tomato La référence API peut être trouvée ici Beaucoup de références sont générées automatiquement (je présume), et manquent d'exemples ou d'explications détaillées. Certaines références api contiennent de nombreux exemples. Je n'ai cependant pas pu trouver d'exemple concret dans les documents à ce sujet. Peut-être qu'il est caché quelque part: P
John

1
voici une référence de son utilisation
Anand Rockzz

52

Je sais que cela a été demandé il y a longtemps, mais il existe une meilleure façon de le faire maintenant! Je ne sais pas si quelqu'un verra cette réponse. Évidemment vos importations:

import { fromEvent, Observable, Subscription } from "rxjs";

Puis dans votre composant:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

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

Assurez-vous ensuite de vous désinscrire sur destroy!

ngOnDestroy() {
    this.resizeSubscription$.unsubscribe()
}

La seule façon qui a fonctionné pour moi. Merci!! :-) ... J'ai juste eu à ajuster votre importation. Peut-être que mon rxjs est plus récent:import { fromEvent, Observable,Subscription } from "rxjs";
Jette

Où puis-je ajouter un anti-rebond (1000) dans cela?
Deepak Thomas

3
Désolé pour la réponse tardive: pour ajouter un anti-rebond, vous voudrez utiliserthis.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
Chris Stanley

41

La bonne façon de procéder consiste à utiliser la classe EventManager pour lier l'événement. Cela permet à votre code de fonctionner sur d'autres plateformes, par exemple le rendu côté serveur avec 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);
  }
}

L'utilisation dans un composant est aussi simple que d'ajouter ce service en tant que fournisseur à votre app.module, puis de l'importer dans le constructeur d'un composant.

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

Pour autant que je sache, cela ne se déclenche jamais sur mobile. Comment obtenez-vous la taille de fenêtre initiale avec cette approche?
user3170530

Le mobile n'a pas d' windowobjet, tout comme un serveur n'en a pas window. Je ne suis pas familier avec les différentes configurations mobiles, mais vous devriez pouvoir facilement adapter le code ci-dessus pour le lier au bon écouteur d'événements global
cgatian

@cgatian Je suis un noob mais cela semble être la bonne réponse. Malheureusement, je n'ai pas de chance de faire mon abonnement Connectez-vous au composant. Pourriez-vous ajouter à la réponse comment vous y abonner dans un composant afin que l'on puisse voir les mises à jour?
Matthew Harwood

@cgatian Je vais faire un plunker mais cela ne semble pas fonctionner. Le filtre dans le service semble étrange sur stackoverflow.com/q/46397531/1191635
Matthew Harwood

3
@cgatian Mobile does not have a window object.... pourquoi pensez-vous que les navigateurs mobiles n'ont pas d'objet fenêtre?
Drenai

31

Voici une meilleure façon de procéder. Basé sur Birowsky réponse .

Étape 1: Créez un angular serviceavec 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
    };
};

Étape 2: Injectez ce qui précède serviceet abonnez-vous à l'un des éléments Observablescréés dans le service où que vous souhaitiez recevoir l'événement de redimensionnement de la fenêtre.

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

}

Une bonne compréhension de la programmation réactive aidera toujours à surmonter les problèmes difficiles. J'espère que cela aide quelqu'un.


Je pense que c'est une erreur: this.height $ = (windowSize $ .pluck ('height') as Observable <number>) .distinctUntilChanged (); Observable <number>) .distinctUntilChanged (); on dirait que vous avez collé deux fois de suite dans le distinctUntilChanged ()
Zuriel

Je ne t'ai pas eu, élobore s'il te plait.
Giridhar Karnik

La détection des modifications ne sera pas déclenchée lorsque vous effectuez cet événement en dehors d'Angular, croyez que c'est ce qu'il voulait dire.
Mark Pieszak - Trilon.io

1
J'ai eu une erreur en disant que «pluck» n'existe pas sur le type BehaviorSubject. Changer le code en this.height $ = windowSize $ .map (x => x.height) a fonctionné pour moi.
Matt Sugden

@GiridharKamik Pouvez-vous fournir une solution qui souscrirait à la largeur et à la hauteur en même temps, à laquelle nous pourrions souscrire à la fois la largeur et la hauteur en 1 souscription simple
ghiscodage

11

Je n'ai vu personne parler MediaMatcherdeangular/cdk .

Vous pouvez définir une MediaQuery et y attacher un écouteur - puis n'importe où sur votre modèle (ou ts), vous pouvez invoquer des éléments si le Matcher correspond. 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;
}

source: https://material.angular.io/components/sidenav/overview


6

En supposant que <600px signifie mobile pour vous, vous pouvez utiliser cet observable et vous y abonner:

Nous avons d'abord besoin de la taille actuelle de la fenêtre. Nous créons donc un observable qui n'émet qu'une seule valeur: la taille actuelle de la fenêtre.

initial$ = Observable.of(window.innerWidth > 599 ? false : true);

Ensuite, nous devons créer un autre observable, afin de savoir quand la taille de la fenêtre a été modifiée. Pour cela, nous pouvons utiliser l'opérateur "fromEvent". Pour en savoir plus sur les opérateurs de rxjs, veuillez visiter: rxjs

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

Fusionnez ces deux flux pour recevoir notre observable:

mobile$ = Observable.merge(this.resize$, this.initial$).distinctUntilChanged();

Vous pouvez maintenant vous y abonner comme ceci:

mobile$.subscribe((event) => { console.log(event); });

N'oubliez pas de vous désinscrire :)


5

Il existe un service ViewportRuler en CDK angulaire. Il s'exécute en dehors de la zone et fonctionne également avec le rendu côté serveur.


2
ce devrait être la réponse acceptée. A tout ce dont vous avez besoin + il s'agit d'une fonctionnalité intégrée CDK angulaire.
Deniss M.

3

Sur la base de la solution de @cgatian, je suggère la simplification suivante:

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

Usage:

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

J'ai trouvé cela comme la meilleure solution, cependant, cela ne fonctionne que lorsque la fenêtre est redimensionnée, mais pas lors des chargements ou lorsque le routeur change. Savez-vous comment postuler avec un changement de routeur, un rechargement ou un chargement?
jcdsr

Vous pouvez ajouter une fonction au service et la déclencher dans le composant @jcdsr: getScreenSize () {this.onResize $ .emit ({width: window.innerWidth, height: window.innerHeight}); }
DanielWaw

3

Ce n'est pas exactement la réponse à la question, mais cela peut aider quelqu'un qui a besoin de détecter des changements de taille sur n'importe quel élément.

J'ai créé une bibliothèque qui ajoute un resizedévénement à n'importe quel élément - Événement de redimensionnement angulaire .

Il utilise en interne à ResizeSensorpartir des requêtes d'éléments CSS .

Exemple d'utilisation

HTML

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

Manuscrit

@Component({...})
class MyComponent {
  width: number;
  height: number;

  onResized(event: ResizedEvent): void {
    this.width = event.newWidth;
    this.height = event.newHeight;
  }
}

si vous voulez détecter les redimensionnements de fenêtre, alors Angular Material Breakpoint Observer gère déjà material.angular.io/cdk/layout/overview
Darren Street

Mais cela ne détecte pas uniquement le redimensionnement des fenêtres, mais tout élément redimensionné.
Martin Volek

2

J'ai écrit cette lib pour trouver une fois que la taille des limites des composants change (redimensionne) dans Angular, cela peut aider d'autres personnes. Vous pouvez le mettre sur le composant racine, fera la même chose que le redimensionnement de la fenêtre.

Étape 1: importez le module

import { BoundSensorModule } from 'angular-bound-sensor';

@NgModule({
  (...)
  imports: [
    BoundSensorModule,
  ],
})
export class AppModule { }

Étape 2: ajoutez la directive comme ci-dessous

<simple-component boundSensor></simple-component>

Étape 3: recevoir les détails de la taille des limites

import { HostListener } from '@angular/core';

@Component({
  selector: 'simple-component'
  (...)
})
class SimpleComponent {
  @HostListener('resize', ['$event'])
  onResize(event) {
    console.log(event.detail);
  }
}

1

Sur Angular2 (2.1.0), j'utilise ngZone pour capturer l'événement de changement d'écran.

Jetez un oeil sur l'exemple:

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

J'espère que cette aide!


1

Le code ci-dessous permet d'observer tout changement de taille pour un div donné en angulaire.

<div #observed-div>
</div>

puis dans le Composant:

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

Je veux changer une varibale de counter = 6 à count = 0 à max-width 767px, comment faire?
Thanveer Shah

0

toutes les solutions ne fonctionnent pas côté serveur ou en universel angulaire


0

J'ai vérifié la plupart de ces réponses. a ensuite décidé de consulter la documentation Angular sur la mise en page .

Angular a son propre Observer pour détecter différentes tailles et il est facile à implémenter dans le composant ou un service.

un exemple simple serait:

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

J'espère que ça aide

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.