Wykrywanie zmian rozmiaru okna w czasie rzeczywistym w Angular 4

118

Próbowałem zbudować responsywny pasek nawigacyjny i nie chcę używać zapytania o media, więc zamierzam użyć *ngIFjako kryterium rozmiaru okna. Ale napotkałem problem, ponieważ nie mogę znaleźć żadnej metody ani dokumentacji dotyczącej wykrywania rozmiaru okna Angular 4. Próbowałem również metody JavaScript, ale nie jest ona obsługiwana.

Próbowałem również następujących rzeczy :

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

... który był używany w jonowym.

I screen.availHeight, ale nadal bez sukcesu.

Ronit Oommen
źródło
1
O co platformchodzi? Czy chodzi o Ionic?
Günter Zöchbauer
@ Günter Zöchbauer Platforma jest kanciasta, chciałem tylko wspomnieć o kodach, które wypróbowałem.
Ronit Oommen
1
Platformjest usługą jonową. Więc zgaduję, że to projekt Ionic?
robbannn
@BlackBeard Nie wydaje mi się, żeby to był duplikat ze względu na różnice między Angular 2 i 4.
tehlivi

Odpowiedzi:

261

Aby uzyskać to na init

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

Jeśli chcesz, aby był aktualizowany przy zmianie rozmiaru:

@HostListener('window:resize', ['$event'])
onResize(event) {
  this.innerWidth = window.innerWidth;
}
Eduardo Vargas
źródło
2
this.innerWidth = event.target.innerWidth; ... jest prawdopodobnie bardziej wydajna, daje ten sam wynik
danday74
2
Zalecamy również to w konstruktorze, jeśli używasz lodash ... this.onResize = debounce (this.onResize, 150, {lead: false, trailing: true}) ... aby zapobiec zbyt częstemu wywoływaniu metody onResize. . będziesz musiał ... zaimportować {debounce} z „lodash”
danday74
Myślę, że wolałem użyć ngAfterViewInitmetody haka na życie zamiast metody na ngOnInit życie
ahmed hamdy
40

Jeśli chcesz zareagować na określone punkty przerwania (np. Zrobić coś, jeśli szerokość jest mniejsza niż 768px), możesz również użyć BreakpointObserver:

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

{ ... }

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

lub nawet posłuchaj zmian w tym punkcie przerwania:

breakpointObserver.observe([
  '(max-width: 768px)'
    ]).subscribe(result => {
      if (result.matches) {
        doSomething();
      } else {
        // if necessary:
        doSomethingElse();
      }
    });
Jeremy Benks
źródło
jeśli chcesz działać tylko wtedy, gdy obserwator spełnia kryteria, które musisz dodać wewnątrz, zasubskrybuj to w if (result.matches)przeciwnym razie zadzwoni, nawet jeśli nie
karoluS
1
@karoluS: Tak, oczywiście. Zredagowałem odpowiedź, żeby to wyjaśnić. Dziękuję Ci.
Jeremy Benks
Wygląda na to, że cdkma to zależność równorzędną od określonej wersji kątowej. Jak 7, jeśli chodzi o najnowsze. Zresztą mogę tego użyć do starszej wersji (kątowej 4 jak w pytaniu)?
LeOn - Han Li
@Leon li: Właściwie używam Angular 7, zgadza się. Jednak: O ile wiem, możesz użyć cdk również w Angular 4. Zgodnie z tym wpisem na blogu potrzebujesz cdk 2.x. O ile mi wiadomo, trzeba to zainstalować ręcznie iz wersją określoną w ten sposób: npm @ angular / cdk @ 4
Jeremy Benks
@JeremyBenks Y, używamy w 4.2.6szczególności ng . Nie można znaleźć wersji cdk 2.x, tylko 4.1.x i 4.3.x. W każdym razie, dzięki!
LeOn - Han Li
9

Jeśli chcesz, aby komponenty były łatwe do testowania, powinieneś opakować globalny obiekt okna w usługę Angular:

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

@Injectable()
export class WindowService {

  get windowRef() {
    return window;
  }

}

Następnie możesz wstrzyknąć go jak każdą inną usługę:

constructor(
    private windowService: WindowService
) { }

I konsumuj ...

  ngOnInit() {
      const width= this.windowService.windowRef.innerWidth;
  }
Kildareflare
źródło
2
Nie widzę sensu wykonywania usługi, aby zrobić dokładnie to, co jest już pod ręką. window.innerWidth.
Rodrigo
1
Po pierwsze, DI jest metodą Angular i sprawia, że ​​zależności komponentu / dyrektywy są wyraźniejsze. Ale głównym punktem, o którym tutaj wspomniano, jest ułatwienie testowania kodu korzystającego z usługi. Korzystając z tego podejścia, WindowService może być mockowany w dowolnym teście napisanym dla komponentów korzystających z usługi.
Kildareflare,
9

W dokumentacji dla Platform width()i height()stwierdzono, że te metody używają odpowiednio window.innerWidthi window.innerHeight. Jednak preferowane jest używanie tych metod, ponieważ wymiary są wartościami zapisanymi w pamięci podręcznej, co zmniejsza ryzyko wielokrotnych i kosztownych odczytów 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();
    });
}
robbannn
źródło
Jak się widthz windowdotyczyć DOM? Logicznie rzecz biorąc, nie powinno zależeć od tego, jak wygląda drzewo dom.
Shahryar Saljoughi
7

To jest przykład usługi, z której korzystam.

Możesz uzyskać szerokość ekranu, subskrybując screenWidth$lub przezscreenWidth$.value .

To samo dotyczy mediaBreakpoint$(lub 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');
    }
  }

}

Mam nadzieję, że to komuś pomoże

Denis
źródło
Moja ulubiona odpowiedź! Po prostu upewnij się, że dodałeś this.init () w konstruktorze, aby działał.
Ben
3
@HostListener("window:resize", [])
public onResize() {
  this.detectScreenSize();
}

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

private detectScreenSize() {
    const height = window.innerHeight;
    const width = window.innerWidth;
}
Dhruv Parekh
źródło
3
Zawsze wyjaśniaj, dlaczego może to być właściwe rozwiązanie
thedp
3

Odpowiedź jest bardzo prosta. napisz poniższy kod

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();
}
}
Harish Mahajan
źródło
1

W tym scenariuszu można użyć metody pobierającej tekst. Lubię to

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

I użyj tego w szablonie w ten sposób:

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

Nie będziesz potrzebować żadnego programu obsługi zdarzeń, aby sprawdzić zmianę rozmiaru / okna, ta metoda sprawdzi rozmiar za każdym razem automatycznie.

Rohan Gayen
źródło