Detectando mudanças no tamanho da janela em tempo real no Angular 4

118

Tenho tentado construir uma barra de navegação responsiva e não desejo usar uma consulta de mídia, então pretendo usar *ngIFcom o tamanho da janela como um critério. Mas tenho enfrentado um problema, pois não consigo encontrar nenhum método ou documentação sobre a detecção de tamanho de janela Angular 4. Também tentei o método JavaScript, mas não é compatível.

Eu também tentei o seguinte :

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

... que foi usado no iônico.

E screen.availHeight, mas ainda sem sucesso.

Ronit Oommen
fonte
1
Sobre o quê platform? Isso é sobre Ionic?
Günter Zöchbauer
@ Günter Zöchbauer A plataforma é angular, só queria referir os códigos que experimentei.
Ronit Oommen
1
Platformé um serviço Ionic. Suponho que seja um projeto Ionic?
robbannn
@BlackBeard Não acho que seja uma duplicata devido à diferença entre Angular 2 e 4.
tehlivi

Respostas:

260

Para obtê-lo no init

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

Se você quiser mantê-lo atualizado no redimensionamento:

@HostListener('window:resize', ['$event'])
onResize(event) {
  this.innerWidth = window.innerWidth;
}
Eduardo vargas
fonte
2
this.innerWidth = event.target.innerWidth; ... é possivelmente mais eficiente, produz o mesmo resultado
danday74
2
Também recomendo isso no construtor se você estiver usando lodash ... this.onResize = debounce (this.onResize, 150, {lead: false, trailing: true}) ... para evitar que o método onResize seja chamado com muita frequência. . você precisará ... importar {debounce} de 'lodash'
danday74
Acho que preferi usar o ngAfterViewInitmétodo do ngOnInit gancho de vida em vez do método do gancho de vida
ahmed hamdy
39

Se você quiser reagir em certos pontos de interrupção (por exemplo, fazer algo se a largura for menor que 768px), você também pode usar BreakpointObserver:

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

{ ... }

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

ou mesmo ouvir as mudanças nesse ponto de interrupção:

breakpointObserver.observe([
  '(max-width: 768px)'
    ]).subscribe(result => {
      if (result.matches) {
        doSomething();
      } else {
        // if necessary:
        doSomethingElse();
      }
    });
Jeremy Benks
fonte
se você quiser agir apenas se o observador corresponder aos critérios que você precisa adicionar, inscreva-se, if (result.matches)caso contrário, ele chamará mesmo que não
karoluS
1
@karoluS: Sim, claro. Editei minha resposta para deixar isso claro. Obrigado.
Jeremy Benks de
Parece que isso cdkdepende de uma versão específica do angular. Tipo 7 para o mais recente. De qualquer forma posso usar isso para uma versão mais antiga (angular 4 como na pergunta)?
LeOn - Han Li
@Leon li: Na verdade, eu uso angular 7, certo. Porém: Pelo que eu sei, você também pode usar cdk no angular 4. De acordo com esta postagem do blog, você precisa do cdk 2.x. Pelo que eu sei, ele deve ser instalado manualmente e com a versão especificada desta forma: npm @ angular / cdk @ 4
Jeremy Benks
@JeremyBenks Y, estamos usando o ng 4.2.6especificamente. Não é possível encontrar uma versão cdk 2.x nisso, apenas 4.1.xe 4.3.x. Enfim, obrigado!
LeOn - Han Li
9

Se você deseja que seus componentes permaneçam facilmente testáveis, você deve envolver o objeto de janela global em um serviço angular:

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

@Injectable()
export class WindowService {

  get windowRef() {
    return window;
  }

}

Você pode injetá-lo como qualquer outro serviço:

constructor(
    private windowService: WindowService
) { }

E consumir ...

  ngOnInit() {
      const width= this.windowService.windowRef.innerWidth;
  }
Kildareflare
fonte
2
Não vejo sentido em fazer um serviço para fazer exatamente o que já está disponível. window.innerWidth.
Rodrigo
1
Em primeiro lugar, DI é a forma Angular e torna as dependências de um componente / diretiva mais claras. Mas o ponto principal mencionado aqui é tornar o código que usa o serviço mais fácil de testar. Usando essa abordagem, o WindowService pode ser simulado em qualquer teste escrito para componentes que usam o serviço.
Kildareflare
9

A documentação para Platform width()e height(), afirma-se que esses métodos usam window.innerWidthe, window.innerHeightrespectivamente. Mas é preferível usar os métodos, pois as dimensões são valores em cache, o que reduz a chance de leituras DOM múltiplas e caras.

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
fonte
Como a obtenção widthde windowrelacionar DOM? Logicamente, não deve depender da aparência da árvore dom.
Shahryar Saljoughi
6

Este é um exemplo de serviço que utilizo.

Você pode obter a largura da tela assinando screenWidth$ou via screenWidth$.value.

O mesmo é para 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');
    }
  }

}

Espero que isso ajude alguém

Denis
fonte
Minha resposta favorita! Apenas certifique-se de adicionar this.init () no construtor 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
fonte
3
Sempre explique por que essa pode ser a solução certa
dia
3

A resposta é muito simples. escreva o código abaixo

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

Você pode usar o método getter typescript para este cenário. Como isso

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

E use isso em um modelo como este:

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

Você não precisará de nenhum manipulador de eventos para verificar o redimensionamento / da janela, este método verificará o tamanho todas as vezes automaticamente.

Rohan Gayen
fonte