簡體   English   中英

*ngFor 內的 angular 內容投影是否共享相同的數據? 如何從父組件訪問子組件的項目內容?

[英]does angular content projection inside *ngFor share same data? How to access project content of child component from parent?

目前我試圖在一個子組件中投影第三個組件,該子組件被投影在ngFor循環內(在子組件內),但是每當我使用查詢列表的索引(ViewChildren('#thirdComponent')更改或設置投影內容中的某些屬性時在父組件中)在父母中,所有孩子的投影內容都顯示相同的變化。 有沒有正確的方法來做到這一點。

是否是由於在子組件中的內容投影位置重復了 select 屬性綁定。孩子的投影是在一次打開一個或多個面板的手風琴內完成的。

@Component({
  selector: "my-app",
  template: `
    <child-comp #child>
      <ng-container selected>
        <some-other-comp #someOtherComp></some-other-comp>
      </ng-container>
    </child-comp>
  `,
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements AfterViewInit {
  h = 0;
  i = 1;
  j = 2;
  k = 3;
  @ViewChildren("someOtherComp") otherCompList: QueryList<SomeOtherComponent>;

  ngAfterViewInit(): void {
    this.otherCompList.toArray()[this.h].prop = this.h;
    // below will result in undefined due to QueryList of size 1 
    // this.otherCompList.toArray()[this.i].prop = this.i;
    // this.otherCompList.toArray()[this.j].prop = this.j;
    // this.otherCompList.toArray()[this.k].prop = this.k;
  }
}

@Component({
  selector: "child-comp",
  template: `
    <div *ngFor="let value of [1, 2, 3]; let i = index">
      <!-- if ngIf is removed than only the last projection is diplayed -->
      <div *ngIf="i === 0">
        <ng-content select="[selected]"> </ng-content>
      </div>
    </div>
  `,
  styleUrls: ["./app.component.css"]
})
export class ChildComponent {}

@Component({
  selector: "some-other-comp",
  template: `
    <p>{{ prop }}</p>
  `,
  styleUrls: ["./app.component.css"]
})
export class SomeOtherComponent {
  prop: any;
}

堆棧閃電戰

利用 *ngTemplateOutlet 和 let-variables

我們可以將模板傳遞給我們的子組件,並利用@Input()裝飾器和*ngTemplateOutlet直接訪問父組件中 HTML 模板的屬性。

例子

首先,我在父組件中定義了一個數組,我想將其用作外部子組件中循環的基礎。

父組件

@Component({
  selector: 'parent',
  templateUrl: 'parent.component.html',
  styleUrls: ['parent.component.scss']
})
export class ParentComponent implements OnInit {

  dataItems: { title: string, description: string }[] = [{
    title: 'First Element',
    description: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nihil!'
  }...] // remaining items truncated for brevity.

  constructor() {
  }

  ngOnInit(): void {
  }

}

這個父組件然后有一個子組件,它接受整個項目列表的輸入

<child [items]="dataItems"></child>

子組件(第一級)

@Component({
  selector: 'child',
  templateUrl: 'child.component.html',
  styleUrls: ['child.component.scss']
})

export class ChildComponent implements OnInit {
  @Input() items!: any[];
  
  constructor() {
  }

  ngOnInit(): void {
  }
}
<ng-container *ngFor="let childItem of items">
  <projected [item]="childItem">
    <ng-template let-item>
      <h4>{{item.title}}</h4>
      <p>{{item.description}}</p>
    </ng-template>
  </projected>
</ng-container>

投影組件(子子組件)

@Component({
  selector: 'projected',
  templateUrl: 'projected.component.html',
  styleUrls: ['projected.component.scss']
})

export class ProjectedComponent implements OnInit {
  @Input() item: any;
  @ContentChild(TemplateRef) templateOutlet!: TemplateRef<any>
  constructor() {
  }

  ngOnInit(): void {
  }

}
<ng-container *ngTemplateOutlet="templateOutlet; context: {$implicit: item}"></ng-container>
<ng-content></ng-content>

它是如何工作的

在這種關系中,父組件並不是絕對必要的,因為我們沒有將內容直接從父組件投影到ProjectedComponent中,我只是選擇在這里定義一個項目列表以保持與您的問題類似的層次結構。

子組件
子組件做了兩件事:

  • 定義一個 *ngFor 循環來循環遍歷一些元素集合。
  • 定義如何在ProjectedComponent的模板中使用這些元素的模板。

ProjectedComponent中,我們使用@ContentChild裝飾器到 select 我們希望通過<ng-content>給出的 TemplateRef

然后使用*ngTemplateOutlet將這個模板放入容器中,這也允許我們為局部變量創建數據綁定上下文。

context: {$implicit: item}告訴 Angular 在模板上定義的任何let-*變量,沒有任何顯式綁定都應該綁定到我們組件中的 item 屬性。

因此,我們可以在父組件級別的模板中引用此屬性。

編輯

從技術上講,如果您想直接在子組件內部定義模板,則不需要上下文綁定,因為您可以直接引用*ngFor模板,但是如果您想將模板提升到ParentComponent級別,則它變得很有必要使解決方案更具可重用性。

您是正確的錯誤的原因(僅更改最后一個元素)是因為在渲染時您有多個元素具有相同的select值。

一種可能的解決方案是使用模板引用將所需的子組件從頂層傳遞到您希望它被投影的位置。

這是一個有效的StackBlitz

 import { AfterViewInit, Component, Input, QueryList, ViewChildren } from "@angular/core"; @Component({ selector: "my-app", template: ` <child-comp #child [templateRef]="templateRef"> </child-comp> <ng-template #templateRef> <some-other-comp #someOtherComp></some-other-comp> </ng-template> `, styleUrls: ["./app.component.css"] }) export class AppComponent implements AfterViewInit { h = 0; i = 1; j = 2; k = 3; @ViewChildren("someOtherComp") otherCompList: QueryList<SomeOtherComponent>; ngAfterViewInit(): void { this.otherCompList.toArray()[this.h].prop = this.h; this.otherCompList.toArray()[this.i].prop = this.i; this.otherCompList.toArray()[this.j].prop = this.j; this.otherCompList.toArray()[this.k].prop = this.k; } } @Component({ selector: "child-comp", template: ` <div *ngFor="let value of [1, 2, 3, 4]; let i = index"> <,-- if ngIf is removed than only the last projection is diplayed --> <ng-container *ngTemplateOutlet="templateRef"></ng-container> </div> `: styleUrls. ["./app.component;css"] }) export class ChildComponent { @Input() templateRef: } @Component({ selector, "some-other-comp": template, ` <p>{{ prop }}</p> `: styleUrls. ["./app.component:css"] }) export class SomeOtherComponent { prop; any; }

暫無
暫無

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

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