ตรวจจับการเปลี่ยนแปลงขนาดหน้าต่างแบบเรียลไทม์ใน Angular 4


118

ฉันพยายามสร้างแถบนำทางที่ตอบสนองและไม่ต้องการใช้คิวรีสื่อดังนั้นฉันตั้งใจจะใช้*ngIFขนาดหน้าต่างเป็นเกณฑ์ แต่ฉันประสบปัญหาเนื่องจากไม่พบวิธีการหรือเอกสารใด ๆ เกี่ยวกับการตรวจจับขนาดหน้าต่าง Angular 4 ฉันได้ลองใช้วิธี JavaScript แล้ว แต่ไม่รองรับ

ฉันได้ลองทำสิ่งต่อไปนี้แล้ว :

constructor(platform: Platform) {
    platform.ready().then((readySource) => {
        console.log('Width: ' + platform.width());
        console.log('Height: ' + platform.height());
    });
}

... ซึ่งใช้ในไอออนิก

และscreen.availHeightแต่ก็ยังไม่ประสบความสำเร็จ


1
อะไรคือสิ่งที่platformเกี่ยวกับ? นี่เกี่ยวกับ Ionic หรือเปล่า?
GünterZöchbauer

@ GünterZöchbauerแพลตฟอร์มเป็นเชิงมุมเพียงแค่ต้องการพูดถึงรหัสที่ฉันได้ลอง
Ronit Oommen

1
Platformเป็นบริการไอออนิก ฉันเดาว่ามันเป็นโครงการไอออนิกใช่ไหม
robbannn


@ BlackBeard ฉันไม่คิดว่ามันจะซ้ำกันเนื่องจาก Angular 2 และ 4 แตกต่างกันมากแค่ไหน
tehlivi

คำตอบ:


261

เพื่อรับมันใน init

public innerWidth: any;
ngOnInit() {
    this.innerWidth = window.innerWidth;
}

หากคุณต้องการอัปเดตเกี่ยวกับการปรับขนาด:

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

2
this.innerWidth = event.target.innerWidth; ... อาจมีประสิทธิภาพมากกว่าสร้างผลลัพธ์เดียวกัน
danday74

2
แนะนำสิ่งนี้ในตัวสร้างด้วยหากคุณใช้ lodash ... this.onResize = debounce (this.onResize, 150, {leading: false, trailing: true}) ... เพื่อป้องกันไม่ให้เมธอด onResize ถูกเรียกบ่อยเกินไป .. คุณจะต้อง ... นำเข้า {debounce} จาก "lodash"
danday74

ฉันคิดว่าชอบที่จะใช้ngAfterViewInitวิธีการเกี่ยวกับชีวิตแทน ngOnInit วิธีขอชีวิต
ahmed hamdy

40

หากคุณต้องการตอบสนองในเบรกพอยต์บางจุด (เช่นทำบางอย่างหากความกว้างน้อยกว่า 768px) คุณยังสามารถใช้ BreakpointObserver:

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

{ ... }

const isSmallScreen = breakpointObserver.isMatched('(max-width: 599px)');

หรือแม้แต่ฟังการเปลี่ยนแปลงของเบรกพอยต์นั้น:

breakpointObserver.observe([
  '(max-width: 768px)'
    ]).subscribe(result => {
      if (result.matches) {
        doSomething();
      } else {
        // if necessary:
        doSomethingElse();
      }
    });

หากคุณต้องการดำเนินการก็ต่อเมื่อผู้สังเกตการณ์ตรงกับเกณฑ์ที่คุณต้องเพิ่มในการสมัครสมาชิกสิ่งนี้if (result.matches)มิฉะนั้นจะเรียกแม้ว่าจะไม่ได้
ก็ตาม

1
@karoluS: ใช่แน่นอน ฉันแก้ไขคำตอบของฉันเพื่อให้ชัดเจน ขอบคุณ.
Jeremy Benks

ดูเหมือนว่าcdkจะมีการพึ่งพาเพียร์กับเวอร์ชันเชิงมุมที่เฉพาะเจาะจง ถูกใจ 7 คนล่าสุด อย่างไรก็ตามฉันสามารถใช้สิ่งนี้กับเวอร์ชันเก่ากว่าได้ (เชิงมุม 4 ในคำถาม)?
LeOn - Han Li

@ Leon li: จริงๆแล้วผมใช้ angular 7 ใช่แล้ว อย่างไรก็ตาม: เท่าที่ฉันรู้คุณสามารถใช้ cdk ในเชิงมุม 4 ได้เช่นกัน ตามโพสต์บล็อกนี้คุณต้องใช้ cdk 2.x. เท่าที่ฉันรู้ต้องติดตั้งด้วยตนเองและมีเวอร์ชันที่ระบุไว้เช่นนี้: npm @ angular / cdk @ 4
Jeremy Benks

@JeremyBenks Y เราใช้ ng 4.2.6โดยเฉพาะ ไม่พบเวอร์ชัน cdk 2.x ที่มีเพียง 4.1.x และ 4.3.x ยังไงก็ขอบคุณ!
LeOn - Han Li

9

หากคุณต้องการให้ส่วนประกอบของคุณยังคงทดสอบได้ง่ายคุณควรรวมวัตถุหน้าต่างส่วนกลางไว้ใน Angular Service:

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

@Injectable()
export class WindowService {

  get windowRef() {
    return window;
  }

}

จากนั้นคุณสามารถฉีดได้เหมือนบริการอื่น ๆ :

constructor(
    private windowService: WindowService
) { }

และบริโภค ...

  ngOnInit() {
      const width= this.windowService.windowRef.innerWidth;
  }

