Détection des changements de taille de fenêtre en temps réel dans Angular 4

118

J'ai essayé de créer une barre de navigation réactive et je ne souhaite pas utiliser de requête multimédia, j'ai donc l'intention d'utiliser *ngIFla taille de la fenêtre comme critère. Mais j'ai rencontré un problème car je ne trouve aucune méthode ou documentation sur la détection de la taille de la fenêtre Angular 4. J'ai également essayé la méthode JavaScript, mais elle n'est pas prise en charge.

J'ai également essayé ce qui suit :

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

... qui a été utilisé en ionique.

Et screen.availHeight, mais toujours pas de succès.

Ronit Oommen
la source
1
De quoi s'agit- platformil? S'agit-il d'Ionic?
Günter Zöchbauer
@ Günter Zöchbauer La plate-forme est angulaire, je voulais juste mentionner les codes que j'avais essayés.
Ronit Oommen
1
Platformest un service ionique. Alors je suppose que c'est un projet ionique?
robbannn
Copie
BlackBeard
@BlackBeard Je ne pense pas que ce soit un doublon en raison de la différence entre Angular 2 et 4.
tehlivi du

Réponses:

261

Pour l'obtenir sur init

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

Si vous voulez le garder à jour lors du redimensionnement:

@HostListener('window:resize', ['$event'])
onResize(event) {
  this.innerWidth = window.innerWidth;
}
Eduardo Vargas
la source
2
this.innerWidth = event.target.innerWidth; ... est peut-être plus efficace, produit le même résultat
danday74
2
Recommandez également ceci dans le constructeur si vous utilisez lodash ... this.onResize = debounce (this.onResize, 150, {Leading: false, trailing: true}) ... pour éviter que la méthode onResize ne soit appelée trop fréquemment. . vous devrez ... importer {debounce} depuis 'lodash'
danday74
Je pense que j'ai préféré utiliser ngAfterViewInitla méthode du ngOnInit crochet de vie au lieu de la méthode du crochet de vie
ahmed hamdy
39

Si vous souhaitez réagir sur certains points d'arrêt (par exemple, faites quelque chose si la largeur est inférieure à 768px), vous pouvez également utiliser BreakpointObserver:

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

{ ... }

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

ou même écouter les modifications apportées à ce point d'arrêt:

breakpointObserver.observe([
  '(max-width: 768px)'
    ]).subscribe(result => {
      if (result.matches) {
        doSomething();
      } else {
        // if necessary:
        doSomethingElse();
      }
    });
Jeremy Benks
la source
si vous voulez agir uniquement si l'observateur correspond aux critères que vous devez ajouter à l'intérieur abonnez-vous, if (result.matches)sinon il appellera même si ce n'est pas le cas
karoluS
1
@karoluS: Oui bien sûr. J'ai modifié ma réponse pour que cela soit clair. Je vous remercie.
Jeremy Benks le
Semble que cela cdka une dépendance de pair sur une version spécifique d'angular. Comme 7 pour le dernier. Quoi qu'il en soit, je peux l'utiliser pour une version plus ancienne (angulaire 4 comme dans la question)?
LeOn - Han Li
@Leon li: J'utilise en fait angular 7, c'est vrai. Cependant: pour autant que je sache, vous pouvez également utiliser cdk dans angular 4. Selon ce billet de blog, vous avez besoin de cdk 2.x. Pour autant que je sache, cela doit être installé manuellement et avec la version spécifiée comme ceci: npm @ angular / cdk @ 4
Jeremy Benks
@JeremyBenks Y, nous utilisons ng 4.2.6spécifiquement. Impossible de trouver une version cdk 2.x à ce sujet, seulement 4.1.x et 4.3.x. Bref, merci!
LeOn - Han Li
9

Si vous souhaitez que vos composants restent facilement testables, vous devez envelopper l'objet de fenêtre globale dans un service angulaire:

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

@Injectable()
export class WindowService {

  get windowRef() {
    return window;
  }

}

Vous pouvez ensuite l'injecter comme n'importe quel autre service:

constructor(
    private windowService: WindowService
) { }

Et consommez ...

  ngOnInit() {
      const width= this.windowService.windowRef.innerWidth;
  }
Kildareflare
la source
2
Je ne vois pas l'intérêt de faire un service pour faire exactement ce qu'il a déjà sous la main. window.innerWidth.
Rodrigo
1
Premièrement, DI est la méthode angulaire et rend les dépendances d'un composant / d'une directive plus claires. Mais le point principal mentionné ici est de rendre le code qui utilise le service plus facile à tester. En utilisant cette approche, WindowService peut être simulé dans n'importe quel test écrit pour les composants qui utilisent le service.
Kildareflare
9

La documentation pour Platform width()et height(), il est indiqué que ces méthodes utilisent respectivement window.innerWidthet window.innerHeight. Mais l'utilisation des méthodes est préférable car les dimensions sont des valeurs mises en cache, ce qui réduit le risque de lectures DOM multiples et coûteuses.

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();
    });
}
Robbannn
la source
Comment ne se widthde se windowrapporter à DOM? Logiquement, cela ne devrait pas dépendre de ce à quoi ressemble l'arbre dom.
Shahryar Saljoughi le
7

Ceci est un exemple de service que j'utilise.

Vous pouvez obtenir la largeur de l'écran en vous abonnant à screenWidth$ou via screenWidth$.value.

La même chose est pour mediaBreakpoint$(ou 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');
    }
  }

}

J'espère que cela aide quelqu'un

Denis
la source
Ma réponse préférée! Assurez-vous simplement d'ajouter this.init () dans le constructeur pour que cela fonctionne.
Ben
3
@HostListener("window:resize", [])
public onResize() {
  this.detectScreenSize();
}

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

private detectScreenSize() {
    const height = window.innerHeight;
    const width = window.innerWidth;
}
Dhruv Parekh
la source
3
Expliquez toujours pourquoi cela pourrait être la bonne solution
thedp
3

La réponse est très simple. écrivez le code ci-dessous

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();
}
}
Harish Mahajan
la source
1

Vous pouvez utiliser la méthode getter dactylographiée pour ce scénario. Comme ça

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

Et utilisez-le dans un modèle comme celui-ci:

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

Vous n'aurez besoin d'aucun gestionnaire d'événements pour vérifier le redimensionnement / de la fenêtre, cette méthode vérifiera automatiquement la taille à chaque fois.

Rohan Gayen
la source