เหตุการณ์การปรับขนาดหน้าต่างเชิงมุม


232

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

ขณะนี้ฉันมี 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);
    }
}

ฉันจะดึงความกว้างและความสูงจากวัตถุเหตุการณ์นี้ได้อย่างไร

ขอบคุณ


6
ไม่ใช่คำถามเชิงมุม ดูวัตถุหน้าต่าง คุณจะได้รับมันinnerHeightและinnerWidthคุณสมบัติ ..
Sasxa

1
@Sasxa ถูกต้องคุณต้องทำconsole.log(event.target.innerWidth )
Pankaj Parkar

ขอบคุณสำหรับข้อมูล Sasxa / Pankaj - ฉันไม่แน่ใจว่ามันเป็นเพียงแค่จาวาสคริปต์ธรรมดาหรือสิ่งที่เป็น typescript หรือสิ่งเชิงเหตุการณ์ ฉันกำลังปีนโค้งการเรียนรู้ที่สูงชันมากสำหรับตัวฉันเองที่นี่
DanAbdn

คำตอบ:


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

หรือใช้มัณฑนากร HostListener :

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

สนับสนุนเป้าหมายทั่วโลกwindow, และdocumentbody

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


15
มีเอกสารเกี่ยวกับไวยากรณ์ที่คุณใช้: window: ปรับขนาดหรือไม่
ผ่อนผัน

3
เผง คุณสามารถใช้document, windowและ, body github.com/angular/angular/blob/…
GünterZöchbauer

5
คำตอบที่สมบูรณ์แบบ .. ฉันเชื่อว่า @HostListener เป็นวิธีที่สะอาดกว่า :) แต่ให้แน่ใจว่าได้นำเข้า HostListener ก่อนใช้import { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma

4
เคล็ดลับด่วน: หากต้องการเปิดใช้งานการโหลดครั้งแรกให้ใช้ ngAfterViewInit จาก @ angular / core angular.io/api/core/AfterViewInit
Zymotik

7
แน่นอนว่าอย่างไรก็ตามสำหรับผู้ที่ต้องการที่จะรู้วิธีการทำงานกับ HostListener debounceTime ตามลิงค์ด้านล่างนี้plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
jcdsr

65

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

คุณสามารถเพิ่มการโยงโฮสต์ภายใน@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
   }
}

2
ฉันได้ลองทุกวิธีในหน้านี้แล้วและดูเหมือนว่าไม่มีวิธีใดเลย มีเอกสารอย่างเป็นทางการเกี่ยวกับเรื่องนี้หรือไม่
มะเขือเทศ

ความสูงวิวพอร์ตที่ได้รับจากการโหลดเป็นอย่างไร
Giridhar Karnik

@GiridharKarnik คุณได้ลองทำwindow.innerWidthข้างในngOnInitหรือngAfterViewInitวิธีการแล้วหรือยัง?
จอห์น

@ Tomato การอ้างอิง API สามารถพบได้ที่นี่การอ้างอิงจำนวนมากถูกสร้างอัตโนมัติ (ฉันเข้าใจ) และไม่มีตัวอย่างหรือคำอธิบายโดยละเอียด การอ้างอิง API บางอย่างมีตัวอย่างมากมาย ฉันไม่สามารถหาตัวอย่างที่ชัดเจนจากเอกสารเกี่ยวกับเรื่องนี้ได้ อาจจะถูกซ่อนอยู่ที่ไหนสักแห่ง: P
John

1
นี่คือข้อมูลอ้างอิงว่ามีการใช้อย่างไร
Anand Rockzz

52

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

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

วิธีเดียวที่ได้ผลสำหรับฉัน ขอบคุณ !! :-) ... ฉันแค่ต้องปรับการนำเข้าของคุณ บางที rxjs ใหม่ของฉันคือ:import { fromEvent, Observable,Subscription } from "rxjs";
Jette

ฉันจะเพิ่ม debounce (1,000) ในนี้ได้ที่ไหน
Deepak Thomas

3
ขออภัยที่ตอบช้า: เพื่อเพิ่ม debounce คุณจะต้องใช้this.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
Chris Stanley

41

วิธีที่ถูกต้องในการทำเช่นนี้คือการใช้คลาส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();
    }
  }
}

สิ่งนี้ไม่ได้เกิดขึ้นบนมือถือเท่าที่ฉันจะบอกได้ คุณจะได้ขนาดหน้าต่างเริ่มต้นด้วยวิธีนี้ได้อย่างไร
user3170530

