插入具有 known/declared 个组件的动态 Angular 4.x 内容

Insert dynamic Angular 4.x content with known/declared components

我有一个(我认为是相当普遍的)问题,我找不到用当前 Angular 4.x 体系结构解决的好方法。也许还有一种方法我还没有找到,但我搜索得相当广泛了。

我想将一些动态的、用户生成的 HTML 内容插入到 Angular 应用程序中。 HTML 内容我还包括一些已知的(包含在模块声明中)Angular 应该呈现的组件。下面是一些 puesdo-app HTML 可能会帮助我解释:

<app-component>

    <!-- standard angular header and router-outlet -->
    <app-header>
        <app-nav>
            <ul>
                <li routerLink="/page-one">First Page</li>
                <li routerLink="/page-two">Second Page</li>
                <li routerLink="/page-three">Third Page</li>
            </ul>
        </app-nav>
    </app-header>
    <router-outlet></router-outlet>

    <!--
        the dynamic content would be inserted into the <main> element as HTML with angular <app-... tags
        the content in the HTML content inserted main tag could contain anything
        the angular components within the content should be initalized as normal
    -->
    <main>
        <h1>Title</h1>
        <app-component>
            <p>this component and its content should be initalized when HTML is inserted into main</p>
        </app-component>
        <app-component>
            <app-sub-component>
                angular components here might also contain other angular components
            </app-sub-component>
        </app-component>
        <p>more regular HTML content</p>
    </main>

    <!-- more traditional Angular components could come after -->
    <app-footer></app-footer>

</app-component>

我已经尝试了两种实现此目的的方法,但都没有真正起作用。

1。在 JIT 编译器中使用动态 Template/Component 模式。

TLDR; 它不适用于 AOT。 AOT 是全有或全无。

我使用了像 这样的动态组件模式。这实现起来非常简单。使用 JIT 编译器,从 HTML 内容中创建一个新的组件和模块,并插入新组件的一个实例。只要项目未编译 AOT,此方法就有效。由于 Angular 4.x JitCompilerFactory 不能包含在 AOT 编译构建中。允许 JIT 编译器进入 AOT 编译构建似乎是有意义的,这样已知组件可以获得性能提升,并将 JIT 编译器仅用于动态组件。这将是两全其美的。但是,我不知道 Angular 的内部工作原理,我认为有充分的理由不允许包含 JIT 编译器。

2。使用 Dynamic Component Loader Pattern 模式

TLDR; 无法获取加载组件的入口点。

在 AOT 构建中使用 JIT 编译器出现问题后,我认为我可以使用 ComponentFactoryResolver。由于我仅使用已在模块中声明的组件,因此我可以在 HTML 内容中应有的位置创建一个组件。这个想法有点疯狂,但我认为它可能会奏效。我的方法是这样的:

  1. 插入动态HTML内容
    • 获取动态内容为 HTML
    • 不要使用 DomSanatizer.bypassSecurityTrustHtml()
    • 删除 Angular 组件
    • 并将其插入 DOM
  2. 解析插入的 HTML 组件
    • 使用 {<app-selector-string> : ComponentClass} json table
    • 找到所有组件作为 DOM 节点并将它们匹配到它们对应的 ComponentClass
  3. 在我想插入组件的地方创建一个视图ViewContainerRef(这部分是不可能的)
  4. 使用 ViewContainerRef.createComponent(),创建适当组件的新实例
    • 插入它代替旧元素
    • 给出组件projectableNodes,即旧html组件的内容
  5. 使用对它的引用为新组件提供输入。

这不起作用,因为在步骤 3 中动态创建 ViewContainerRef 是不可能的。(在步骤 3 之后也可能无法执行任何操作。IDK .我没有做到那么远)

有什么办法吗?我是不是想多了?

谢谢!

这个很有可能,其实...

Angular 的 ApplicationRef 有一个 attachView() 方法可以跟踪新的组件引用。然后可以像常规组件一样将该组件引用插入任何位置。

这是一个伪代码示例:

// create a componentRef
const componentFactory = ComponentFactoryResolver.resolveComponentFactory(AngularComponent);
const componentRef = componentFactory.create(Injector);

// attach the componentRef to the angular ApplicationRef
ApplicationRef.attachView(componentRef.hostView);

// insert the root element of the new componentRef into any DOM node
const componentRoot: HTMLElement = (componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0];
const node = document.getElementById('insertionPoint'); 
Renderer2.insertBefore(node.parentNode, componentRoot, node);

// don't forget to destroy the component when you're done with it
ApplicationRef.detachView(componentRef.hostView);
componentRef.destroy();

更好的解释here