จาวาสคริปต์ - เหตุการณ์ปรับขนาดหน้าต่างใน 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
