Rilevamento delle modifiche alle dimensioni della finestra in tempo reale in Angular 4


118

Ho provato a creare una barra di navigazione reattiva e non desidero utilizzare una query multimediale, quindi intendo utilizzare *ngIFla dimensione della finestra come criterio. Ma ho riscontrato un problema in quanto non sono in grado di trovare alcun metodo o documentazione sul rilevamento delle dimensioni della finestra di Angular 4. Ho anche provato il metodo JavaScript, ma non è supportato.

Ho anche provato quanto segue :

constructor(platform: Platform) {
    platform.ready().then((readySource) => {
        console.log('Width: ' + platform.width());
        console.log('Height: ' + platform.height());
    });
}

... che è stato utilizzato in ionico.

E screen.availHeight, ma ancora nessun successo.


1
Di cosa si platformtratta? Si tratta di Ionic?
— Günter Zöchbauer

@ Günter Zöchbauer La piattaforma è angolare, volevo solo menzionare i codici che avevo provato.
— Ronit Oommen

1
Platformè un servizio ionico. Quindi immagino sia un progetto ionico?
— robbannn


@BlackBeard Non penso che sia un duplicato a causa di quanto Angular 2 e 4 differiscono.
— tehlivi

Risposte:


261

Per ottenerlo su init

public innerWidth: any;
ngOnInit() {
    this.innerWidth = window.innerWidth;
}

Se vuoi tenerlo aggiornato sul ridimensionamento:

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

2
this.innerWidth = event.target.innerWidth; ... è forse più efficiente, produce lo stesso risultato
— danday74

2
Consiglia anche questo nel costruttore se stai usando lodash ... this.onResize = debounce (this.onResize, 150, {leading: false, trailing: true}) ... per evitare che il metodo onResize venga chiamato troppo frequentemente .. . dovrai ... importare {debounce} da "lodash"
— danday74

Penso che abbia preferito usare il ngAfterViewInitmetodo del ngOnInit gancio della vita invece del metodo del gancio della vita
— ahmed hamdy

39

Se vuoi reagire su determinati punti di interruzione (es. Fare qualcosa se la larghezza è inferiore a 768px), puoi anche usare BreakpointObserver:

import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';

{ ... }

const isSmallScreen = breakpointObserver.isMatched('(max-width: 599px)');

o anche ascoltare le modifiche a quel punto di interruzione:

breakpointObserver.observe([
  '(max-width: 768px)'
    ]).subscribe(result => {
      if (result.matches) {
        doSomething();
      } else {
        // if necessary:
        doSomethingElse();
      }
    });

se vuoi agire solo se l'osservatore soddisfa i criteri devi aggiungere all'interno iscriviti if (result.matches)altrimenti chiamerà anche se non lo fa
— karoluS

1
@karoluS: Sì, certo. Ho modificato la mia risposta per renderlo più chiaro. Grazie.
— Jeremy Benks

Sembra che questo cdkabbia una dipendenza tra pari da una versione specifica di angular. Come 7 per l'ultimo. Cmq posso usarlo per la vecchia versione (angolare 4 come nella domanda)?
— LeOn - Han Li

@ Leon li: in realtà uso l'angolo 7, è vero. Tuttavia: per quanto ne so puoi usare cdk anche in angular 4. Secondo questo post del blog è necessario cdk 2.x. Per quanto ne so, questo deve essere installato manualmente e con la versione specificata in questo modo: npm @ angular / cdk @ 4
— Jeremy Benks

@ JeremyBenks Y, stiamo usando ng 4.2.6specificamente. Impossibile trovare una versione cdk 2.x su quella, solo 4.1.xe 4.3.x. Comunque grazie!
— LeOn - Han Li

9

Se desideri che i tuoi componenti rimangano facilmente testabili, dovresti avvolgere l'oggetto finestra globale in un servizio angolare:

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

@Injectable()
export class WindowService {

  get windowRef() {
    return window;
  }

}

Puoi quindi iniettarlo come qualsiasi altro servizio:

constructor(
    private windowService: WindowService
) { }

E consuma ...

  ngOnInit() {
      const width= this.windowService.windowRef.innerWidth;
  }

2
Non vedo il punto di fare un servizio per fare esattamente ciò che è già a portata di mano. window.innerWidth.
— Rodrigo

1
Innanzitutto DI è il modo angolare e rende più chiare le dipendenze di un componente / direttiva. Ma il punto principale qui menzionato è rendere il codice che utilizza il servizio più facile da testare. Utilizzando questo approccio, WindowService può essere deriso in qualsiasi test scritto per i componenti che utilizzano il servizio.
— Kildareflare

