Evento de cambio de tamaño de ventana angular

232

Me gustaría realizar algunas tareas basadas en el evento de cambio de tamaño de la ventana (en carga y dinámicamente).

Actualmente tengo mi DOM de la siguiente manera:

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

El evento se dispara correctamente

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

¿Cómo recupero el ancho y la altura de este objeto de evento?

Gracias.

DanAbdn
fuente
66
No es realmente una pregunta angular. mira el objeto de la ventana . Puedes conseguirlo innerHeighty sus innerWidthpropiedades ..
Sasxa
1
@Sasxa es correcto, solo tienes que hacerloconsole.log(event.target.innerWidth )
Pankaj Parkar
Gracias por la información Sasxa / Pankaj: no estaba seguro de si era solo una cosa de JavaScript simple o una cosa de Typecript o un evento de Angular. Estoy subiendo una curva de aprendizaje muy empinada para mí aquí y aprecio su aporte.
DanAbdn

Respuestas:

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

o usando el decorador HostListener :

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

Apoyados objetivos globales son window, documentybody .

Hasta que https://github.com/angular/angular/issues/13248 se implemente en Angular, es mejor que el rendimiento se suscriba a los eventos DOM de forma imperativa y use RXJS para reducir la cantidad de eventos como se muestra en algunas de las otras respuestas.

Günter Zöchbauer
fuente
15
¿Existe alguna documentación sobre la sintaxis que usa: window: resize ?
Clemente
3
Exactamente. Puede usar document, windowy body github.com/angular/angular/blob/…
Günter Zöchbauer el
55
respuesta perfecta ... creo que @HostListener es la forma más limpia :) pero asegúrese de importar HostListener primero usandoimport { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma
44
Consejo rápido: si también quiere disparar en la primera carga, implemente ngAfterViewInit desde @ angular / core. angular.io/api/core/AfterViewInit
Zymotik
77
Claro, sin embargo, para aquellos que quieran saber cómo funciona HostListener con debounceTime, siga el siguiente enlace plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
jcdsr
65

La respuesta de @ Günter es correcta. Solo quería proponer otro método.

También puede agregar el enlace de host dentro del @Component()decorador. Puede poner el evento y la llamada a la función deseada en la propiedad de metadatos del host de la siguiente manera:

@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
   }
}
Juan
fuente
2
He probado todos los métodos en esta página y parece que ninguno de ellos funciona. ¿Existe alguna documentación oficial sobre esto?
Tomate
¿Cómo se obtiene la altura de la ventana gráfica en la carga?
Giridhar Karnik
@GiridharKarnik, ¿has intentado hacer un window.innerWidthinterior ngOnInito ngAfterViewInitmétodos?
John
@Tomato La referencia de API se puede encontrar aquí. Muchas de las referencias son autogeneradas (supongo) y carecen de ejemplos o explicaciones detalladas. Algunas api-referencias tienen muchos ejemplos. Sin embargo, no pude encontrar un ejemplo concreto de los documentos sobre esto. Tal vez está oculto en alguna parte: P
John
1
aquí hay una referencia de cómo se usa
Anand Rockzz
52

Sé que esto se preguntó hace mucho tiempo, ¡pero ahora hay una mejor manera de hacerlo! Sin embargo, no estoy seguro de si alguien verá esta respuesta. Obviamente sus importaciones:

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

Luego en su componente:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

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

¡Entonces asegúrese de darse de baja en destruir!

ngOnDestroy() {
    this.resizeSubscription$.unsubscribe()
}
Chris Stanley
fuente
La única forma que me ha funcionado. ¡¡Gracias!! :-) ... Solo tuve que ajustar su importación. Quizás mi rxjs es más nuevo:import { fromEvent, Observable,Subscription } from "rxjs";
Jette
¿Dónde puedo agregar debounce (1000) en esto?
Deepak Thomas el
3
Perdón por la respuesta tardía: para agregar debounce, querrás usarthis.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
Chris Stanley
41

La forma correcta de hacer esto es utilizar la clase EventManager para vincular el evento. Esto permite que su código funcione en plataformas alternativas, por ejemplo, renderizado del lado del servidor con 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);
  }
}

El uso en un componente es tan simple como agregar este servicio como proveedor a su módulo de aplicación y luego importarlo en el constructor de un componente.

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
fuente
Por lo que puedo decir, esto nunca se dispara en el móvil. ¿Cómo se obtiene el tamaño inicial de la ventana con este enfoque?
user3170530
Mobile no tiene un windowobjeto, al igual que un servidor no tiene un window. No estoy familiarizado con las diferentes configuraciones móviles, pero debería poder adaptar fácilmente el código anterior para enlazar con el oyente de eventos global correcto
cgatian
@cgatian Soy un novato pero esta parece ser la respuesta correcta. Desafortunadamente no tengo suerte haciendo mi suscripción Inicie sesión en el componente. ¿Podría agregar a la respuesta cómo suscribirse a esto en un componente para poder ver las actualizaciones?
Matthew Harwood
@cgatian Voy a hacer un saqueador pero parece que esto no funciona. El filtro en el servicio parece extraño aunque stackoverflow.com/q/46397531/1191635
Matthew Harwood
3
@cgatian Mobile does not have a window object... ¿por qué crees que los navegadores móviles no tienen un objeto de ventana?
Drenai el
31

