Ereignis zur Größenänderung des Winkelfensters

232

Ich möchte einige Aufgaben basierend auf dem Ereignis zur Größenänderung des Fensters ausführen (beim Laden und dynamisch).

Derzeit habe ich mein DOM wie folgt:

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

Das Ereignis wird korrekt ausgelöst

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

Wie rufe ich die Breite und Höhe von diesem Ereignisobjekt ab?

Vielen Dank.

DanAbdn
quelle
6
Nicht wirklich eine Winkelfrage. schau dir das Fensterobjekt an . Sie können es innerHeightund innerWidthEigenschaften bekommen ..
Sasxa
1
@ Sasxa ist richtig, Sie müssen nur tunconsole.log(event.target.innerWidth )
Pankaj Parkar
Vielen Dank für die Info Sasxa / Pankaj - ich war mir nicht sicher, ob es sich nur um eine einfache Javascript-Sache oder eine Typescript-Sache oder eine Angular-Ereignissache handelte. Ich klettere hier eine sehr steile Lernkurve für mich und schätze Ihren Beitrag.
DanAbdn

Antworten:

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

oder mit dem HostListener-Dekorator :

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

Unterstützte globale Ziele sind window, documentund body.

Bis um https://github.com/angular/angular/issues/13248 in Angular implementiert ist, ist es für die Leistung besser, DOM-Ereignisse unbedingt zu abonnieren und RXJS zu verwenden, um die Anzahl der Ereignisse zu reduzieren, wie in einigen anderen Antworten gezeigt.

Günter Zöchbauer
quelle
15
Gibt es eine Dokumentation zu der von Ihnen verwendeten Syntax: window: resize ?
Clement
3
Genau. Sie können verwenden document, windowund body github.com/angular/angular/blob/…
Günter Zöchbauer
5
perfekte Antwort .. Ich glaube, @HostListener ist der sauberere Weg :) aber stellen Sie sicher, dass Sie den HostListener zuerst mitimport { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma
4
Kurztipp: Wenn Sie auch beim ersten Laden auslösen möchten, implementieren Sie ngAfterViewInit von @ angle / core. angle.io/api/core/AfterViewInit
Zymotik
6
Sicher, für diejenigen, die wissen möchten, wie HostListener mit debounceTime funktioniert, folgen Sie dem Link unten plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
jcdsr
65

@ Günters Antwort ist richtig. Ich wollte nur noch eine andere Methode vorschlagen.

Sie können auch die Host-Bindung im @Component()-decorator hinzufügen. Sie können das Ereignis und den gewünschten Funktionsaufruf wie folgt in die Eigenschaft host-metadata einfügen:

@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
quelle
2
Ich habe jede Methode auf dieser Seite ausprobiert und es scheint, dass keine von ihnen funktioniert. Gibt es eine offizielle Dokumentation dazu?
Tomate
Wie wird die Höhe des Ansichtsfensters unter Last ermittelt?
Giridhar Karnik
@ GiridharKarnik, hast du versucht, ein window.innerWidthInside ngOnInitoder ngAfterViewInitMethoden zu machen?
John
@Tomato Die API-Referenz finden Sie hier. Viele der Referenzen werden automatisch generiert (ich nehme an) und es fehlen Beispiele oder detaillierte Erklärungen. Einige API-Referenzen haben viele Beispiele. Ich konnte jedoch kein konkretes Beispiel aus den Dokumenten dazu finden. Vielleicht ist es irgendwo versteckt: P
John
1
Hier ist eine Referenz, wie es verwendet wird
Anand Rockzz
52

Ich weiß, dass dies vor langer Zeit gefragt wurde, aber es gibt jetzt einen besseren Weg, dies zu tun! Ich bin mir nicht sicher, ob jemand diese Antwort sehen wird. Offensichtlich Ihre Importe:

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

Dann in Ihrer Komponente:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

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

Dann melden Sie sich unbedingt bei destroy ab!

ngOnDestroy() {
    this.resizeSubscription$.unsubscribe()
}
Chris Stanley
quelle
Der einzige Weg, der für mich funktioniert hat. Vielen Dank!! :-) ... Ich musste nur deinen Import anpassen. Vielleicht ist mein RXJs neuer:import { fromEvent, Observable,Subscription } from "rxjs";
Jette
Wo kann ich Debounce (1000) hinzufügen?
Deepak Thomas
3
Entschuldigung für die verspätete Antwort: Um eine Entprellung hinzuzufügen, sollten Siethis.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
Chris Stanley
41

