【问题标题】:nativeElement.getBoundingClientRect() always returns 0 in Angular testsnativeElement.getBoundingClientRect() 在 Angular 测试中总是返回 0
【发布时间】:2018-07-31 08:45:35
【问题描述】:

我有一个 Angular 组件 (5.2),它相对于另一个元素的 nativeElement.getBoundingClientRect() 输出定位自身。

在测试中,我创建了一个带有样式的包装器组件来模拟定位元素,但返回的ClientRect 始终全为零。

有没有办法让 Angular实际上在 DOM 中定位我的元素?

为了说明,这是我的TestComponent。在内部,<popup> 组件将使用anchor.nativeElement.getBoundingClientRect() 计算它的固定位置。

我认为这无关紧要,但我正在使用 Jest 执行测试。

我试过 withwithout async().

@Component({
    selector: 'test',
    styles: [`
        :host {
            width: 1000px;
            height: 1000px;
            display: flex;
            justify-content: center;
            align-content: center;
        }
    `],
    template: `
        <button #button></button>
        <popup [anchor]="button">
            <div id="helloWorld">hello world</div>
        </popup>
    `
})
class TestComponent {
    @ViewChild('button')
    public buttonElement: ElementRef;
}

describe('test', () => {
    let fixture;
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [TestComponent],
            imports: [PopupModule]
        }).compileComponents().then(() => {
            fixture = TestBed.createComponent(TestComponent);
            fixture.detectChanges();
        });
    }));
    it('should be positioned', () => {
        const button = fixture.componentInstance.buttonElement.nativeElement;

        // Either of these always fail, because the button is not positioned.
        // I have also tried using fixed positioning of the button.

        // this will fail
        expect(button.getBoundingClientRect().top).not.toEqual(0);

        // this will fail, too
        return fixture.whenRenderingDone().then(() => {
            expect(button.getBoundingClientRect().top).not.toEqual(0);
        });
    });
});

【问题讨论】:

  • 我想这是一个时间问题。尝试在beforeEach 中创建组件并用async 包围。
  • 我更新了这个例子来说明async()的用法
  • buttonElement.nativeElement 存在吗?如果是这样,您可以尝试访问button.offsetHeight(只需访问它,无需对值执行任何操作),看看它是否会强制将按钮定位在页面中。
  • @JeffFairley 你解决了这个问题吗?我也有
  • 没有。我相信这是一个 JSDOM 问题。可能是故意的,因为 IDK 计算 DOM 大小会带来什么成本。

标签: angular typescript jestjs


【解决方案1】:

长话短说:

您只需要在 ngAfterViewInit() 钩子中使用 ElementRef 类型的 nativeElement ,您的测试将“看到” DOM 中的元素。

长读:

.getBoundingClientRect() 仅在元素已渲染并存在于 DOM 中时才有效。今天,当我尝试访问被点击的元素时,我遇到了这种情况,[routerLink] 它是由路由器操作动态重新渲染的——我无法直观地识别出这种行为。它会导致该元素消失,并且无法进行任何测量。接下来,渲染了一些类似的元素,但在 ClientRect 中为 0,0,0,0。

因此,如果您使用某些元素的动态重新渲染(例如菜单或其他内容),您的 element.getBoundingClientRect() 将仅在元素消失、重新渲染或“动作时间”时返回 0,0,0,0 " (点击等)在 DOM 中尚未出现,就像您的情况一样。

所以你需要等待它会被重新渲染,接下来 - 你可以通过.getBoundingClientRect() 获得它的测量值。对于@ViewChild() 最好的等待方式是ngAfterViewInit() 钩子,您可以在其中访问nativeElement(作为最佳方式)。此外 - 还有一些技巧,例如:

private elementInited$ = new Subject();
public desiredElement: ElementRef;
@ViewChild('someElemRef') set setElementRef(el: ElementRef) {
  if (!!el) {
    this.desiredElement= el;
    this.elementInited$.next();
  }
}

ngOnInit() {
  this.elementInited$.subscribe((inited) => {
  // do stuff with nativeElement
  const values = this.desiredElement.nativeElement.getBoundingClientRect() // <== works!
  })
}

但它太棘手了:)

另外,等待元素的好方法是@Directive()。 使用指令,您可以正确地以“角度方式”侦听元素,并且仅当元素存在于 DOM 中时指令才会触发。

在 Devtools 中使用 Chrome 的渲染亮点也可能非常有帮助 - 只需打开 devtools 菜单 - More tools &gt; rendering &gt; Paint flashing 复选框有助于识别哪些元素重新渲染。

【讨论】:

    【解决方案2】:

    对我来说,只有当我在我的 viewChild 元素上使用此方法时,一切才开始起作用,我声明如下:

    @ViewChild('progress', { static: true }) progress; 
    

    之后可以使用getBoundingClientRect方法。

    this.progress.nativeElement.getBoundingClientRect()
    

    PS:当我使用 ElementRef 时,它对我不起作用。只有使用 ViewChild 才可以。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-27
      • 1970-01-01
      • 2019-02-27
      • 2013-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多