Açısal pencere yeniden boyutlandırma olayı


232

Bazı görevleri pencere yeniden boyut olay (yük ve dinamik olarak) dayalı gerçekleştirmek istiyorum.

Şu anda benim DOM var aşağıdaki gibi:

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

Olay doğru şekilde tetikleniyor

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

Bu olay nesnesinden Genişlik ve Yüksekliği nasıl alabilirim?

Teşekkürler.


6
Gerçekten Açısal bir soru değil. bakmak pencere nesnesi. Onu innerHeightve innerWidthözelliklerini alabilirsiniz ..
Sasxa

1
@Sasxa doğru, yapmanız gerekenconsole.log(event.target.innerWidth )
Pankaj Parkar

Bilgi için teşekkürler Sasxa / Pankaj - Bunun sadece düz bir javascript meselesi mi yoksa daktilo meselesi mi yoksa Açısal bir olay meselesi mi olduğundan emin değildim. Burada kendim için çok dik bir öğrenme eğrisine tırmanıyorum ve girdilerinizi takdir ediyorum.
DanAbdn

Yanıtlar:


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

veya HostListener dekoratörünü kullanarak :

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

Desteklenen küresel hedefler window, documentve body.

Https://github.com/angular/angular/issues/13248 Angular'da uygulanıncaya kadar , performansın DOM etkinliklerine zorunlu olarak abone olması ve diğer yanıtların bazılarında gösterildiği gibi etkinliklerin miktarını azaltmak için RXJS'yi kullanmak daha iyidir.


15
Kullandığınız sözdizimi hakkında herhangi bir belge var mı: window: resize ?
Clement

3
Kesinlikle. Sen kullanabilirsiniz document, windowve body github.com/angular/angular/blob/...
Günter Zochbauer

5
mükemmel cevap .. inanıyorum @HostListener temiz yolu :) ama HostListener almak emin olun ilk kullanarakimport { Component, OnInit, HostListener } from '@angular/core';
Gaurav Sharma

4
Hızlı ipucu: İlk yüklemede de tetiklemek istiyorsanız, @ açısal / çekirdekten ngAfterViewInit uygulayın. angular.io/api/core/AfterViewInit
Zymotik

7
Ancak, HostListener'ın debounceTime ile nasıl çalıştığını bilmek isteyenler için, aşağıdaki bağlantıyı takip edin plnkr.co/edit/3J0dcDaLTJBxkzo8Akyg?p=preview
jcdsr

65

@ Günter'in cevabı doğrudur. Sadece başka bir yöntem önermek istedim.

Ayrıca @Component()-decorator içine host-bağlayıcı ekleyebilirsiniz . Olayı ve istenen işlev çağrısını şu şekilde host-metadata-özelliğine koyabilirsiniz:

@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
Bu sayfadaki her yöntemi denedim ve hiçbiri çalışmıyor gibi görünüyor. Bununla ilgili resmi bir belge var mı?
Domates

yükte görüntü yüksekliği nasıl elde edilir?
Giridhar Karnik

@GiridharKarnik, window.innerWidthiçeride ngOnInitveya ngAfterViewInityöntemlerde çalışmayı denediniz mi?
John

@Tomato API referansı burada bulunabilir Referansların çoğu otomatik olarak üretilir (sanırım) ve örneklerin eksikliği veya ayrıntılı bir açıklaması vardır. Bazı API referanslarının birçok örneği vardır. Bununla ilgili dokümanlardan somut bir örnek bulamadım. Belki bir yerde gizlidir: P
John

1
İşte nasıl kullanıldığı bir referans
Anand Rockzz

52

Bunun uzun zaman önce sorulduğunu biliyorum, ama şimdi bunu yapmanın daha iyi bir yolu var! Bu yanıtı görüp görmeyeceğinden emin değilim. Açıkçası ithalatınız:

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

Sonra bileşeninizde:

resizeObservable$: Observable<Event>
resizeSubscription$: Subscription

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

Sonra imha aboneliği iptal ettiğinizden emin olun!

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

Benim için işe yarayan tek yol. Teşekkürler!! :-) ... Sadece içe aktarmayı ayarlamak zorunda kaldım. Belki de benim rxjs daha yeni:import { fromEvent, Observable,Subscription } from "rxjs";
Jette