Aquí hay una mejor manera de hacerlo. Basado en la respuesta de Birowsky .

Paso 1: Crea un angular servicearchivo con 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
    };
};

Paso 2: inyecte lo anterior servicey suscríbase a cualquiera de los Observablescreados dentro del servicio donde desee recibir el evento de cambio de tamaño de la ventana.

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

}

Una buena comprensión de la programación reactiva siempre ayudará a superar problemas difíciles. Espero que esto ayude a alguien.

Giridhar Karnik
fuente
Creo que esto es un error: this.height $ = (windowSize $ .pluck ('height') como Observable <number>) .distinctUntilChanged (); Observable <number>) .distinctUntilChanged (); parece que pegaste en el distinto UntilChanged () dos veces seguidas
Zuriel
No te entendí, eloborate por favor.
Giridhar Karnik
La detección de cambios no se activará mientras realiza este evento fuera de Angular, creo que eso es lo que quiso decir.
Mark Pieszak - Trilon.io
1
Tuve un error diciendo que 'pluck' no existe en el tipo BehaviorSubject. Cambiar el código a this.height $ = windowSize $ .map (x => x.height) funcionó para mí.
Matt Sugden
@GiridharKamik le puede proporcionar una solución que suscribirse en anchura y altura a la vez, lo que podríamos suscribir a la anchura, la altura en 1 sencilla suscribirse
ghiscoding
11

No he visto a nadie hablando MediaMatcherdeangular/cdk .

Puede definir un MediaQuery y adjuntarle un oyente; luego, en cualquier lugar de su plantilla (o ts) puede invocar cosas si el Matcher coincide. 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;
}

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

Stavm
fuente
6

Suponiendo que <600px significa móvil para usted, puede usar este observable y suscribirse a él:

Primero necesitamos el tamaño actual de la ventana. Entonces creamos un observable que solo emite un único valor: el tamaño de la ventana actual.

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

Luego, necesitamos crear otro observable, para saber cuándo se cambió el tamaño de la ventana. Para esto podemos usar el operador "fromEvent". Para obtener más información sobre los operadores de rxjs, visite: rxjs

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

Combine estas dos corrientes para recibir nuestro observable:

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

Ahora puedes suscribirte así:

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

Recuerde darse de baja :)

Flosut Mözil
fuente
5

Hay un servicio ViewportRuler en angular CDK. Se ejecuta fuera de la zona y también funciona con renderizado del lado del servidor.

Totati
fuente
2
Esta debería ser la respuesta aceptada. Tiene todo lo necesario + esta es la funcionalidad incorporada angular de CDK.
Deniss M.
3

Basado en la solución de @cgatian, sugeriría la siguiente simplificación:

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

Uso:

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) { }
}
Johannes Hoppe
fuente
Encontré esto como la mejor solución, sin embargo, esto solo funciona cuando se cambia el tamaño de la ventana, pero no cuando se carga o cuando cambia el enrutador. ¿Sabes cómo aplicar con el cambio de router, recargar o cargar?
jcdsr
Puede agregar una función al servicio y activarla en el componente @jcdsr: getScreenSize () {this.onResize $ .emit ({width: window.innerWidth, height: window.innerHeight}); }
DanielWaw
3

Esta no es exactamente la respuesta a la pregunta, pero puede ayudar a alguien que necesita detectar cambios de tamaño en cualquier elemento.

He creado una biblioteca que agrega resizedeventos a cualquier elemento: evento de cambio de tamaño angular .

Utiliza internamente ResizeSensorde CSS Element Queries .

Ejemplo de uso

HTML

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

Mecanografiado

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

  onResized(event: ResizedEvent): void {
    this.width = event.newWidth;
    this.height = event.newHeight;
  }
}
Martin Volek
fuente
si desea detectar el cambio de tamaño de la ventana, Angular Material Breakpoint Observer ya maneja material.angular.io/cdk/layout/overview
Darren Street
Pero esto no detecta solo el cambio de tamaño de Windows, sino el cambio de tamaño de cualquier elemento.
Martin Volek
2

Escribí esta biblioteca para encontrar una vez que el cambio del tamaño del límite del componente (redimensionar) en Angular, puede ayudar a otras personas. Puede ponerlo en el componente raíz, hará lo mismo que cambiar el tamaño de la ventana.

Paso 1: importe el módulo

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

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

Paso 2: agregue la directiva como a continuación

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

Paso 3: reciba los detalles del tamaño del límite

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

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

En Angular2 (2.1.0) uso ngZone para capturar el evento de cambio de pantalla.

Echa un vistazo al ejemplo:

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

Espero esta ayuda!

Anh Hoang
fuente
1

El siguiente código permite observar cualquier cambio de tamaño para cualquier div dado en Angular.

<div #observed-div>
</div>

luego en el Componente:

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
fuente
Quiero cambiar una variable de counter = 6 a count = 0 a max-width 767px, ¿cómo hago eso?
Thanveer Shah
0

todas las soluciones no funcionan con el lado del servidor o en angular universal

jcdsr
fuente
0

Revisé la mayoría de estas respuestas. luego decidió consultar la documentación angular en el diseño .

Angular tiene su propio observador para detectar diferentes tamaños y es fácil de implementar en el componente o servicio.

Un ejemplo simple sería:

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

Espero eso ayude

Mj Jameel
fuente