Why is it that a parent angular component with multiple child component instances only has the first instance passing data to the child component?

huangapple go评论62阅读模式
英文:

Why is it that a parent angular component with multiple child component instances only has the first instance passing data to the child component?

问题

It seems like you're facing an issue where the data is not being passed correctly to your custom select components in your Ionic 7 project. You've tried a few approaches, but the issue persists.

One possible solution to ensure that the data is passed correctly to each select component instance and that the emitted value is assigned to the correct instance is to use template references (#) along with ngFor to create dynamic instances of your custom component. Here's how you can modify your home.page.html:

<ion-content>
  <ng-container *ngFor="let data of [districts, parishes, villages]; let i = index">
    <app-select
      [title]="i === 0 ? 'Select District' : (i === 1 ? 'Select Parish' : 'Select Village')"
      [data]="data"
      itemTextField="text"
      [multiple]="false"
      (click)="selectComponents[i].open(data)"
      (selectedChanged)="selectChanged($event, i)"
      #selectComponent
    ></app-select>
  </ng-container>
</ion-content>

Now, in your home.page.ts, you need to keep track of the instances of your custom select components and handle the selected changes correctly:

import { Component, OnInit, ViewChildren, QueryList } from '@angular/core';
import { IonModal } from '@ionic/angular';
import { SelectComponent } from 'path-to-select-component'; // Update with the actual path

@Component({
  selector: 'app-add-exporter',
  templateUrl: './add-exporter.page.html',
  styleUrls: ['./add-exporter.page.scss'],
})
export class AddExporterPage implements OnInit {
  @ViewChildren('selectComponent') selectComponents!: QueryList<SelectComponent>;

  // ... Your other code ...

  selectChanged(event: any, index: number) {
    // Handle the selected change here based on the index of the component.
    if (index === 0) {
      this.selectedDistrict = event[0].text;
    } else if (index === 1) {
      this.selectedParish = event[0].text;
    } else if (index === 2) {
      this.selectedVillage = event[0].text;
    }
  }

  ngOnInit() {
    // ...
  }

  // ... Your other code ...
}

With this approach, you create dynamic instances of your custom select component, and each one will correctly handle its data and emitted values. Make sure to update the path to your custom SelectComponent in the import statement.

英文:

I'm working on an ionic 7 project where I have a page containing three instances of a custom component, and each of these instances passes a different array dataset to the child component. The component is meant to display the received array data in a list and this works perfectly. But when I click on each of the component instances, the data displayed in the child component is always the data passed by the first instance.

I tried passing the data to the child component through the individual instances on the same page.
Below is the html page for the parent component (home.page.html).

&lt;ion-content&gt;
        &lt;app-select
          title=&quot;Select District&quot;
          [data]=&quot;districts&quot;
          itemTextField=&quot;text&quot;
          [multiple]=false
          (click)=&quot;select.open()&quot;
          (selectedChanged)=&quot;districtChanged($event)&quot;
        #select&gt;&lt;/app-select&gt;

        &lt;app-select
          title=&quot;Select Parish&quot;
          [data]=&quot;parishes&quot;
          itemTextField=&quot;text&quot;
          [multiple]=false
          (click)=&quot;select.open()&quot;
          (selectedChanged)=&quot;parishChanged($event)&quot;
        #select&gt;&lt;/app-select&gt;

        &lt;app-select
          title=&quot;Select Village&quot;
          [data]=&quot;villages&quot;
          itemTextField=&quot;text&quot;
          [multiple]=false
          (click)=&quot;select.open()&quot;
          (selectedChanged)=&quot;villageChanged($event)&quot;
        #select&gt;&lt;/app-select&gt;
&lt;/ion-content&gt;

This is the home.page.ts file

import { Component, OnInit, ViewChild } from &#39;@angular/core&#39;;
import { IonDatetime, IonModal } from &#39;@ionic/angular&#39;;

export interface Item {
  text: string;
  value: string;
}

@Component({
  selector: &#39;app-add-exporter&#39;,
  templateUrl: &#39;./add-exporter.page.html&#39;,
  styleUrls: [&#39;./add-exporter.page.scss&#39;],
})
export class AddExporterPage implements OnInit {

  @ViewChild(&#39;modal&#39;, { static: true }) modal!: IonModal;

  public selectedDistrict: String = &#39;&#39;;
  public selectedParish: String = &#39;&#39;;
  public selectedVillage: String = &#39;&#39;;

  public district: any;
  public districts: Item[] = [
    { text: &#39;District 1&#39;, value: &#39;District 1&#39; },
    { text: &#39;District 2&#39;, value: &#39;District 2&#39; },
    { text: &#39;District 3&#39;, value: &#39;District 3&#39; },
    { text: &#39;District 4&#39;, value: &#39;District 4&#39; },
    { text: &#39;District 5&#39;, value: &#39;District 5&#39; },
    { text: &#39;District 6&#39;, value: &#39;District 6&#39; },
    { text: &#39;District 7&#39;, value: &#39;District 7&#39; },
    { text: &#39;District 8&#39;, value: &#39;District 8&#39; },
    { text: &#39;District 9&#39;, value: &#39;District 9&#39; }
  ];

  public parish: any;
  public parishes: Item[] = [
    { text: &#39;Parish 1&#39;, value: &#39;Parish 1&#39; },
    { text: &#39;Parish 2&#39;, value: &#39;Parish 2&#39; },
    { text: &#39;Parish 3&#39;, value: &#39;Parish 3&#39; },
    { text: &#39;Parish 4&#39;, value: &#39;Parish 4&#39; },
    { text: &#39;Parish 5&#39;, value: &#39;Parish 5&#39; },
    { text: &#39;Parish 6&#39;, value: &#39;Parish 6&#39; },
    { text: &#39;Parish 7&#39;, value: &#39;Parish 7&#39; },
    { text: &#39;Parish 8&#39;, value: &#39;Parish 8&#39; },
    { text: &#39;Parish 9&#39;, value: &#39;Parish 9&#39; }
  ];

  public village: any;
  public villages: Item[] = [
    { text: &#39;Village 1&#39;, value: &#39;Village 1&#39; },
    { text: &#39;Village 2&#39;, value: &#39;Village 2&#39; },
    { text: &#39;Village 3&#39;, value: &#39;Village 3&#39; },
    { text: &#39;Village 4&#39;, value: &#39;Village 4&#39; },
    { text: &#39;Village 5&#39;, value: &#39;Village 5&#39; },
    { text: &#39;Village 6&#39;, value: &#39;Village 6&#39; },
    { text: &#39;Village 7&#39;, value: &#39;Village 7&#39; },
    { text: &#39;Village 8&#39;, value: &#39;Village 8&#39; },
    { text: &#39;Village 9&#39;, value: &#39;Village 9&#39; }
  ];

  constructor() {
    
  }

  ngOnInit() {
    
  }

  districtChanged(event: any){
    this.selectedDistrict = event[0].text;
  }

  parishChanged(event: any){
    this.selectedParish = event[0].text;
  }

  villageChanged(event: any){
    this.selectedVillage = event[0].text;
  }

}

Select.component.html

&lt;div *ngIf=&quot;selected.length; else placeholder&quot;&gt;
  &lt;span *ngFor=&quot;let item of selected; let last = last&quot;&gt;
    {{ leaf(item) }}{{ last ? &#39;&#39; : &#39;, &#39;}}
  &lt;/span&gt;
&lt;/div&gt;
&lt;ng-template #placeholder&gt;Select&lt;/ng-template&gt;

&lt;ion-modal [isOpen]=&quot;isOpen&quot; (willDismiss)=&quot;cancel()&quot;&gt;
  &lt;ng-template&gt;
    &lt;ion-header class=&quot;ion-no-border&quot;&gt;
      &lt;ion-toolbar color=&quot;primary&quot;&gt;
        &lt;ion-title&gt;{{ title }}&lt;/ion-title&gt;
      &lt;/ion-toolbar&gt;
    &lt;/ion-header&gt;

    &lt;ion-content class=&quot;ion-padding-horizontal&quot;&gt;
      &lt;ion-item lines=&quot;full&quot; *ngFor=&quot;let item of filtered&quot;&gt;
        &lt;ion-checkbox
          slot=&quot;start&quot;
          labelPlacement=&quot;end&quot;
          justify=&quot;start&quot;
          [(ngModel)]=&quot;item.selected&quot;
          (ionChange)=&quot;itemSelected()&quot;
        &gt;
          {{ leaf(item) }}
        &lt;/ion-checkbox&gt;
      &lt;/ion-item&gt;
    &lt;/ion-content&gt;

  &lt;/ng-template&gt;
&lt;/ion-modal&gt;

And this is the code in the custom component file (select.component.ts).

import { CommonModule } from &#39;@angular/common&#39;;
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges} from &#39;@angular/core&#39;;
import { IonicModule, SearchbarCustomEvent } from &#39;@ionic/angular&#39;;

@Component({
  standalone: true,
  imports: [IonicModule, CommonModule, FormsModule],
  selector: &#39;app-select&#39;,
  templateUrl: &#39;./select.component.html&#39;,
  styleUrls: [&#39;./select.component.scss&#39;],
})
export class SelectComponent  implements OnChanges {

  @Input() title = &#39;Search&#39;;
  @Input() data: any = [];
  @Input() multiple: boolean = false;
  @Input() itemTextField = &#39;name&#39;;
  @Output() selectedChanged: EventEmitter&lt;any&gt; = new EventEmitter();
  
  isOpen: boolean = false;
  selected: any[] = [];
  filtered: any[] = [];

  constructor() { }

  ngOnChanges(changes: SimpleChanges): void {
    this.filtered = this.data;
  }

  open(){
    this.isOpen = true;
    this.filtered = this.data;
  }

  cancel(){
    this.isOpen = false;
  }

  select(){
    this.isOpen = false;
  }

  leaf(obj: any){
    return this.itemTextField.split(&#39;.&#39;).reduce((val, el) =&gt; val[el], obj);
  }

  itemSelected(){
    if(!this.multiple){
      if(this.selected.length){
        this.selected[0].selected = false;
      }
      this.selected = this.data.filter((item: any) =&gt; item.selected);
      this.selectedChanged.emit(this.selected);
      this.isOpen = false;
    }
  }

}

I expected each select component instance to pass it's data to the custom component so that it's displayed within the component. But what actually happened is, when I click on each of the component instances, the data displayed in the child component is always the data passed by the first instance (list of districts), and definitely the value emitted on select is displayed on the first instance, regardless of which instance was clicked. Even when I click the parishes or villages component, I still get districts displayed.

So, I tried a different approach by passing the array data through the select.open() function in home.page.ts

&lt;ion-content&gt;
        &lt;app-select
          title=&quot;Select District&quot;
          [data]=&quot;districts&quot;
          itemTextField=&quot;text&quot;
          [multiple]=false
          (click)=&quot;select.open(districts)&quot;
          (selectedChanged)=&quot;districtChanged($event)&quot;
        #select&gt;&lt;/app-select&gt;

        &lt;app-select
          title=&quot;Select Parish&quot;
          [data]=&quot;parishes&quot;
          itemTextField=&quot;text&quot;
          [multiple]=false
          (click)=&quot;select.open(parishes)&quot;
          (selectedChanged)=&quot;parishChanged($event)&quot;
        #select&gt;&lt;/app-select&gt;

        &lt;app-select
          title=&quot;Select Village&quot;
          [data]=&quot;villages&quot;
          itemTextField=&quot;text&quot;
          [multiple]=false
          (click)=&quot;select.open(villages)&quot;
          (selectedChanged)=&quot;villageChanged($event)&quot;
        #select&gt;&lt;/app-select&gt;
&lt;/ion-content&gt;

and edited the open() function in select.component.ts as shown below

  open(data: any[]){
    this.data = data;
    this.isOpen = true;
    this.filtered = this.data;
  }

The data was passed and displayed well for the individual instances, but the problem was that the result is always emitted to the first instance and the value was assigned to the first instance.

I also tried this answer, but still emits the value to the first select input for districts.

答案1

得分: 1

这可能是由于在home.page.html中声明的重复#select模板变量引起的,根据Angular文档,模板变量应该只声明一次。

英文:

This might be caused by the duplicate #select template variables declared in home.page.html, template variables should only be declared once according to the Angular documentation.

huangapple
  • 本文由 发表于 2023年5月26日 01:30:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76334910.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定