Angular Ivy,一个文本节点如何指向它的 ng-template

Angular Ivy, how a text node points to its ng-template

我需要在测试中发现 tpl1 的文本节点在呈现后属于 ng-template

1
<ng-template tpl>
  tpl1
</ng-template>
2

如果在渲染后找到 tpl1 的 debugNode,那么它的父节点指向 component 的节点,但是 我想确保 tpl1 的 debugNode =13=] 派生自 ng-template.

到目前为止,我能够经典模式下做到这一点,但不能ivy.

中找到一种方法来做到这一点

下面有一个最小示例,任何解决方案(不仅是 elDef 和 tNode)都适用于我。

import {Component, DebugNode, Directive, TemplateRef, VERSION, ViewChild, ViewContainerRef} from '@angular/core';
import {TestBed} from "@angular/core/testing";

@Directive({
  selector: '[tpl]',
})
class TplDirective {
  public constructor(public readonly tpl: TemplateRef<any>) {}
}

@Component({
  selector: 'target',
  template: `
    1
    <ng-template tpl>
      tpl1
    </ng-template>
    2
  `,
})
class TargetComponent {
  @ViewChild(TplDirective, {
    read: TemplateRef,
  }) public readonly tpl?: TemplateRef<any>;
  @ViewChild(TplDirective, {
    read: ViewContainerRef,
  }) public readonly vcr?: ViewContainerRef;
}

describe('issue-289', () => {
  beforeEach(() => TestBed.configureTestingModule({
    declarations: [TplDirective, TargetComponent],
  }).compileComponents());

  fit('finds right parent in ivy', () => {
    const fixture = TestBed.createComponent(TargetComponent);
    fixture.detectChanges();
    const componentEl = fixture.debugElement;
    const component: TargetComponent = componentEl.componentInstance;

    // check that only defaults have been rendered
    expect (componentEl.childNodes.length).toEqual(3);
    const [txtEl1, tplEl, txtEl2] = componentEl.childNodes;
    expect(txtEl1.nativeNode.nodeName).toEqual('#text');
    expect(tplEl.nativeNode.nodeName).toEqual('#comment');
    expect(txtEl2.nativeNode.nodeName).toEqual('#text');

    // rendering the template
    component.vcr.createEmbeddedView(component.tpl);
    fixture.detectChanges();

    // looking for the new element
    expect (componentEl.childNodes.length).toEqual(4);
    const txtTplEl = componentEl.childNodes.find(el => el !== txtEl1 && el !== tplEl && el !== txtEl2);
    expect(txtTplEl.nativeNode.nodeName).toEqual('#text');

    // getting internal node, 1st is classic, 2nd is ivy
    const tplElNode = (tplEl.injector as any).elDef || (tplEl.injector as any)._tNode;
    expect(tplElNode).toBeDefined();

    // FIXME find how txtTplEl points to tplEl
    // in classic (not ivy) it works like that
    const txtTplNode = (txtTplEl as any)._debugContext?.view?.parentNodeDef;

    // should succeed
    expect(txtTplNode).toEqual(tplElNode);
  });
});

这样检查怎么样

const view = component.vcr.createEmbeddedView(component.tpl);
       /\
       ||
   assign to variable

expect(view.rootNodes.includes(txtTplEl.nativeNode)).toBeTruthy();

它不涉及私有 API 并且应该在两个版本中都有效。

Forked Stackblitz