Evento di ridimensionamento della finestra angolare


232

Vorrei eseguire alcune attività in base all'evento di ridimensionamento della finestra (al caricamento e in modo dinamico).

Attualmente ho il mio DOM come segue:

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

L'evento si attiva correttamente

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

Come posso recuperare la larghezza e l'altezza da questo oggetto evento?

Grazie.


6
Non proprio una domanda angolare. guarda l' oggetto finestra . Puoi ottenerlo innerHeighte le innerWidthproprietà ..
Sasxa,

1
@Sasxa è corretto, devi solo farloconsole.log(event.target.innerWidth )
Pankaj Parkar,

Grazie per le informazioni Sasxa / Pankaj - Non ero sicuro che fosse solo una cosa javascript o una cosa dattiloscritta o un evento angolare. Sto scalando una curva di apprendimento molto ripida per me stesso e apprezzo il tuo contributo.
DanAbdn,

Risposte:


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

o usando il decoratore HostListener :

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

Obiettivi globali supportati sono window, documente body.

Fino a quando https://github.com/angular/angular/issues/13248 è implementato in Angular, è meglio che le prestazioni si iscrivano in modo imperativo agli eventi DOM e utilizzino RXJS per ridurre la quantità di eventi, come mostrato in alcune delle altre risposte.


15
C'è qualche documentazione sulla sintassi che usi: window: resize ?
Clemente,

3
Esattamente. Puoi usare document, windowe body github.com/angular/angular/blob/…
Günter Zöchbauer

5
risposta perfetta .. credo che @HostListener sia il modo più pulito :) ma assicurati di importare HostListener usando primaimport { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma,

4
Suggerimento rapido: se si desidera eseguire il trigger anche al primo caricamento, implementare ngAfterViewInit da @ angular / core. angular.io/api/core/AfterViewInit
Zymotik

7
Certo, tuttavia, per coloro che vogliono sapere come funziona HostListener con debounceTime, segui il link qui sotto plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
jcdsr

65

@ La risposta di Günter è corretta. Volevo solo proporre un altro metodo.

È inoltre possibile aggiungere l'associazione host all'interno del @Component()-decorator. È possibile inserire l'evento e la funzione desiderata nella proprietà host-metadata in questo modo:

@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
Ho provato tutti i metodi in questa pagina e sembra che nessuno di loro funzioni. C'è qualche documentazione ufficiale su questo.
Pomodoro

come si ottiene l'altezza del viewport al carico?
Giridhar Karnik,

@GiridharKarnik, hai provato a fare un window.innerWidthinterno ngOnInito ngAfterViewInitmetodi?
Giovanni

@Tomato Il riferimento API può essere trovato qui Molti dei riferimenti sono generati automaticamente (presumo) e mancano di esempi o spiegazioni dettagliate. Alcuni riferimenti api hanno molti esempi. Non sono riuscito a trovare un esempio concreto dai documenti su questo argomento. Forse è nascosto da qualche parte: P
John

1
ecco un riferimento a come è stato usato
Anand Rockzz,

52

So che è stato chiesto molto tempo fa, ma ora c'è un modo migliore per farlo! Non sono sicuro se qualcuno vedrà questa risposta però. Ovviamente le tue importazioni:

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

Quindi nel tuo componente:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

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

Quindi assicurati di annullare l'iscrizione per distruggere!

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

L'unico modo che ha funzionato per me. Grazie!! :-) ... dovevo solo regolare la tua importazione. Forse il mio rxjs è più recente:import { fromEvent, Observable,Subscription } from "rxjs";
Jette l'

Dove posso aggiungere debounce (1000) in questo?
Deepak Thomas,

3
Ci scusiamo per la risposta tardiva: per aggiungere il rimbalzo, ti consigliamo di utilizzarethis.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
Chris Stanley,

41

Il modo corretto per farlo è utilizzare la classe EventManager per associare l'evento. Ciò consente al codice di funzionare su piattaforme alternative, ad esempio il rendering lato server 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);
  }
}

L'utilizzo in un componente è semplice come aggiungere questo servizio come provider al tuo modello di app e quindi importarlo nel costruttore di 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();
    }
  }
}

Questo non funziona mai sul cellulare per quanto ne so. Come si ottiene la dimensione della finestra iniziale con questo approccio?
user3170530,

Il cellulare non ha un windowoggetto, proprio come un server non ha un window. Non ho familiarità con le diverse configurazioni mobili, ma dovresti essere in grado di adattare facilmente il codice sopra per legare al listener di eventi globale corretto
cgatian

@cgatian Sono un noob ma questa sembra la risposta giusta. Purtroppo non ho avuto fortuna con la mia iscrizione. Accedi al componente. Potresti aggiungere alla risposta come iscriverti a questo in un componente in modo che uno possa vedere gli aggiornamenti?
Matthew Harwood,

@cgatian Farò un plunker ma questo sembra non funzionare. Il filtro nel servizio sembra strano sebbene stackoverflow.com/q/46397531/1191635
Matthew Harwood,

3
@cgatian Mobile does not have a window object.... perché pensi che i browser per dispositivi mobili non abbiano un oggetto finestra?
Drenai,

31

Ecco un modo migliore per farlo. Basato su Birowsky risposta .

