在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啊 这是关于离子的吗?
君特Zöchbauer

@GünterZöchbauer该平台是有角度的,只想提及我尝试过的代码。
Ronit Oommen,2017年

1
Platform是离子服务。所以我猜这是一个离子项目?
robbannn


@BlackBeard我不认为这是重复的,因为Angular 2和Angular 2有多少不同。
tehlivi

Answers:


260

在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 '18

2
如果使用lodash,也建议在构造函数中使用此方法... this.onResize = debounce(this.onResize,150,{前导:false,尾随:true})...以防止onResize方法被过于频繁地调用..您需要...从“ lodash”导入{去抖动}
danday74 '18

我认为更喜欢使用ngAfterViewInit救生钩方法而不是 ngOnInit 救生钩方法
艾哈迈德·哈米

39

如果您想对某些断点做出反应(例如,如果宽度小于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)否则即使它不调用,它也会调用
karoluS

1
@karoluS:当然可以。我编辑了我的答案以使其清楚。谢谢。
杰里米·本克斯

似乎这cdk与特定版本的angular有对等关系。喜欢最新的7。无论如何,我可以将其用于较旧的版本(问题中为角度4)?
LeOn-韩立

@Leon li:我实际上使用角度7,是的。但是:据我所知,您也可以在角度4中使用cdk。根据此博客文章,您需要cdk2.x。据我所知,它必须手动安装并使用如下指定的版本:npm @ angular / cdk @ 4
Jeremy Benks

@JeremyBenks是的,我们正在4.2.6专门使用ng 。找不到cdk 2.x版本,只有4.1.x和4.3.x。不管怎样,谢谢!
LeOn-韩立

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。
罗德里戈

1
首先,DI是Angular的方式,它使组件/指令的依赖关系更加清晰。但是这里提到的要点是使使用该服务的代码更易于测试。使用这种方法,可以在针对使用该服务的组件编写的任何测试中模拟WindowService。
Kildareflare

9

对于文档Platform width()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();
    });
}

如何获得widthwindow涉及到DOM?从逻辑上讲,它不应该依赖于dom树的外观。
Shahryar Saljoughi

6

这是我使用的服务示例。

您可以通过订阅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>

您不需要任何事件处理程序即可检查窗口的大小/,此方法将每次自动检查大小。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.