簡體   English   中英

如何將變量從父組件中聲明的ng-template傳遞給子組件/指令?

[英]How to pass variables from ng-template declared in parent component to a child component/directive?

所以我想知道是否有辦法傳遞ng-template並生成所有內容以包含插值中使用的變量?

另外我還是新角色所以除了刪除html元素之外,我還需要擔心刪除其他內容嗎?

最后會有一個指向stackblitz.com repo的鏈接,它將包含下面顯示的所有代碼。

以下是我的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

首先讓我們思考一下我們如何將上下文傳遞給嵌入式視圖。 你寫了:

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()或附加到樹。

簡單的解決方案是:

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

Stackblitz示例

但它只會檢測一次變化。 我們可以在每個Popup.ngDoCheck()鈎子上調用detectChanges方法,但Angular本身使用的方法更簡單。

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

我們使用第二種創建嵌入視圖的方法,以便Angular自動檢查模板。

我仍然是新角色所以除了刪除html元素之外我還需要擔心刪除其他內容嗎?

我認為在關閉彈出窗口時我們也應該銷毀嵌入式視圖。

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

最終的Stackblitz示例

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM