Evento de redimensionamento de janela angular

232

Gostaria de executar algumas tarefas com base no evento de redimensionamento da janela (no carregamento e dinamicamente).

Atualmente, tenho o meu DOM da seguinte maneira:

<div id="Harbour">
    <div id="Port" (window:resize)="onResize($event)" >
        <router-outlet></router-outlet>
    </div>
</div>

O evento é acionado corretamente

export class AppComponent {
   onResize(event) {
        console.log(event);
    }
}

Como recupero a largura e a altura desse objeto de evento?

Obrigado.

DanAbdn
fonte
6
Não é realmente uma pergunta angular. olhe para o objeto da janela . Você pode obtê-lo innerHeighte innerWidthpropriedades ..
Sasxa
1
@Sasxa estiver correto, você só precisa fazerconsole.log(event.target.innerWidth )
Pankaj Parkar
Obrigado pela informação Sasxa / Pankaj - eu não tinha certeza se era apenas uma coisa simples de javascript ou uma coisa de TypeScript ou uma coisa de evento Angular. Estou subindo uma curva de aprendizado muito acentuada para mim aqui e aprecio sua opinião.
21416 DanAbdn

Respostas:

527
<div (window:resize)="onResize($event)"
onResize(event) {
  event.target.innerWidth;
}

ou usando o decorador HostListener :

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

Metas globais suportados são window, documente body.

Até que https://github.com/angular/angular/issues/13248 seja implementado no Angular, é melhor para o desempenho inscrever-se imperativamente nos eventos DOM e usar o RXJS para reduzir a quantidade de eventos, conforme mostrado em algumas das outras respostas.

Günter Zöchbauer
fonte
15
Existe alguma documentação sobre a sintaxe usada: window: redimensionar ?
Clemente
3
Exatamente. Você pode usar document, windowe body github.com/angular/angular/blob/…
Günter Zöchbauer
5
resposta perfeita .. eu acredito que @HostListener é a maneira mais limpa :) mas certifique-se de importar o HostListener primeiro usando import { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma
4
Dica rápida: Se você também deseja acionar o primeiro carregamento, implemente o ngAfterViewInit a partir de @ angular / core. angular.io/api/core/AfterViewInit
Zymotik
7
Claro, no entanto, para aqueles que querem saber como funciona o HostListener com debounceTime, siga o link abaixo plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
jcdsr
65

A resposta de @ Günter está correta. Eu só queria propor outro método.

Você também pode adicionar a ligação ao host dentro do @Component()-decorator. Você pode colocar o evento e a chamada de função desejada na propriedade host-metadata-property da seguinte forma:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  host: {
    '(window:resize)': 'onResize($event)'
  }
})
export class AppComponent{
   onResize(event){
     event.target.innerWidth; // window width
   }
}
John
fonte
2
Eu tentei todos os métodos nesta página e parece que nenhum deles funciona. Existe alguma documentação oficial sobre isso.
Tomate
como a altura da janela de visualização é obtida no carregamento?
Giridhar Karnik
@GiridharKarnik, você já tentou fazer um window.innerWidthinterior ngOnInit, ou ngAfterViewInitmétodos?
John
@Tomato A referência da API pode ser encontrada aqui Muitas das referências são geradas automaticamente (presumo) e faltam exemplos ou explicações detalhadas. Algumas referências de API têm muitos exemplos. Não consegui encontrar um exemplo concreto dos documentos sobre isso. Talvez esteja oculto em algum lugar: P
John
1
aqui é uma referência de como a sua utilização
Anand Rockzz
52

Eu sei que isso foi perguntado há muito tempo, mas existe uma maneira melhor de fazer isso agora! Não tenho certeza se alguém verá esta resposta. Obviamente suas importações:

import { fromEvent, Observable, Subscription } from "rxjs";

Então no seu componente:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

ngOnInit() {
    this.resizeObservable$ = fromEvent(window, 'resize')
    this.resizeSubscription$ = this.resizeObservable$.subscribe( evt => {
      console.log('event: ', evt)
    })
}

Não se esqueça de cancelar a inscrição em destruir!

ngOnDestroy() {
    this.resizeSubscription$.unsubscribe()
}
Chris Stanley
fonte
A única maneira que funcionou para mim. Obrigado!! :-) ... Eu apenas tive que ajustar sua importação. Talvez meus rxjs é mais recente:import { fromEvent, Observable,Subscription } from "rxjs";
Jette
Onde posso adicionar debounce (1000) nisso?
Deepak Thomas
3
Desculpe pelo atraso na resposta: Para adicionar debounce, você vai querer usarthis.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
Chris Stanley
41

A maneira correta de fazer isso é utilizar a classe EventManager para vincular o evento. Isso permite que seu código funcione em plataformas alternativas, por exemplo, renderização no lado do servidor com o Angular Universal.

import { EventManager } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import { Injectable } from '@angular/core';

@Injectable()
export class ResizeService {

  get onResize$(): Observable<Window> {
    return this.resizeSubject.asObservable();
  }

  private resizeSubject: Subject<Window>;

  constructor(private eventManager: EventManager) {
    this.resizeSubject = new Subject();
    this.eventManager.addGlobalEventListener('window', 'resize', this.onResize.bind(this));
  }

  private onResize(event: UIEvent) {
    this.resizeSubject.next(<Window>event.target);
  }
}

O uso em um componente é tão simples quanto adicionar esse serviço como provedor ao seu app.module e depois importá-lo no construtor de um componente.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'my-component',
  template: ``,
  styles: [``]
})
export class MyComponent implements OnInit {

  private resizeSubscription: Subscription;

  constructor(private resizeService: ResizeService) { }

  ngOnInit() {
    this.resizeSubscription = this.resizeService.onResize$
      .subscribe(size => console.log(size));
  }

  ngOnDestroy() {
    if (this.resizeSubscription) {
      this.resizeSubscription.unsubscribe();
    }
  }
}
cgatian
fonte
Isso nunca é acionado no celular, até onde eu sei. Como você obtém o tamanho inicial da janela com essa abordagem?
user3170530
O celular não possui um windowobjeto, assim como um servidor não possui um window. Im não estão familiarizados com as diferentes configurações móveis, mas você deve ser capaz de se adaptar facilmente o código acima para se ligar ao correta ouvinte evento global
cgatian
@cgatian Eu sou um noob, mas esta parece ser a resposta certa. Infelizmente, não estou tendo sorte em fazer minha assinatura. Faça o login no componente. Você poderia adicionar à resposta como assinar isso em um componente para que se possa ver as atualizações?
Matthew Harwood
@ cgatian Eu vou fazer um plunker, mas isso parecia não funcionar. O filtro no serviço parece estranho tho stackoverflow.com/q/46397531/1191635
Matthew Harwood
3
@cgatian Mobile does not have a window object.... por que você acha que os navegadores móveis não têm um objeto de janela?
Drenai
31

Aqui está uma maneira melhor de fazer isso. Baseado na resposta de Birowsky .

Etapa 1: Crie um angular servicecom o RxJS Observables .

import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs';

@Injectable()
export class WindowService {
    height$: Observable<number>;
    //create more Observables as and when needed for various properties
    hello: string = "Hello";
    constructor() {
        let windowSize$ = new BehaviorSubject(getWindowSize());

        this.height$ = (windowSize$.pluck('height') as Observable<number>).distinctUntilChanged();

        Observable.fromEvent(window, 'resize')
            .map(getWindowSize)
            .subscribe(windowSize$);
    }

}

function getWindowSize() {
    return {
        height: window.innerHeight
        //you can sense other parameters here
    };
};

Etapa 2: injete o que foi dito acima servicee assine qualquer um dos Observablescriados no serviço onde quer que você queira receber o evento de redimensionamento da janela.

import { Component } from '@angular/core';
//import service
import { WindowService } from '../Services/window.service';

@Component({
    selector: 'pm-app',
    templateUrl: './componentTemplates/app.component.html',
    providers: [WindowService]
})
export class AppComponent { 

    constructor(private windowService: WindowService) {

        //subscribe to the window resize event
        windowService.height$.subscribe((value:any) => {
            //Do whatever you want with the value.
            //You can also subscribe to other observables of the service
        });
    }

}

Um bom entendimento da Programação Reativa sempre ajudará a superar problemas difíceis. Espero que isso ajude alguém.