โทรศัพท์มือถือไม่ได้มีวัตถุเช่นเดียวกับเซิร์ฟเวอร์ไม่ได้window windowฉันไม่คุ้นเคยกับการกำหนดค่ามือถือที่แตกต่างกัน แต่คุณควรจะสามารถปรับเปลี่ยนรหัสข้างต้นได้อย่างง่ายดายเพื่อผูกกับผู้ฟังเหตุการณ์ที่ถูกต้องทั่วโลก
cgatian

@ cgatian ฉันเป็นคนโง่ แต่นี่ดูเหมือนจะเป็นคำตอบที่ถูกต้อง โชคไม่ดีที่ฉันไม่มีโชคทำให้การสมัครสมาชิกของฉันเข้าสู่ระบบ คุณสามารถเพิ่มคำตอบวิธีการสมัครสมาชิกนี้ในส่วนประกอบเพื่อให้สามารถเห็นการปรับปรุงได้หรือไม่
Matthew Harwood

@catat ฉันจะทำเสียงแตก ๆ แต่นี่ดูเหมือนจะไม่ทำงาน ดูเหมือนว่าตัวกรองในบริการแปลกประหลาดstackoverflow.com/q/46397531/1191635
Matthew Harwood

3
@cgatian Mobile does not have a window object.... ทำไมคุณคิดว่าเบราว์เซอร์มือถือไม่มีวัตถุในหน้าต่าง
Drenai

31

นี่เป็นวิธีที่ดีกว่าที่จะทำ ตามคำตอบของ 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สร้างขึ้นภายในบริการทุกที่ที่คุณต้องการรับเหตุการณ์การปรับขนาดหน้าต่าง

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

}

ความเข้าใจอย่างถ่องแท้ในการเขียนโปรแกรมแบบตอบโต้มักจะช่วยในการเอาชนะปัญหาที่ยากลำบาก หวังว่านี่จะช่วยใครซักคน


ฉันเชื่อว่านี่เป็นข้อผิดพลาด: this.height $ = (windowSize $ .pluck ('height') เป็น Observable <number>) .distinctUntilChanged (); Observable <number>) .distinctUntilChanged (); ดูเหมือนว่าคุณวางใน differUntilChanged () สองครั้งติดต่อกัน
Zuriel

ฉันไม่ได้รับคุณบรรจงโปรด
Giridhar Karnik

การตรวจจับการเปลี่ยนแปลงจะไม่ถูกไล่ออกเมื่อคุณกำลังทำกิจกรรมนี้นอก Angular เชื่อว่านั่นคือสิ่งที่เขาหมายถึง
Mark Pieszak - Trilon.io

1
ฉันพบข้อผิดพลาดว่า 'ถอนออก' ไม่มีอยู่ในประเภท BehaviorSubject การเปลี่ยนรหัสเป็น this.height $ = windowSize $ .map (x => x.height) ใช้งานได้สำหรับฉัน
Matt Sugden

@GiridharKamik คุณสามารถให้บริการโซลูชั่นที่จะสมัครสมาชิกในความกว้างและความสูงในเวลาเดียวกันซึ่งเราสามารถสมัครสมาชิกทั้งความกว้างความสูงใน 1 สมัครง่าย
ghiscoding

11

ผมยังไม่เห็นใครพูดถึงเรื่องของMediaMatcherangular/cdk

คุณสามารถกำหนด MediaQuery และแนบลิสเทนเนอร์ได้จากนั้นที่ใดก็ได้บนเทมเพลตของคุณ (หรือทีเอส) คุณสามารถเรียกใช้สิ่งต่าง ๆ ได้หาก Matcher ตรงกัน 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;
}

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


6

สมมติว่า <600px หมายถึงอุปกรณ์เคลื่อนที่สำหรับคุณคุณสามารถใช้สิ่งที่สังเกตได้และสมัครใช้งาน:

ก่อนอื่นเราต้องมีขนาดของหน้าต่างปัจจุบัน ดังนั้นเราจึงสร้างสิ่งที่สังเกตได้ซึ่งปล่อยค่าเดียวนั่นคือขนาดของหน้าต่างปัจจุบัน

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

จากนั้นเราจำเป็นต้องสร้างสิ่งที่สังเกตได้อื่นเพื่อให้เราทราบว่าขนาดของหน้าต่างเปลี่ยนไปเมื่อใด สำหรับสิ่งนี้เราสามารถใช้โอเปอเรเตอร์ "fromEvent" หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับผู้ให้บริการ rxjs กรุณาเยี่ยมชม: rxjs

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