Buna nereden debounce (1000) ekleyebilirim?
Deepak Thomas

3
Geç cevap verdiğim için üzgünüm: debounce eklemek için kullanmak isteyeceksinizthis.resizeSubscription$ = this.resizeObservable$.pipe(debounceTime(1000)).subscribe( evt => { console.log('event: ', evt) })
Chris Stanley

41

Bunu yapmanın doğru yolu , olayı bağlamak için EventManager sınıfını kullanmaktır . Bu, kodunuzun Angular Universal ile sunucu tarafı oluşturma gibi alternatif platformlarda çalışmasını sağlar.

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

Bir bileşendeki kullanımı, bu hizmeti app.module'unuza bir sağlayıcı olarak eklemek ve daha sonra bir bileşenin yapıcısına içe aktarmak kadar basittir.

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

Bu, mobil cihazlarda söyleyebildiğim kadarıyla hiç ateş etmiyor. Bu yaklaşımla ilk pencere boyutunu nasıl elde edersiniz?
user3170530

Mobil bir windownesneye sahip değildir , tıpkı bir sunucunun sahip olmadığı gibi window. Farklı mobil yapılandırmalara aşina değilim, ancak doğru küresel olay dinleyicisine bağlanmak için yukarıdaki kodu kolayca uyarlayabilmelisiniz
cgatian

@cgatian Ben bir çaylağım ama bu doğru cevap gibi görünüyor. Ne yazık ki aboneliğimi yapmakta hiç şansım yok Giriş yap bileşen. Güncelleştirmeleri görebilmeniz için bu bileşene nasıl abone olunacağını ekleyebilir misiniz?
Matthew Harwood

@cgatian Bir dalgıç yapacağım ama bu işe yaramadı. Hizmetteki filtre tuhaf görünüyor tho stackoverflow.com/q/46397531/1191635
Matthew Harwood

3
@cgatian Mobile does not have a window object.... sizce neden mobil tarayıcıların pencere nesnesi yok?
Drenai

31

İşte bunu yapmanın daha iyi bir yolu. Birowsky'nin cevabına dayanarak .

Adım 1: angular serviceRxJS Gözlemlenebilir ile bir oluşturun .

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

Adım 2: Yukarıdakileri enjekte edin ve pencere yeniden boyutlandırma olayını almak istediğiniz her yerde hizmet içinde oluşturulan serviceherhangi birine abone Observablesolun.

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

}

Reaktif Programlamanın sağlam bir şekilde anlaşılması zor sorunların üstesinden gelmede her zaman yardımcı olacaktır. Umarım bu birine yardımcı olur.


Bunun bir hata olduğuna inanıyorum: this.height $ = (Gözlenebilir <sayı> olarak windowSize $ .pluck ('height')) .distinctUntilChanged (); Gözlemlenebilir <sayı>) .distinctUntilChanged (); iki kez üst üste
differenttUntilChanged

Seni anlamadım, lütfen eloborat.
Giridhar Karnik

Bu olayı Angular dışında yaptığınız için değişiklik algılaması tetiklenmeyecek, bunun ne anlama geldiğine inanıyoruz.
Mark Pieszak - Trilon.io

1
'Pluck' türü BehaviorSubject üzerinde yok diyerek bir hata yaşadım. Kodu this.height $ = windowSize $ .map (x => x.height) olarak değiştirmek benim için çalıştı.
Matt Sugden

@GiridharKamik Can sen 1 basit genişlik ve yükseklik aynı zamanda, biz genişlik hem abone olabilir, yükseklik üzerinde abone verecek bir çözüm sağlamak abone
ghiscoding

11

Ben yaklaşık kimse konuşurken görmedim MediaMatcherait angular/cdk.

Bir MediaQuery tanımlayabilir ve ona bir dinleyici ekleyebilirsiniz - o zaman şablonunuzda (veya ts) herhangi bir yerde Eşleştirici eşleşirse bir şeyler çağırabilirsiniz. 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;
}

kaynak: https://material.angular.io/components/sidenav/overview


6

<600 piksel’in sizin için mobil anlamına geldiğini varsayarsak, bu gözlemlenebilir öğeyi kullanabilir ve abone olabilirsiniz:

