如何将变量从父组件中声明的ng-template传递给子组件/指令?

亚历杭德罗·塞拉诺(Alejandro Serrano)

所以我想知道是否有一种方法可以传递ng模板并生成其所有内容以包括插值中使用的变量?

另外,我对angular还是很陌生,所以除了删除html元素之外,我还需要担心是否要删除其他内容吗?

在这末尾,将有一个指向stackblitz.com仓库的链接,该仓库将具有下面显示的所有代码。

以下是实现我的指令的src / app / app.component.html代码:

<hello name="{{ name }}"></hello>
<p>
  Start editing to see some magic happen :)
</p>
<!-- popup/popup.directive.ts contains the code i used in button tag -->
<button PopupDir="" body="this is a hardcoded message that is passed to popup box"> simple 
</button>

<ng-template #Complicated="">
  <div style="background-color: red;">
    a little more complicated but simple and still doable
  </div>
</ng-template>
<button PopupDir="" [body]="Complicated">
  complicated
</button>

<ng-template #EvenMoreComplicated="">
  <!-- name and data isn't being passed i need help here--> 
  <div style="background-color: green; min-height: 100px; min-width:100px;">
    {{name}} {{data}}
  </div>
</ng-template>
<button PopupDir="" [body]="EvenMoreComplicated">
  more complicated
</button>

以下是我的src / app / popup / popup.directive.ts

import { Directive, Input, TemplateRef, ViewContainerRef, HostListener } from '@angular/core'

@Directive({
  selector: 'PopupDir, [PopupDir]'
})
export class Popup {
  @Input() body: string | TemplateRef<any>;
  viewContainer: ViewContainerRef;
  popupElement: HTMLElement;

  //i dont know if i need this
  constructor (viewContainer: ViewContainerRef) {
    this.viewContainer = viewContainer;
  }

  //adds onlick rule to parent tag
  @HostListener('click')
  onclick () {
    this.openPopup();
  }

  openPopup() {
    //Pcreate pupup html programatically
    this.popupElement = this.createPopup();

    //insert it in the dom
    const lastChild = document.body.lastElementChild;
    lastChild.insertAdjacentElement('afterend', this.popupElement);
  }

  createPopup(): HTMLElement {
    const popup = document.createElement('div');
    popup.classList.add('popupbox');

    //if you click anywhere on popup it will close/remove itself
    popup.addEventListener('click', (e: Event) => this.removePopup());
    //if statement to determine what type of "body" it is
    if (typeof this.body === 'string')
    {
      popup.innerText = this.body;
    } else if (typeof this.body === 'object')
    {
      const appendElementToPopup = (element: any) => popup.appendChild(element);
      //this is where i get stuck on how to include the context and then display the context/data that is passed by interpolation in ng-template
      this.body.createEmbeddedView(this.viewContainer._view.context).rootNodes.forEach(appendElementToPopup);
    }
    return popup;
  }

  removePopup() {
    this.popupElement.remove();
  }
}

这是显示我的问题的仓库的链接:https : //stackblitz.com/edit/popupproblem

yurzui

首先让我们考虑一下如何将上下文传递给嵌入式视图。你写了:

this.body.createEmbeddedView(this.viewContainer._view.context)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

您的Popup组件托管在AppComponent视图中,因此this.viewContainer._view.context将是AppComponent实例。但是我想让你说:

1)嵌入式视图已经可以访问ng-template定义的模板范围

2)如果我们传递上下文,则应仅通过模板引用变量使用它。

this.body.createEmbeddedView(this.viewContainer._view.context)
             ||
             \/

this.body.createEmbeddedView({
  name = 'Angular';
  data = 'this should be passed too'
})
             ||
             \/

<ng-template #EvenMoreComplicated let-name="name" let-data="data">
    {{name}} {{data}}

因此,在这种情况下,您不需要传递上下文,因为它已经存在。

this.body.createEmbeddedView({})
             ||
             \/
<ng-template #EvenMoreComplicated>
        {{name}} {{data}}

为什么UI不更新?

角度变化检测机制依赖于视图树。

         AppComponent_View
         /                \
ChildComponent_View    EmbeddedView
        |
 SubChildComponent_View

我们看到有两种视图:组件视图和嵌入式视图。TemplateRef(ng-template)表示嵌入式视图。

当Angular要更新UI时,它只是简单地通过该视图两个检查绑定。

现在,让我们提醒一下如何通过低级API创建嵌入式视图:

  • TemplateRef.createEmbeddedView

  • ViewContainerRef.createEmbeddedView

它们之间的主要区别是,前者仅创建EmbeddedView,而后者创建EmbeddedView并将其添加到Angular变化检测树中这样,嵌入式视图就成为变更检测树的一部分,我们可以看到更新的绑定。

现在该看一下您的代码了:

this.body.createEmbeddedView(this.viewContainer._view.context).rootNodes.forEach(appendElementToPopup);

应该清楚,您正在使用第一种方法。这意味着您必须自己进行更改检测:viewRef.detectChanges()手动调用或附加到tree。

简单的解决方案可能是:

const view = this.body.createEmbeddedView({});
view.detectChanges();
view.rootNodes.forEach(appendElementToPopup);

Stackblitz示例

但是它将仅检测一次更改。我们可以detectChanges在每个Popup.ngDoCheck()钩子调用方法,但是Angular本身有一种更简单的方法。

const view = this.viewContainer.createEmbeddedView(this.body);
view.rootNodes.forEach(appendElementToPopup);

我们使用了第二种方法来创建嵌入式视图,以便Angular本身可以自动检查模板。

我对angular还是很陌生,所以除了删除html元素之外,我还需要担心删除其他内容吗?

我认为我们也应该在关闭弹出窗口时销毁嵌入式视图。

removePopup() {
  this.viewContainer.clear();
  ...
}

最终Stackblitz示例

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章