如何使用Angular 2正确检测计算样式属性的变化?

时间:2017-04-27 13:16:18

标签: javascript css angular dom getcomputedstyle

我创建了一个Angular 2应用程序,其中包含一个组件,我试图将其相对于另一个组件的HTML元素定位。为了计算精确的坐标,我需要知道浏览器渲染的自己元素的宽度和高度。

我正在使用window.getComputedStyle(this.elementRef.nativeElement)来获取这些属性。我注意到,随着页面的呈现,属性不断变化,直到页面呈现完成。为了获得有关任何更改的信息并调整定位,我会检查组件的ngAfterViewChecked方法中的值。

然而,当渲染导致新的计算样式属性时,似乎ngAfterViewChecked 被调用,因为我发现我的钩子不再被调用,即使计算出的样式属性仍在变化。我假设Angular框架不是为了检测这种变化而设计的。

我的第一次尝试是实现ngDoCheck钩子,但似乎在一段时间之后并且在计算的样式属性具有其最终值之前不会调用此钩子。我假设我还没有完全理解这个钩子何时被调用。

我终于发现,如果我在setTimeout中实现ngAfterViewChecked函数,这会导致稍后再次调用此相同的钩子,即使我只传递一个虚函数,如:

setTimeout(() => {}, 500);
但是坦率地说,我不明白为什么会这样。 有人可以向我解释setTimeoutngAfterViewChecked挂钩之间的联系吗?

虽然这是一种有点肮脏的解决方法:检测和处理计算样式属性更改的正确'Angular'方法是什么?

代码摘录:

export class MyComponent implements OnInit, AfterViewChecked
{
  private cssWidth: number;
  private cssHeight: number;

  constructor(private elementRef: ElementRef, private renderer: Renderer2)
  {
  }

  ngAfterViewChecked()
  {
    console.log("ngAfterViewChecked");
    this.updateView();
  }

  public updateView()
  {
    const sizeX = parseFloat(window.getComputedStyle(this.elementRef.nativeElement).width) || 0;
    const sizeY = parseFloat(window.getComputedStyle(this.elementRef.nativeElement.height) || 0;

    if (sizeX === this.cssWidth && sizeY === this.cssHeight) {
      // no change
      return;
    }

    console.log("Change detected!");

    // TODO Does not work without this dummy timeout function (else no more changes detected) - why so??
    setTimeout(() => {}, 500);

    [.. doing the positioning here ..]
 }

2 个答案:

答案 0 :(得分:1)

在vanilla JavaScript中,您可以使用:

MutationObserver

并在进行DOM更改时触发您自己的函数。

  

MutationObserver为开发人员提供了一种对变化作出反应的方法   一个DOM。它被设计为替代在中定义的突变事件   DOM3事件规范。

答案 1 :(得分:1)

请参阅顶部图here以查看生命周期图。如果你在doCheck中的钩子不起作用,那么AfterViewChecked也不会起作用,因为doCheck启动了AfterViewChecked。

每次异步过程完成时,都会发生另一轮doCheck。这将调用AfterViewChecked,这将调用您的updateView函数。 SetTimout()是一个异步过程。我怀疑上面的代码会无限循环。

我相信你想要的是让你在这里展示的内容的父组件使用ngAfterViewChecked()并调用updateView()。探讨了该主题here

使两个组件协同工作的Angular方法是拥有一个可以在子组件之间传递更改的父组件,因为父组件是可以查看子组件中发生的事件的上下文。

我个人使用的解决方案是在ngOnInit()函数中未显示的组件中的EventEmitter。它会通知父母它已初始化。然后,在父组件中,对发出的事件进行函数调用,该函数调用告诉子组件您在此处更新View。