İlk önce mevcut pencere boyutuna ihtiyacımız var. Bu nedenle, yalnızca tek bir değer yayan bir gözlemlenebilir oluştururuz: geçerli pencere boyutu.

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

Sonra başka bir gözlemlenebilir yaratmamız gerekiyor, böylece pencere boyutunun ne zaman değiştirildiğini biliyoruz. Bunun için "fromEvent" operatörünü kullanabiliriz. Rxjs`nin operatörleri hakkında daha fazla bilgi edinmek için lütfen şu adresi ziyaret edin: rxjs

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

Gözlemlenebilirimizi almak için bu iki akışı birleştirin:

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

Şimdi buna şu şekilde abone olabilirsiniz:

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

Aboneliği iptal etmeyi unutmayın :)


5

Açısal CDK'da bir ViewportRuler hizmeti var. Bölgenin dışında çalışır ve sunucu tarafı oluşturma ile de çalışır.


2
kabul edilen cevap bu olmalıdır. Gereken her şeye sahiptir + bu, açısal CDK dahili işlevselliğidir.
Deniss M.

3

@Cgatian'ın çözümüne dayanarak aşağıdaki basitleştirmeyi öneririm:

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

Kullanımı:

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

Bunu en iyi çözüm olarak buldum, ancak bu sadece pencere yeniden boyutlandırıldığında çalışır, ancak yüklendiğinde veya yönlendirici değiştiğinde çalışmaz. Yönlendirici değişikliği, yeniden yükleme veya yükleme ile nasıl başvurulacağını biliyor musunuz?
jcdsr

Hizmete bir işlev ekleyebilir ve bunu @jcdsr: getScreenSize () {this.onResize $ .emit ({width: window.innerWidth, height: window.innerHeight}) içinde tetikleyebilirsiniz. }
DanielWaw

3

Bu soru için tam olarak cevap değildir, ancak herhangi bir elemandaki boyut değişikliklerini tespit etmesi gereken birine yardımcı olabilir.

Açısal Yeniden Boyutlandırma Olayıresized - herhangi bir öğeye olay ekleyen bir kütüphane oluşturduk .

Dahili ResizeSensorolarak CSS Eleman Sorguları'ndan kullanır .

Örnek kullanım

HTML

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

daktilo ile yazılmış yazı

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

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

pencere boyutlarını algılamak istiyorsanız Açısal Malzeme Kesme Noktası Gözlemcisi zaten material.angular.io/cdk/layout/overview
Darren Street

Ancak bu yalnızca pencerelerin yeniden boyutlandırılmasını değil, herhangi bir öğenin yeniden boyutlandırılmasını algılar.
Martin Volek

2

Bu lib'i yazdım bileşen sınır boyutu değişikliği (yeniden boyutlandırma) bulmak için , bu diğer insanlara yardımcı olabilir. Kök bileşenine koyabilirsiniz, pencere yeniden boyutlandırma ile aynı şeyi yapar.

Adım 1: Modülü içe aktarın

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

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

Adım 2: Aşağıdaki gibi yönergeyi ekleyin

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

3. Adım: Sınır boyutu ayrıntılarını alın

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

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

1

Angular2'de (2.1.0) Ekran değiştirme olayını yakalamak için ngZone kullanıyorum.

Örneğe bir göz atın:

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

Umarım bu yardım!


1

Aşağıdaki kod, Açısal'da herhangi bir div için herhangi bir boyut değişikliğini gözlemlemenizi sağlar.

<div #observed-div>
</div>

sonra Bileşende:

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

Ben max = genişlik 767px bir sayaç = 6 sayaç = 0 bir değişken değiştirmek istiyorum, Bunu nasıl yaparım?
Thanveer Shah

0

tüm çözümler sunucu tarafında veya açısal evrenselde çalışmıyor


0

Bu cevapların çoğunu kontrol ettim. daha sonra Layout'taki Açısal belgeleri kontrol etmeye karar verdi .

Angular, farklı boyutları tespit etmek için kendi Gözlemcisine sahiptir ve bileşene veya bir Hizmete uygulanması kolaydır.

basit bir örnek:

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

Umarım yardımcı olur

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.