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.