Issue
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).
<ion-content>
<app-select
title="Select District"
[data]="districts"
itemTextField="text"
[multiple]=false
(click)="select.open()"
(selectedChanged)="districtChanged($event)"
#select></app-select>
<app-select
title="Select Parish"
[data]="parishes"
itemTextField="text"
[multiple]=false
(click)="select.open()"
(selectedChanged)="parishChanged($event)"
#select></app-select>
<app-select
title="Select Village"
[data]="villages"
itemTextField="text"
[multiple]=false
(click)="select.open()"
(selectedChanged)="villageChanged($event)"
#select></app-select>
</ion-content>
This is the home.page.ts file
import { Component, OnInit, ViewChild } from '@angular/core';
import { IonDatetime, IonModal } from '@ionic/angular';
export interface Item {
text: string;
value: string;
}
@Component({
selector: 'app-add-exporter',
templateUrl: './add-exporter.page.html',
styleUrls: ['./add-exporter.page.scss'],
})
export class AddExporterPage implements OnInit {
@ViewChild('modal', { static: true }) modal!: IonModal;
public selectedDistrict: String = '';
public selectedParish: String = '';
public selectedVillage: String = '';
public district: any;
public districts: Item[] = [
{ text: 'District 1', value: 'District 1' },
{ text: 'District 2', value: 'District 2' },
{ text: 'District 3', value: 'District 3' },
{ text: 'District 4', value: 'District 4' },
{ text: 'District 5', value: 'District 5' },
{ text: 'District 6', value: 'District 6' },
{ text: 'District 7', value: 'District 7' },
{ text: 'District 8', value: 'District 8' },
{ text: 'District 9', value: 'District 9' }
];
public parish: any;
public parishes: Item[] = [
{ text: 'Parish 1', value: 'Parish 1' },
{ text: 'Parish 2', value: 'Parish 2' },
{ text: 'Parish 3', value: 'Parish 3' },
{ text: 'Parish 4', value: 'Parish 4' },
{ text: 'Parish 5', value: 'Parish 5' },
{ text: 'Parish 6', value: 'Parish 6' },
{ text: 'Parish 7', value: 'Parish 7' },
{ text: 'Parish 8', value: 'Parish 8' },
{ text: 'Parish 9', value: 'Parish 9' }
];
public village: any;
public villages: Item[] = [
{ text: 'Village 1', value: 'Village 1' },
{ text: 'Village 2', value: 'Village 2' },
{ text: 'Village 3', value: 'Village 3' },
{ text: 'Village 4', value: 'Village 4' },
{ text: 'Village 5', value: 'Village 5' },
{ text: 'Village 6', value: 'Village 6' },
{ text: 'Village 7', value: 'Village 7' },
{ text: 'Village 8', value: 'Village 8' },
{ text: 'Village 9', value: 'Village 9' }
];
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
<div *ngIf="selected.length; else placeholder">
<span *ngFor="let item of selected; let last = last">
{{ leaf(item) }}{{ last ? '' : ', '}}
</span>
</div>
<ng-template #placeholder>Select</ng-template>
<ion-modal [isOpen]="isOpen" (willDismiss)="cancel()">
<ng-template>
<ion-header class="ion-no-border">
<ion-toolbar color="primary">
<ion-title>{{ title }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding-horizontal">
<ion-item lines="full" *ngFor="let item of filtered">
<ion-checkbox
slot="start"
labelPlacement="end"
justify="start"
[(ngModel)]="item.selected"
(ionChange)="itemSelected()"
>
{{ leaf(item) }}
</ion-checkbox>
</ion-item>
</ion-content>
</ng-template>
</ion-modal>
And this is the code in the custom component file (select.component.ts).
import { CommonModule } from '@angular/common';
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, SimpleChanges} from '@angular/core';
import { IonicModule, SearchbarCustomEvent } from '@ionic/angular';
@Component({
standalone: true,
imports: [IonicModule, CommonModule, FormsModule],
selector: 'app-select',
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'],
})
export class SelectComponent implements OnChanges {
@Input() title = 'Search';
@Input() data: any = [];
@Input() multiple: boolean = false;
@Input() itemTextField = 'name';
@Output() selectedChanged: EventEmitter<any> = 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('.').reduce((val, el) => val[el], obj);
}
itemSelected(){
if(!this.multiple){
if(this.selected.length){
this.selected[0].selected = false;
}
this.selected = this.data.filter((item: any) => 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
<ion-content>
<app-select
title="Select District"
[data]="districts"
itemTextField="text"
[multiple]=false
(click)="select.open(districts)"
(selectedChanged)="districtChanged($event)"
#select></app-select>
<app-select
title="Select Parish"
[data]="parishes"
itemTextField="text"
[multiple]=false
(click)="select.open(parishes)"
(selectedChanged)="parishChanged($event)"
#select></app-select>
<app-select
title="Select Village"
[data]="villages"
itemTextField="text"
[multiple]=false
(click)="select.open(villages)"
(selectedChanged)="villageChanged($event)"
#select></app-select>
</ion-content>
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.
Solution
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.
Answered By - kkamman
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.