【问题标题】:Detecting real time window size changes in Angular 4检测Angular 4中的实时窗口大小变化
【发布时间】:2018-01-03 04:17:39
【问题描述】:

我一直在尝试构建一个响应式导航栏并且不希望使用媒体查询,所以我打算使用*ngIf 和窗口大小作为标准。 但是我一直面临一个问题,因为我找不到任何关于 Angular 4 窗口大小检测的方法或文档。 JavaScript方法我也试过了,不支持。

我也试过following:

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

...在离子中使用。

还有screen.availHeight,但还是没有成功。

【问题讨论】:

标签: html angular dom-events


【解决方案1】:

你可以用这个 https://github.com/ManuCutillas/ng2-responsive 希望它有所帮助:-)

【讨论】:

    【解决方案2】:

    在初始化时获取它

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

    如果你想在调整大小时保持更新:

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

    【讨论】:

    • this.innerWidth = event.target.innerWidth; ... 可能更有效,产生相同的结果
    • 如果您使用 lodash,也建议在构造函数中使用这个 ... this.onResize = debounce(this.onResize, 150, {leading: false, trailing: true}) ... 以防止onResize 方法被调用太频繁了......你需要...... import { debounce } from 'lodash'
    • 我认为更喜欢使用ngAfterViewInit生命挂钩方法而不是ngOnInit生命挂钩方法
    • @HostListener 代码可以包含在一个角度组件中(例如,在构造函数下。可以跳过 ['$event'] 和 event 参数,尽管也可以在该参数中找到 innerWidth,这将使函数更可测试。为了使代码更干,你可以在 ngOnInit 或 ngAfterViewInit 中调用 onResize,但你必须处理事件参数。
    • 你好,第二个代码sn-p在哪里添加?
    【解决方案3】:

    Platformwidth()height() 的文档中指出,这些方法分别使用window.innerWidthwindow.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();
        });
    }
    

    【讨论】:

    • 获得windowwidthDOM 有什么关系?从逻辑上讲,它不应该取决于 dom 树的样子。
    【解决方案4】:

    如果您希望组件保持易于测试,您应该将全局窗口对象包装在 Angular 服务中:

    import { Injectable } from '@angular/core';
    
    @Injectable()
    export class WindowService {
    
      get windowRef() {
        return window;
      }
    
    }
    

    然后您可以像任何其他服务一样注入它:

    constructor(
        private windowService: WindowService
    ) { }
    

    然后消费……

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

    【讨论】:

    • 我看不出做一项服务来完全做手头上的事情有什么意义。 window.innerWidth.
    • 首先,DI 是 Angular 的方式,它使组件/指令的依赖关系更加清晰。但这里提到的要点是使使用该服务的代码更易于测试。使用这种方法,可以在为使用该服务的组件编写的任何测试中模拟 WindowService。
    【解决方案5】:

    如果您想对某些断点做出反应(例如,如果宽度为 768 像素或更小,则执行某些操作),您可以使用BreakpointObserver

    import { Component } from '@angular/core';
    import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.scss']
    })
    export class AppComponent {
    
      constructor(
        private breakpointObserver: BreakpointObserver,
      ) {
        // detect screen size changes
        this.breakpointObserver.observe([
          "(max-width: 768px)"
        ]).subscribe((result: BreakpointState) => {
          if (result.matches) {
              // hide stuff        
          } else {
              // show stuff
          }
        });
      }
    }
    

    【讨论】:

    • 如果您只想在观察者符合您必须在其中添加的条件时采取行动,请订阅此if (result.matches),否则即使不匹配也会调用
    • @karoluS:当然可以。我编辑了我的答案以明确这一点。谢谢。
    • 似乎这个cdk 对特定版本的角度有对等依赖。就像最新的 7 一样。无论如何我可以将它用于旧版本(问题中的角度 4)?
    • @Leon li:我实际上使用 angular 7,没错。但是:据我所知,您也可以在 Angular 4 中使用 cdk 。根据this blog post,您需要 cdk 2.x。据我所知,这必须手动安装,并使用如下指定的版本:npm @angular/cdk@4
    • @JeremyBenks Y,我们专门使用 ng 4.2.6。找不到 cdk 2.x 版本,只有 4.1.x 和 4.3.x。无论如何,谢谢!
    【解决方案6】:
    @HostListener("window:resize", [])
    public onResize() {
      this.detectScreenSize();
    }
    
    public ngAfterViewInit() {
        this.detectScreenSize();
    }
    
    private detectScreenSize() {
        const height = window.innerHeight;
        const width = window.innerWidth;
    }
    

    【讨论】:

    • 总是解释为什么这可能是正确的解决方案
    【解决方案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() {
        this.init();
      }
    
      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() 即可。
    【解决方案8】:

    答案很简单。编写以下代码

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

    【讨论】:

      【解决方案9】:

      对于这种情况,您可以使用 typescript getter 方法。像这样

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

      并像这样在模板中使用它:

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

      您不需要任何事件处理程序来检查窗口的大小调整/,此方法每次都会自动检查大小。

      【讨论】:

        【解决方案10】:

        现在我知道问题最初是指屏幕尺寸,所以基本上是widthheight 属性,但对于大多数人来说Breakpoints 才是真正重要的,因此,为了制定一个全局可重用的解决方案,我更愿意使用AngularBreakpointObserver 来处理这个问题。

        以下配置基本上是service 和一些functions,可以returnObservable&lt;BreakpointState&gt; 并在需要时成为subscribed

        import { Injectable } from '@angular/core';
        import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';
        import { Observable } from 'rxjs';
        
        @Injectable({
          providedIn: 'root',
        })
        export class ScreenService {
        
          constructor(private observer: BreakpointObserver) {}
        
          isBelowSm(): Observable<BreakpointState> {
            return this.observer.observe(['(max-width: 575px)']);
          }
        
          isBelowMd(): Observable<BreakpointState> {
            return this.observer.observe(['(max-width: 767px)']);
          }
        
          isBelowLg(): Observable<BreakpointState> {
            return this.observer.observe(['(max-width: 991px)']);
          }
        
          isBelowXl(): Observable<BreakpointState> {
            return this.observer.observe(['(max-width: 1199px)']);
          }
        }
        

        上面的代码可以调整以处理bootstrap方式的屏幕大小(通过将max-width更改为min-width并为每个值添加1px,当然还有反转functions名称.)

        现在在component class 中,只需将subscribing 转换为上述任何函数返回的observable 即可。

        即:app.component.ts

        export class AppComponent implements AfterViewInit {
        
          isBelowLg: boolean;
        
          constructor(private screenService: ScreenService) {}
        
          ngAfterViewInit(): void {
            this.screenService.isBelowLg().subscribe((isBelowLg: BreakpointState) => {
              this.isBelowLg = isBelowLg.matches;
            });
          }
        }
        

        请参阅 AfterViewInit 生命周期钩子在视图初始化后使用 detectChanges() 会省去很多麻烦。

        编辑:

        作为AfterViewInit 的替代方案,它是相同的,但另外,您需要使用ChangeDetectorRefdetectChanges(),只需在订阅组件中注入一个实例即:app.component.ts像这样:

        constructor(
            private screenService: ScreenService,
            private cdref: ChangeDetectorRef
          ) {}
        

        之后,只需拨打detectChanges() 即可:

        this.cdref.detectChanges();
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-01-18
          • 2023-04-05
          • 2018-05-14
          • 2012-05-29
          • 1970-01-01
          • 2020-03-15
          • 2019-05-15
          • 1970-01-01
          相关资源
          最近更新 更多