Der richtige Weg, dies zu tun, besteht darin, die EventManager- Klasse zum Binden des Ereignisses zu verwenden. Auf diese Weise kann Ihr Code auf alternativen Plattformen verwendet werden, z. B. beim serverseitigen Rendern mit 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);
  }
}

Die Verwendung in einer Komponente ist so einfach wie das Hinzufügen dieses Dienstes als Anbieter zu Ihrem app.module und das anschließende Importieren in den Konstruktor einer Komponente.

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
quelle
Soweit ich das beurteilen kann, wird dies auf Mobilgeräten nie ausgelöst. Wie erhalten Sie mit diesem Ansatz die anfängliche Fenstergröße?
user3170530
Mobile hat kein windowObjekt, genauso wie ein Server kein hat window.
Ich bin
@cgatian Ich bin ein Noob, aber das scheint die richtige Antwort zu sein. Leider habe ich kein Glück, mein Abonnement zu erstellen. Melden Sie sich bei der Komponente an. Können Sie der Antwort hinzufügen, wie Sie dies in einer Komponente abonnieren, damit Sie die Updates sehen können?
Matthew Harwood
@cgatian Ich werde einen Plunker machen, aber das schien nicht zu funktionieren. Der Filter im Service scheint seltsam, obwohl stackoverflow.com/q/46397531/1191635
Matthew Harwood
3
@cgatian Mobile does not have a window object.... warum haben mobile Browser Ihrer Meinung nach kein Fensterobjekt?
Drenai
31

Hier ist ein besserer Weg, dies zu tun. Basierend auf Birowskys Antwort.

Schritt 1: Erstellen Sie eine angular servicemit 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
    };
};

Schritt 2: Injizieren Sie das Obige serviceund abonnieren Sie eines der Observablesim Service erstellten Ereignisse, wo immer Sie das Fenstergrößenänderungsereignis erhalten möchten.

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
        });
    }

}

Ein fundiertes Verständnis der reaktiven Programmierung hilft immer bei der Überwindung schwieriger Probleme. Hoffe das hilft jemandem.

Giridhar Karnik
quelle
Ich glaube, das ist ein Fehler: this.height $ = (windowSize $ .pluck ('height') als Observable <Number>) .distinctUntilChanged (); Observable <Number>) .distinctUntilChanged (); Sieht so aus, als hätten Sie zweimal hintereinander in die uniqueUntilChanged () eingefügt
Zuriel
Ich habe dich nicht verstanden, bitte.
Giridhar Karnik
Die Änderungserkennung wird nicht ausgelöst, wenn Sie dieses Ereignis außerhalb von Angular durchführen. Glauben Sie, dass er das gemeint hat.
Mark Pieszak - Trilon.io
1
Ich hatte einen Fehler, der besagte, dass beim Typ BehaviorSubject kein "Zupfen" vorhanden ist. Das Ändern des Codes in this.height $ = windowSize $ .map (x => x.height) hat bei mir funktioniert.
Matt Sugden
@ GiridharKamik Können Sie eine Lösung bereitstellen, die gleichzeitig Breite und Höhe abonniert, bei der wir sowohl die Breite als auch die Höhe in einem einfachen Abonnement abonnieren können
Ghiscoding
11

Ich habe jemand reden hier nicht über gesehen MediaMatchervonangular/cdk .

Sie können eine MediaQuery definieren und einen Listener daran anhängen. Dann können Sie an einer beliebigen Stelle in Ihrer Vorlage (oder in ts) Inhalte aufrufen, wenn der Matcher übereinstimmt. 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;
}

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

Stavm
quelle
6

Angenommen, <600px bedeutet für Sie mobil, können Sie dieses Observable verwenden und abonnieren:

Zuerst benötigen wir die aktuelle Fenstergröße. Wir erstellen also eine Observable, die nur einen einzigen Wert ausgibt: die aktuelle Fenstergröße.

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