9

La documentazione per Platform width()e height(), si afferma che questi metodi usano rispettivamente window.innerWidthe window.innerHeight. Tuttavia, è preferibile utilizzare i metodi poiché le dimensioni sono valori memorizzati nella cache, il che riduce la possibilità di letture DOM multiple e costose.

import { Platform } from 'ionic-angular';

...
private width:number;
private height:number;

constructor(private platform: Platform){
    platform.ready().then(() => {
        this.width = platform.width();
        this.height = platform.height();
    });
}

In che modo ottenere widthdi windowrelazionarsi DOM? Logicamente, non dovrebbe dipendere da come appare l'albero dom.
— Shahryar Saljoughi

7

Questo è un esempio di servizio che utilizzo.

Puoi ottenere la larghezza dello schermo iscrivendoti a screenWidth$, o tramite screenWidth$.value.

Lo stesso vale per mediaBreakpoint$(o mediaBreakpoint$.value)

import {
  Injectable,
  OnDestroy,
} from '@angular/core';
import {
  Subject,
  BehaviorSubject,
  fromEvent,
} from 'rxjs';
import {
  takeUntil,
  debounceTime,
} from 'rxjs/operators';

@Injectable()
export class ResponsiveService implements OnDestroy {
  private _unsubscriber$: Subject<any> = new Subject();
  public screenWidth$: BehaviorSubject<number> = new BehaviorSubject(null);
  public mediaBreakpoint$: BehaviorSubject<string> = new BehaviorSubject(null);

  constructor() {}

  init() {
    this._setScreenWidth(window.innerWidth);
    this._setMediaBreakpoint(window.innerWidth);
    fromEvent(window, 'resize')
      .pipe(
        debounceTime(1000),
        takeUntil(this._unsubscriber$)
      ).subscribe((evt: any) => {
        this._setScreenWidth(evt.target.innerWidth);
        this._setMediaBreakpoint(evt.target.innerWidth);
      });
  }

  ngOnDestroy() {
    this._unsubscriber$.next();
    this._unsubscriber$.complete();
  }

  private _setScreenWidth(width: number): void {
    this.screenWidth$.next(width);
  }

  private _setMediaBreakpoint(width: number): void {
    if (width < 576) {
      this.mediaBreakpoint$.next('xs');
    } else if (width >= 576 && width < 768) {
      this.mediaBreakpoint$.next('sm');
    } else if (width >= 768 && width < 992) {
      this.mediaBreakpoint$.next('md');
    } else if (width >= 992 && width < 1200) {
      this.mediaBreakpoint$.next('lg');
    } else if (width >= 1200 && width < 1600) {
      this.mediaBreakpoint$.next('xl');
    } else {
      this.mediaBreakpoint$.next('xxl');
    }
  }

}

Spero che questo aiuti qualcuno


La mia risposta preferita! Assicurati solo di aggiungere this.init () nel costruttore affinché funzioni.
— Ben


3
@HostListener("window:resize", [])
public onResize() {
  this.detectScreenSize();
}

public ngAfterViewInit() {
    this.detectScreenSize();
}

private detectScreenSize() {
    const height = window.innerHeight;
    const width = window.innerWidth;
}

3
Sempre spiegare il motivo per cui questa potrebbe essere la soluzione giusta
— thedp

3

La risposta è molto semplice. scrivi il codice sottostante

import { Component, OnInit, OnDestroy, Input } from "@angular/core";
// Import this, and write at the top of your .ts file
import { HostListener } from "@angular/core";

@Component({
 selector: "app-login",
 templateUrl: './login.component.html',
 styleUrls: ['./login.component.css']
})

export class LoginComponent implements OnInit, OnDestroy {
// Declare height and width variables
scrHeight:any;
scrWidth:any;

@HostListener('window:resize', ['$event'])
getScreenSize(event?) {
      this.scrHeight = window.innerHeight;
      this.scrWidth = window.innerWidth;
      console.log(this.scrHeight, this.scrWidth);
}

// Constructor
constructor() {
    this.getScreenSize();
}
}

1

È possibile utilizzare il metodo getter del dattiloscritto per questo scenario. Come questo

public get width() {
  return window.innerWidth;
}

E usalo in un modello come questo:

<section [ngClass]="{ 'desktop-view': width >= 768, 'mobile-view': width < 768 
}"></section>

Non avrai bisogno di alcun gestore di eventi per controllare il ridimensionamento / della finestra, questo metodo controllerà automaticamente le dimensioni ogni volta.

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.