Giridhar Karnik
fonte
Eu acredito que este é um erro: this.height $ = (windowSize $ .pluck ('height') como Observable <número>) .distinctUntilChanged (); Observable <número>) .distinctUntilChanged (); Parece que você colado na distinctUntilChanged () duas vezes seguidas
Zuriel
Eu não te entendi, por favor.
Giridhar Karnik
A detecção de alterações não será acionada enquanto você estiver realizando este evento fora do Angular, acredite que é isso que ele quis dizer.
Mark Pieszak - Trilon.io
1
Ocorreu um erro ao dizer 'arrancar' não existe no tipo BehaviorSubject. Alterar o código para this.height $ = windowSize $ .map (x => x.height) funcionou para mim.
Matt Sugden
@GiridharKamik Can você fornece uma solução que se inscrever em largura e altura ao mesmo tempo, que poderia subscrever tanto a largura, altura em 1 simples inscrever
ghiscoding
11

Eu não vi ninguém falando sobre MediaMatcherde angular/cdk.

Você pode definir um MediaQuery e anexar um ouvinte a ele. Em qualquer lugar do seu modelo (ou ts), você pode invocar coisas se o Matcher corresponder. LiveExample

App.Component.ts

import {Component, ChangeDetectorRef} from '@angular/core';
import {MediaMatcher} from '@angular/cdk/layout';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  mobileQuery: MediaQueryList;

  constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher) {
    this.mobileQuery = media.matchMedia('(max-width: 600px)');
    this._mobileQueryListener = () => changeDetectorRef.detectChanges();
    this.mobileQuery.addListener(this._mobileQueryListener);
  }

  private _mobileQueryListener: () => void;

  ngOnDestroy() {
    this.mobileQuery.removeListener(this._mobileQueryListener);
  }

}

App.Component.Html

<div [class]="mobileQuery.matches ? 'text-red' : 'text-blue'"> I turn red on mobile mode 
</div>

App.Component.css

.text-red { 
   color: red;
}

.text-blue {
   color: blue;
}

fonte: https://material.angular.io/components/sidenav/overview

Stavm
fonte
6

Supondo que <600px significa móvel para você, você pode usar este observável e assinar:

Primeiro, precisamos do tamanho atual da janela. Então criamos um observável que emite apenas um único valor: o tamanho atual da janela.

initial$ = Observable.of(window.innerWidth > 599 ? false : true);

Então precisamos criar outro observável, para que possamos saber quando o tamanho da janela foi alterado. Para isso, podemos usar o operador "fromEvent". Para saber mais sobre os operadores do rxjs, visite: rxjs

resize$ = Observable.fromEvent(window, 'resize').map((event: any) => {
  return event.target.innerWidth > 599 ? false : true;
 });

Mescla esses dois fluxos para receber nosso observável:

mobile$ = Observable.merge(this.resize$, this.initial$).distinctUntilChanged();

Agora você pode se inscrever assim:

mobile$.subscribe((event) => { console.log(event); });

Lembre-se de cancelar a inscrição :)

Flosut Mözil
fonte
5

Há um serviço ViewportRuler no CDK angular. Ele roda fora da zona e também funciona com a renderização no servidor.

Totati
fonte
2
essa deve ser a resposta aceita. Tem tudo o que é necessário + esta é a funcionalidade embutida do CDK angular.
Deniss M.
3

Com base na solução do @cgatian, sugiro a seguinte simplificação:

import { EventManager } from '@angular/platform-browser';
import { Injectable, EventEmitter } from '@angular/core';

@Injectable()
export class ResizeService {

  public onResize$ = new EventEmitter<{ width: number; height: number; }>();

  constructor(eventManager: EventManager) {
    eventManager.addGlobalEventListener('window', 'resize',
      e => this.onResize$.emit({
        width: e.target.innerWidth,
        height: e.target.innerHeight
      }));
  }
}

Uso:

import { Component } from '@angular/core';
import { ResizeService } from './resize-service';

@Component({
  selector: 'my-component',
  template: `{{ rs.onResize$ | async | json }}`
})
export class MyComponent {
  constructor(private rs: ResizeService) { }
}
Johannes Hoppe
fonte
Achei isso como a melhor solução, no entanto, isso só funciona quando a janela é redimensionada, mas não quando carrega ou quando o roteador muda. Você sabe como aplicar com a alteração, recarregamento ou carregamento do roteador?
Jcdsr #
Você pode adicionar uma função ao serviço e acioná-la no componente @jcdsr: getScreenSize () {this.onResize $ .emit ({width: window.innerWidth, height: window.innerHeight}); }
DanielWaw 28/01
3

Esta não é exatamente a resposta para a pergunta, mas pode ajudar alguém que precisa detectar alterações de tamanho em qualquer elemento.

Eu criei uma biblioteca que adiciona resizedevento a qualquer elemento - Angular Resize Event .

Ele usa internamente a ResizeSensorpartir de consultas de elemento CSS .

Exemplo de uso

HTML

<div (resized)="onResized($event)"></div>

TypeScript

@Component({...})
class MyComponent {
  width: number;
  height: number;

  onResized(event: ResizedEvent): void {
    this.width = event.newWidth;
    this.height = event.newHeight;
  }
}
Martin Volek
fonte
se você quiser detectar janela é redimensionada depois alças angular de materiais Breakpoint Observer já material.angular.io/cdk/layout/overview
Darren Rua
Mas isso não detecta apenas o redimensionamento das janelas, mas qualquer elemento é redimensionado.
Martin Volek
2

Eu escrevi esta biblioteca para encontrar uma vez que a alteração do tamanho do limite do componente (redimensionamento) no Angular, pode ajudar outras pessoas. Você pode colocá-lo no componente raiz, fará o mesmo que redimensionar a janela.

Etapa 1: Importar o Módulo

import { BoundSensorModule } from 'angular-bound-sensor';

@NgModule({
  (...)
  imports: [
    BoundSensorModule,
  ],
})
export class AppModule { }

Etapa 2: adicione a diretiva como abaixo

<simple-component boundSensor></simple-component>

Etapa 3: receber os detalhes do tamanho do limite

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

@Component({
  selector: 'simple-component'
  (...)
})
class SimpleComponent {
  @HostListener('resize', ['$event'])
  onResize(event) {
    console.log(event.detail);
  }
}
ORAÇÃO
fonte
1

No Angular2 (2.1.0), uso o ngZone para capturar o evento de alteração de tela.

Veja o exemplo:

import { Component, NgZone } from '@angular/core';//import ngZone library
...
//capture screen changed inside constructor
constructor(private ngZone: NgZone) {
    window.onresize = (e) =>
    {
        ngZone.run(() => {
            console.log(window.innerWidth);
            console.log(window.innerHeight);
        });
    };
}

Espero que isso ajude!

Anh Hoang
fonte
1

O código abaixo permite observar qualquer alteração de tamanho para qualquer div em Angular.

<div #observed-div>
</div>

depois no componente:

oldWidth = 0;
oldHeight = 0;

@ViewChild('observed-div') myDiv: ElementRef;
ngAfterViewChecked() {
  const newWidth = this.myDiv.nativeElement.offsetWidth;
  const newHeight = this.myDiv.nativeElement.offsetHeight;
  if (this.oldWidth !== newWidth || this.oldHeight !== newHeight)
    console.log('resized!');

  this.oldWidth = newWidth;
  this.oldHeight = newHeight;
}
abedfar
fonte
Quero alterar uma varibala do contador = 6 para contar = 0 na largura máxima 767px, como faço isso?
Thanveer Shah
0

todas as soluções não estão funcionando no lado do servidor ou no modo universal angular

jcdsr
fonte
0

Eu verifiquei a maioria dessas respostas. Decidiu então verificar a documentação do Angular no Layout .

O Angular possui seu próprio Observador para detectar tamanhos diferentes e é fácil de implementar no componente ou em um Serviço.

um exemplo simples seria:

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

@Component({...})
class MyComponent {
  constructor(breakpointObserver: BreakpointObserver) {
    breakpointObserver.observe([
      Breakpoints.HandsetLandscape,
      Breakpoints.HandsetPortrait
    ]).subscribe(result => {
      if (result.matches) {
        this.activateHandsetLayout();
      }
    });
  }
}

espero que ajude

Mj Jameel
fonte