ผสานทั้งสองสตรีมเพื่อรับการสังเกตของเรา:

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

ตอนนี้คุณสามารถสมัครสมาชิกแบบนี้:

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

จำไว้ว่าให้ยกเลิกการสมัคร :)


5

มีบริการViewportRulerใน CDK เชิงมุม มันทำงานนอกเขตและทำงานกับการแสดงผลฝั่งเซิร์ฟเวอร์ด้วย


2
นี่ควรเป็นคำตอบที่ยอมรับได้ มีทุกสิ่งที่จำเป็น + นี่คือฟังก์ชั่น CDK เชิงมุม
Deniss M.

3

จากการแก้ปัญหาของ @cgatian ฉันขอแนะนำการทำให้เข้าใจง่ายดังต่อไปนี้:

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

การใช้งาน:

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

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

คุณสามารถเพิ่มฟังก์ชั่นให้กับบริการและทริกเกอร์มันใน component @jcdsr: getScreenSize () {this.onResize $ .emit ({width: window.innerWidth, height: window.innerHeight}); }
DanielWaw

3

นี่ไม่ใช่คำตอบที่ถูกต้องสำหรับคำถาม แต่สามารถช่วยคนที่ต้องการตรวจจับการเปลี่ยนแปลงขนาดขององค์ประกอบใด ๆ

ฉันสร้างห้องสมุดที่เพิ่มresizedเหตุการณ์ในองค์ประกอบใด ๆ - Angular Resize Eventเชิงมุมปรับขนาดเหตุการณ์

มันใช้ภายในResizeSensorจากแบบสอบถามองค์ประกอบ CSSธาตุแบบสอบถาม

ตัวอย่างการใช้งาน

HTML

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

สิ่งที่พิมพ์ด้วยพิมพ์ดีด

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

  onResized(event: ResizedEvent): void {
    this.width = event.newWidth;
    this.height = event.newHeight;
  }
}

ถ้าคุณต้องการตรวจจับหน้าต่างปรับขนาดแล้ว Angular Material Breakpoint Observer จะจัดการกับmaterial.angular.io/cdk/layout/overview
Darren Street

แต่นี่ไม่ได้ตรวจสอบเฉพาะการปรับขนาดหน้าต่าง แต่องค์ประกอบใด ๆ ที่ปรับขนาด
Martin Volek

2

ฉันเขียนlib นี้เพื่อค้นหาเมื่อการเปลี่ยนแปลงขนาดขอบเขตขององค์ประกอบ (ปรับขนาด) ในเชิงมุมสิ่งนี้อาจช่วยผู้อื่นได้ คุณอาจวางลงบนองค์ประกอบรูทจะทำเช่นเดียวกับการปรับขนาดหน้าต่าง

ขั้นตอนที่ 1:นำเข้าโมดูล

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

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

ขั้นตอนที่ 2:เพิ่มคำสั่งด้านล่าง

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

ขั้นตอนที่ 3:รับรายละเอียดขนาดขอบเขต

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

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

1

ใน Angular2 (2.1.0) ฉันใช้ ngZone เพื่อจับภาพเหตุการณ์การเปลี่ยนแปลงหน้าจอ

ลองดูตัวอย่าง:

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

ฉันหวังว่าความช่วยเหลือนี้!


1

โค้ดด้านล่างช่วยให้สังเกตการเปลี่ยนแปลงขนาดใด ๆ สำหรับ div ที่ให้ใน Angular

<div #observed-div>
</div>

จากนั้นในส่วนประกอบ:

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

ฉันต้องการเปลี่ยน varibale จาก counter = 6 เป็น count = 0 ที่ความกว้างสูงสุด 767px ฉันจะทำอย่างไร
Thanveer Shah

0

โซลูชันทั้งหมดไม่ทำงานกับฝั่งเซิร์ฟเวอร์หรือเชิงมุมทั่วไป


0

ฉันตรวจสอบคำตอบส่วนใหญ่แล้ว จึงตัดสินใจที่จะตรวจสอบเอกสารเชิงมุมบนเค้าโครง

Angular มีผู้สังเกตการณ์ของตัวเองสำหรับตรวจจับขนาดที่แตกต่างกันและนำไปใช้กับส่วนประกอบหรือบริการได้ง่าย

ตัวอย่างง่าย ๆ คือ:

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

หวังว่ามันจะช่วย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.