Detectando cambios de tamaño de ventana en tiempo real en Angular 4

118

He estado tratando de construir una barra de navegación receptiva y no deseo usar una consulta de medios, por lo que tengo la intención de usarla *ngIFcon el tamaño de la ventana como criterio. Pero he tenido un problema porque no puedo encontrar ningún método o documentación sobre la detección del tamaño de la ventana de Angular 4. También probé el método JavaScript, pero no es compatible.

También probé lo siguiente :

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

... que se usó en iónico.

Y screen.availHeight, pero aún sin éxito.

Ronit Oommen
fuente
1
¿De qué se platformtrata? ¿Se trata de Ionic?
Günter Zöchbauer
@ Günter Zöchbauer La plataforma es angular, solo quería mencionar los códigos que había probado.
Ronit Oommen
1
Platformes un servicio iónico. ¿Entonces supongo que es un proyecto Ionic?
robbannn
@BlackBeard No creo que sea un duplicado debido a la diferencia entre Angular 2 y 4.
tehlivi

Respuestas:

261

Para conseguirlo en init

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

Si desea mantenerlo actualizado al cambiar el tamaño:

@HostListener('window:resize', ['$event'])
onResize(event) {
  this.innerWidth = window.innerWidth;
}
Eduardo Vargas
fuente
2
this.innerWidth = event.target.innerWidth; ... es posiblemente más eficiente, produce el mismo resultado
danday74
2
También recomiende esto en el constructor si está usando lodash ... this.onResize = debounce (this.onResize, 150, {lead: false, trailing: true}) ... para evitar que el método onResize sea llamado con demasiada frecuencia. .Necesitará ... importar {debounce} de 'lodash'
danday74
Creo que prefiero usar el ngAfterViewInitmétodo de ngOnInit gancho de vida en lugar del método de gancho de vida
ahmed hamdy
39

Si desea reaccionar en ciertos puntos de interrupción (por ejemplo, hacer algo si el ancho es menor a 768px), también puede usar BreakpointObserver:

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

{ ... }

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

o incluso escuchar los cambios en ese punto de interrupción:

breakpointObserver.observe([
  '(max-width: 768px)'
    ]).subscribe(result => {
      if (result.matches) {
        doSomething();
      } else {
        // if necessary:
        doSomethingElse();
      }
    });
Jeremy Benks
fuente
si desea actuar solo si el observador coincide con los criterios que debe agregar dentro, suscríbase esto, de lo if (result.matches)contrario, llamará incluso si no lo hace
karoluS
1
@karoluS: Sí, por supuesto. Edité mi respuesta para dejarlo en claro. Gracias.
Jeremy Benks
Parece que esto cdktiene dependencia de pares en una versión específica de angular. Como 7 para lo último. De todos modos, ¿puedo usar esto para la versión anterior (angular 4 como en la pregunta)?
LeOn - Han Li
@Leon li: De hecho, uso angular 7, así es. Sin embargo: hasta donde yo sé, también puedes usar cdk en angular 4. De acuerdo con esta publicación de blog , necesita cdk 2.x. Hasta donde yo sé, esto debe instalarse manualmente y con la versión especificada de esta manera: npm @ angular / cdk @ 4
Jeremy Benks
@JeremyBenks Y, estamos usando ng 4.2.6específicamente. No puedo encontrar una versión de cdk 2.x en eso, solo 4.1.xy 4.3.x. ¡Gracias de todos modos!
LeOn - Han Li
9

Si desea que sus componentes sigan siendo fácilmente probables, debe envolver el objeto de ventana global en un servicio angular:

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

@Injectable()
export class WindowService {

  get windowRef() {
    return window;
  }

}

Luego puede inyectarlo como cualquier otro servicio:

constructor(
    private windowService: WindowService
) { }

Y consumir ...

  ngOnInit() {
      const width= this.windowService.windowRef.innerWidth;
  }
Kildareflare
fuente
2
No veo el sentido de hacer un servicio para hacer exactamente lo que ya está disponible. window.innerWidth.
Rodrigo
1
En primer lugar, DI es la forma angular y aclara las dependencias de un componente / directiva. Pero el punto principal mencionado aquí es hacer que el código que usa el servicio sea más fácil de probar. Con este enfoque, WindowService puede simularse en cualquier prueba escrita para componentes que usan el servicio.
Kildareflare
9

La documentación de Platform width()y height(), se indica que estos métodos usan window.innerWidthy window.innerHeightrespectivamente. Pero se prefiere usar los métodos ya que las dimensiones son valores en caché, lo que reduce la posibilidad de lecturas DOM múltiples y costosas.

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
fuente
¿Cómo se relaciona el conseguir widthde ? Lógicamente, no debería depender del aspecto del árbol dom. windowDOM
Shahryar Saljoughi
7

Este es un ejemplo de servicio que utilizo.

Puede obtener el ancho de la pantalla suscribiéndose ao screenWidth$vía screenWidth$.value.

Lo mismo es para 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');
    }
  }

}

Espero que esto ayude a alguien

Denis
fuente
¡Mi respuesta favorita! Solo asegúrese de agregar this.init () en el constructor para que funcione.
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
fuente
3
Explique siempre por qué esta podría ser la solución correcta
thedp
3

La respuesta es muy simple. escribe el siguiente código

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
fuente
1

Puede utilizar el método getter de mecanografiado para este escenario. Me gusta esto

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

Y usa eso en una plantilla como esta:

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

No necesitará ningún controlador de eventos para verificar el cambio de tamaño / de la ventana, este método verificará el tamaño cada vez automáticamente.

Rohan Gayen
fuente