Dann müssen wir ein weiteres Observable erstellen, damit wir wissen, wann die Fenstergröße geändert wurde. Hierfür können wir den Operator "fromEvent" verwenden. Um mehr über die Betreiber von rxjs zu erfahren, besuchen Sie bitte: rxjs

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

Verschmelze diese beiden Ströme, um unser Beobachtbares zu erhalten:

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

Jetzt können Sie es folgendermaßen abonnieren:

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

Denken Sie daran, sich abzumelden :)

Flosut Mözil
quelle
5

Es gibt einen ViewportRuler- Dienst in Angular CDK. Es wird außerhalb der Zone ausgeführt und funktioniert auch mit serverseitigem Rendering.

Totati
quelle
2
Dies sollte die akzeptierte Antwort sein. Hat alles was benötigt wird + das ist eckige CDK eingebaute Funktionalität.
Deniss M.
3

Basierend auf der Lösung von @cgatian würde ich die folgende Vereinfachung vorschlagen:

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
      }));
  }
}

Verwendung:

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
quelle
Ich fand dies die beste Lösung, dies funktioniert jedoch nur, wenn die Größe des Fensters geändert wird, nicht jedoch, wenn es geladen wird oder wenn der Router wechselt. Wissen Sie, wie Sie sich beim Wechseln, Neuladen oder Laden des Routers bewerben können?
JCDSR
Sie können dem Service eine Funktion hinzufügen und diese in der Komponente @jcdsr auslösen: getScreenSize () {this.onResize $ .emit ({width: window.innerWidth, height: window.innerHeight}); }
DanielWaw
3

Dies ist keine genaue Antwort auf die Frage, kann jedoch jemandem helfen, der Größenänderungen an einem Element erkennen muss.

Ich habe eine Bibliothek erstellt, die resizedjedem Element ein Ereignis hinzufügt - Angular Resize Event .

Es wird intern ResizeSensoraus CSS-Elementabfragen verwendet .

Anwendungsbeispiel

HTML

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

Typoskript

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

  onResized(event: ResizedEvent): void {
    this.width = event.newWidth;
    this.height = event.newHeight;
  }
}
Martin Volek
quelle
Wenn Sie Fenstergrößenänderungen erkennen möchten, verarbeitet Angular Material Breakpoint Observer bereits material.angular.io/cdk/layout/overview
Darren Street
Dies erkennt jedoch nicht nur die Größe von Fenstern, sondern auch die Größe von Elementen.
Martin Volek
2

Ich habe diese Bibliothek geschrieben , um zu finden, wie sich die Größe der Komponentengrenzen in Angular ändert (Größe ändern). Möge dies anderen Menschen helfen. Sie können es auf die Stammkomponente setzen, wird das gleiche tun wie die Fenstergröße ändern.

Schritt 1: Importieren Sie das Modul

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

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

Schritt 2: Fügen Sie die Direktive wie unten hinzu

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

Schritt 3: Erhalten Sie die Details zur Grenzgröße

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

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

Auf Angular2 (2.1.0) verwende ich ngZone, um das Bildschirmänderungsereignis zu erfassen.

Schauen Sie sich das Beispiel an:

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);
        });
    };
}

Ich hoffe diese Hilfe!

Anh Hoang
quelle
1

Mit dem folgenden Code können Sie jede Größenänderung für ein bestimmtes Div in Angular beobachten.

<div #observed-div>
</div>

dann in der Komponente:

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
quelle
Ich möchte eine Varibale von counter = 6 in count = 0 bei maximal 767px ändern. Wie mache ich das?
Thanveer Shah
0

Alle Lösungen funktionieren nicht serverseitig oder in Angular Universal

jcdsr
quelle
0

Ich habe die meisten dieser Antworten überprüft. Dann entschied ich mich, die Angular-Dokumentation zum Layout zu lesen .

Angular verfügt über einen eigenen Observer zum Erkennen unterschiedlicher Größen und ist einfach in die Komponente oder einen Service zu implementieren.

Ein einfaches Beispiel wäre:

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();
      }
    });
  }
}

ich hoffe es hilft

Mj Jameel
quelle