Passaggio 1: crea un osservabileangular service con 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
    };
};

Passaggio 2: iniettare quanto sopra servicee iscriversi a uno qualsiasi dei Observablescreati all'interno del servizio ovunque si desideri ricevere l'evento di ridimensionamento della finestra.

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 buona conoscenza della programmazione reattiva aiuterà sempre a superare problemi difficili. Spero che questo aiuti qualcuno.


Credo che questo sia un errore: this.height $ = (windowSize $ .pluck ('height') as Observable <number>) .distinctUntilChanged (); Observable <number>) .distinctUntilChanged (); sembra che tu abbia incollato nel distinto
UntilChanged

Non ti ho preso, elobora per favore.
Giridhar Karnik,

Il rilevamento del cambiamento non verrà attivato mentre stai facendo questo evento al di fuori di Angular, credi che sia quello che intendeva dire.
Mark Pieszak - Trilon.io,

1
Ho riscontrato un errore che diceva che "pluck" non esiste sul tipo BehaviorSubject. La modifica del codice in this.height $ = windowSize $ .map (x => x.height) ha funzionato per me.
Matt Sugden,

@GiridharKamik Puoi fornire una soluzione che possa abbonarsi su larghezza e altezza allo stesso tempo, che potremmo abbonare sia alla larghezza, sia all'altezza in 1 semplice abbonamento
ghiscoding

11

Non ho visto nessuno parlare MediaMatcherdiangular/cdk .

Puoi definire un MediaQuery e allegare un ascoltatore ad esso - quindi ovunque sul tuo modello (o ts) puoi invocare cose se il Matcher è abbinato. 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;
}

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


6

Supponendo che <600px significhi mobile per te, puoi utilizzare questo osservabile e iscriverti ad esso:

Per prima cosa abbiamo bisogno delle dimensioni della finestra corrente. Quindi creiamo un osservabile che emette solo un singolo valore: la dimensione della finestra corrente.

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

Quindi dobbiamo crearne un altro osservabile, in modo da sapere quando è stata modificata la dimensione della finestra. Per questo possiamo usare l'operatore "fromEvent". Per ulteriori informazioni sugli operatori di rxjs, visitare: rxjs

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

Unisci questi due flussi per ricevere il nostro osservabile:

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

Ora puoi iscriverti in questo modo:

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

Ricorda di annullare l'iscrizione :)


5

C'è un servizio ViewportRuler in CDK angolare. Funziona al di fuori della zona e funziona anche con il rendering lato server.


2
questa dovrebbe essere la risposta accettata. Ha tutto il necessario + questa è la funzionalità integrata CDK angolare.
Deniss M.

3

Basandomi sulla soluzione di @cgatian, suggerirei la seguente semplificazione:

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

Ho trovato questa come la migliore soluzione, tuttavia, funziona solo quando la finestra viene ridimensionata, ma non quando si carica o quando il router cambia. Sai, come fare domanda con cambio router, ricarica o caricamento?
jcdsr,

È possibile aggiungere una funzione al servizio e attivarla nel componente @jcdsr: getScreenSize () {this.onResize $ .emit ({width: window.innerWidth, height: window.innerHeight}); }
DanielWaw,

3

Questa non è esattamente la risposta alla domanda, ma può aiutare qualcuno che ha bisogno di rilevare cambiamenti di dimensioni su qualsiasi elemento.

Ho creato una libreria che aggiunge un resizedevento a qualsiasi elemento: Angular Resize Event .

Utilizza internamente ResizeSensordalle query degli elementi CSS .

Esempio di utilizzo

HTML

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

Dattiloscritto

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

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

se si desidera rilevare il ridimensionamento delle finestre, allora Observer Material Breakpoint Observer gestisce già material.angular.io/cdk/layout/overview
Darren Street

Ma questo non rileva solo il ridimensionamento di Windows, ma qualsiasi ridimensionamento di elementi.
Martin Volek,

2

Ho scritto questa libreria per trovare una volta che la modifica della dimensione del limite del componente (ridimensionamento) in Angolare, può aiutare altre persone. Puoi metterlo sul componente radice, farà la stessa cosa del ridimensionamento della finestra.

Passaggio 1: importare il modulo

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

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

Passaggio 2: aggiungere la direttiva come di seguito

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

Passaggio 3: ricevere i dettagli sulla dimensione del contorno

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

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

1

Su Angular2 (2.1.0) uso ngZone per catturare l'evento di cambio schermo.

Dai un'occhiata all'esempio:

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

Spero che questo aiuto!


1

Il codice seguente consente di osservare qualsiasi modifica delle dimensioni per ogni dato div in Angolare.

<div #observed-div>
</div>

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

Voglio cambiare una varibale dal contatore = 6 per contare = 0 alla massima larghezza 767px, come posso farlo?
Thanveer Shah,

0

tutte le soluzioni non funzionano con lato server o in angolare universale


0

Ho controllato la maggior parte di queste risposte. quindi ha deciso di consultare la documentazione angolare sul layout .

Angular ha un proprio osservatore per il rilevamento di dimensioni diverse ed è facile da implementare nel componente o in un servizio.

un semplice esempio sarebbe:

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

spero che sia d'aiuto

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.