ค้นหาเว็บไซต์

จาวาสคริปต์ - เหตุการณ์ปรับขนาดหน้าต่างใน Angular


ฉันต้องการทำงานบางอย่างตามเหตุการณ์ปรับขนาดหน้าต่าง (เมื่อโหลดและแบบไดนามิก)

ปัจจุบันฉันมี DOM ของฉันดังนี้:

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

เหตุการณ์ทำงานอย่างถูกต้อง

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

ฉันจะดึงความกว้างและความสูงจากออบเจ็กต์อีเวนต์นี้ได้อย่างไร?

ขอบคุณ

คำตอบ #1

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

หรือใช้ตัวตกแต่ง HostListener:

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

หรืออีกวิธีหนึ่งโดยใช้ตัวตกแต่ง HostListener

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

เป้าหมายระดับโลกที่รองรับได้แก่ window, document, และ body.

จนกว่า https://github.com/angular/angular/issues/13248 จะถูกนำไปใช้ใน Angular จะดีกว่าสำหรับประสิทธิภาพในการสมัครรับเหตุการณ์ DOM แบบมีคำสั่งและใช้ RXJS เพื่อลดจำนวนเหตุการณ์ดังที่แสดงในบางคำตอบอื่น ๆ

คำตอบ #2

ฉันรู้ว่านี่ถูกถามมานานแล้ว แต่ตอนนี้มีวิธีที่ดีกว่าที่จะทำสิ่งนี้! ฉันไม่แน่ใจว่าใครจะเห็นคำตอบนี้หรือเปล่า อย่างชัดเจน การนำเข้าของคุณ:

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

จากนั้นในคอมโพเนนต์ของคุณ:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

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

แล้วอย่าลืมยกเลิกการสมัครเมื่อทำลาย!

ngOnDestroy() {
    this.resizeSubscription$.unsubscribe()
}

คำตอบ #3

คำตอบของ @ Günterนั้นถูกต้อง ฉันแค่อยากจะแนะนำวิธีอื่น

คุณยังสามารถเพิ่ม host-binding ภายใน @Component()-decorator ได้ คุณสามารถใส่อีเวนต์และการเรียกฟังก์ชันที่ต้องการใน host-metadata-property ได้ดังนี้:

@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
   }
}

คำตอบ #4

มีบริการ ViewportRuler ใน Angular CDK มันทำงานนอกโซน รองรับ orientationchange และ resize และยังทำงานร่วมกับการเรนเดอร์ฝั่งเซิร์ฟเวอร์ได้ด้วย

@Component({
  selector: 'my-app',
  template: `
    <p>Viewport size: {{ width }} x {{ height }}</p>
  `
})
export class AppComponent implements OnDestroy {
  width: number;
  height: number;
  private readonly viewportChange = this.viewportRuler
    .change(200)
    .subscribe(() => this.ngZone.run(() => this.setSize()));
  constructor(
    private readonly viewportRuler: ViewportRuler,
    private readonly ngZone: NgZone
  ) {
    // Change happens well, on change. The first load is not a change, so we init the values here. (You can use `startWith` operator too.)
    this.setSize();
  }
  // Never forget to unsubscribe!
  ngOnDestroy() {
    this.viewportChange.unsubscribe();
  }

  private setSize() {
    const { width, height } = this.viewportRuler.getViewportSize();
    this.width = width;
    this.height = height;
  }
}

ตัวอย่าง Stackblitz สำหรับ ViewportRuler

ประโยชน์คือ มันจำกัดรอบการตรวจจับการเปลี่ยนแปลง (มันจะถูกเรียกใช้เฉพาะเมื่อคุณเรียก callback ในโซน) ในขณะที่ (window:resize) จะเรียกการตรวจจับการเปลี่ยนแปลงทุกครั้งที่ถูกเรียกใช้งาน

คำตอบ #5

วิธีที่ถูกต้องในการทำเช่นนี้คือการใช้คลาส EventManager เพื่อผูกกับเหตุการณ์ ซึ่งจะทำให้โค้ดของคุณทำงานบนแพลตฟอร์มอื่นได้ เช่น การเรนเดอร์ฝั่งเซิร์ฟเวอร์ด้วย 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);
  }
}

การใช้งานในคอมโพเนนต์นั้นง่ายเพียงแค่เพิ่มบริการนี้เป็นผู้ให้บริการใน app.module ของคุณ จากนั้นนำเข้ามาในตัวสร้างของคอมโพเนนต์

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

คำตอบ #6

นี่คือวิธีที่ดีกว่าที่จะทำ ตามคำตอบของ Birowsky

ขั้นตอนที่ 1: สร้าง angular service พร้อม 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
    };
};

ขั้นตอนที่ 2: ฉีด service ที่กล่าวมาข้างต้นและสมัครรับ Observables ใด ๆ ที่สร้างขึ้นภายใน service นั้นทุกที่ที่คุณต้องการรับเหตุการณ์ปรับขนาดหน้าต่าง

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

}

ความเข้าใจอย่างลึกซึ้งเกี่ยวกับการเขียนโปรแกรมเชิงปฏิกิริยาจะช่วยเสมอในการเอาชนะปัญหาที่ยาก หวังว่านี่จะช่วยใครสักคนได้

คำตอบ #7

ฉันยังไม่เห็นใครพูดถึง MediaMatcher ของ angular/cdk เลย

คุณสามารถกำหนด MediaQuery และแนบตัวฟังไปกับมัน - จากนั้นที่ใดก็ได้ในเทมเพลตของคุณ (หรือ ts) คุณสามารถเรียกใช้งานบางอย่างได้ถ้า Matcher ตรงกัน ตัวอย่างสด

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

}

แอป.ส่วนประกอบ.เอชทีเอ็มแอล

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

แหล่งที่มา: https://material.angular.io/components/sidenav/overview

บทความที่เกี่ยวข้อง