2
ฉันมองไม่เห็นประเด็นในการให้บริการเพื่อทำสิ่งที่มีอยู่แล้ว window.innerWidth
Rodrigo

1
ประการแรก DI เป็นวิธีเชิงมุมและทำให้การอ้างอิงของส่วนประกอบ / คำสั่งชัดเจนขึ้น แต่ประเด็นหลักที่กล่าวถึงนี้คือการทำให้โค้ดที่ใช้บริการทดสอบง่ายขึ้น การใช้วิธีนี้ WindowService สามารถจำลองได้ในการทดสอบใด ๆ ที่เขียนขึ้นสำหรับส่วนประกอบที่ใช้บริการ
Kildareflare

9

เอกสารสำหรับPlatform width()และheight()ระบุว่าวิธีการเหล่านี้ใช้window.innerWidthและwindow.innerHeightตามลำดับ แต่การใช้วิธีนี้เป็นที่ต้องการเนื่องจากมิติข้อมูลเป็นค่าแคชซึ่งจะช่วยลดโอกาสในการอ่าน DOM หลายรายการและมีราคาแพง

import { Platform } from 'ionic-angular';

...
private width:number;
private height:number;

constructor(private platform: Platform){
    platform.ready().then(() => {
        this.width = platform.width();
        this.height = platform.height();
    });
}

วิธีการที่ไม่ได้รับwidthของwindowที่เกี่ยวข้องกับDOM? ตามเหตุผลแล้วไม่ควรขึ้นอยู่กับว่าต้นไม้โดมมีลักษณะอย่างไร
Shahryar Saljoughi

7

นี่คือตัวอย่างบริการที่ฉันใช้

คุณจะได้รับความกว้างหน้าจอโดยการสมัครสมาชิกหรือผ่านทางscreenWidth$screenWidth$.value

เช่นเดียวกับmediaBreakpoint$(หรือmediaBreakpoint$.value)

import {
  Injectable,
  OnDestroy,
} from '@angular/core';
import {
  Subject,
  BehaviorSubject,
  fromEvent,
} from 'rxjs';
import {
  takeUntil,
  debounceTime,
} from 'rxjs/operators';

@Injectable()
export class ResponsiveService implements OnDestroy {
  private _unsubscriber$: Subject<any> = new Subject();
  public screenWidth$: BehaviorSubject<number> = new BehaviorSubject(null);
  public mediaBreakpoint$: BehaviorSubject<string> = new BehaviorSubject(null);

  constructor() {}

  init() {
    this._setScreenWidth(window.innerWidth);
    this._setMediaBreakpoint(window.innerWidth);
    fromEvent(window, 'resize')
      .pipe(
        debounceTime(1000),
        takeUntil(this._unsubscriber$)
      ).subscribe((evt: any) => {
        this._setScreenWidth(evt.target.innerWidth);
        this._setMediaBreakpoint(evt.target.innerWidth);
      });
  }

  ngOnDestroy() {
    this._unsubscriber$.next();
    this._unsubscriber$.complete();
  }

  private _setScreenWidth(width: number): void {
    this.screenWidth$.next(width);
  }

  private _setMediaBreakpoint(width: number): void {
    if (width < 576) {
      this.mediaBreakpoint$.next('xs');
    } else if (width >= 576 && width < 768) {
      this.mediaBreakpoint$.next('sm');
    } else if (width >= 768 && width < 992) {
      this.mediaBreakpoint$.next('md');
    } else if (width >= 992 && width < 1200) {
      this.mediaBreakpoint$.next('lg');
    } else if (width >= 1200 && width < 1600) {
      this.mediaBreakpoint$.next('xl');
    } else {
      this.mediaBreakpoint$.next('xxl');
    }
  }

}

หวังว่านี่จะช่วยใครบางคนได้


คำตอบที่ชอบ! ตรวจสอบให้แน่ใจว่าคุณได้เพิ่ม this.init () ในตัวสร้างเพื่อให้มันทำงานได้
เบ็น


3
@HostListener("window:resize", [])
public onResize() {
  this.detectScreenSize();
}

public ngAfterViewInit() {
    this.detectScreenSize();
}

private detectScreenSize() {
    const height = window.innerHeight;
    const width = window.innerWidth;
}

3
อธิบายเสมอว่าเหตุใดจึงอาจเป็นทางออกที่ถูกต้อง
thedp

3

คำตอบนั้นง่ายมาก เขียนโค้ดด้านล่าง

import { Component, OnInit, OnDestroy, Input } from "@angular/core";
// Import this, and write at the top of your .ts file
import { HostListener } from "@angular/core";

@Component({
 selector: "app-login",
 templateUrl: './login.component.html',
 styleUrls: ['./login.component.css']
})

export class LoginComponent implements OnInit, OnDestroy {
// Declare height and width variables
scrHeight:any;
scrWidth:any;

@HostListener('window:resize', ['$event'])
getScreenSize(event?) {
      this.scrHeight = window.innerHeight;
      this.scrWidth = window.innerWidth;
      console.log(this.scrHeight, this.scrWidth);
}

// Constructor
constructor() {
    this.getScreenSize();
}
}

1

คุณอาจใช้เมธอด typescript getter สำหรับสถานการณ์นี้ แบบนี้

public get width() {
  return window.innerWidth;
}

และใช้ในเทมเพลตดังนี้:

<section [ngClass]="{ 'desktop-view': width >= 768, 'mobile-view': width < 768 
}"></section>

คุณไม่ต้องใช้ตัวจัดการเหตุการณ์ใด ๆ เพื่อตรวจสอบการปรับขนาด / ของหน้าต่างวิธีนี้จะตรวจสอบขนาดทุกครั้งโดยอัตโนมัติ

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