I have an angular component that I use as a tab in a for loop on the html page:
...
<ng-container *ngFor="let tabData of data$ | async;">
<tab-component
id="{{ tabData.id }}"
name="{{ tabData.name }}"
>
</tab-component>
</ng-container>
<child-component [selectedData]="selectedData"></child-component>
And in the.ts file:
public data$: Observable<Data[]>
public selectedData: Data
ngOnInit() {
this.data$ = this.service.getAllData();
}
ngAfterContentInit() {
this.data$.subscribe(items => this.selectedData = items[0])
}
I would like the first tab to always be the selectedData by default when first loading the page (element 0 in the array). Then on click or the right/left arrow keys, dynamically update the value of selectedData passed to the child component. So far, I've tried everything and the value of selectedData in the child component has always been undefined
Please help me, how can I achieve this!
ngOnInIt
itself and do check the value of items
before assigning it - whether you are getting it or not and if you are not able to find the value there, then there must be the issue with the getAllDataService
.<child-component [selectedTab]="selectedTab"></child-component>
Hope this helps!
Where exactly are you using the selectedData
in your template HTML file?
In the snippet you provided there is a selectedTab
used, but no selectedData
anywhere...
<ng-container *ngFor="let tabData of data$ | async;">
<tab-component
id="{{ tabData.id }}"
name="{{ tabData.name }}"
>
</tab-component>
</ng-container>
<child-component [selectedTab]=selectedTab></child-component>
Also, you can follow @Eugene's advice and do:
ngOnInit() {
this.data$ = this.service.getAllData().pipe(
tap((items) => this.selectedData = items[0])
);
}
without using ngAfterContentInit()
and the need to subscribe a second time.
You could use a subject to express the currently selected tab data, then use combineLatest
to create an observable of both sources.
private data$: Observable<Data[]> = this.service.getAllData();
private selectedData$ = new BehaviorSubject<Data>(undefined);
vm$ = combineLatest([this.data$, this.selectedData$]).pipe(
map(([tabData, selected]) => ({
tabData,
selectedTab: selected ?? tabData[0]
})
);
setSelected(data: Data) {
this.selectedData$.next(data);
}
Here we create a single observable that the view can use ( a view model ) using combineLatest
. This observable will emit whenever either of its sources emit.
We set the selectedData$
BehaviorSubject
to emit an initial value of undefined
. Then, inside the map, we set the selectedTab
property to use tabData[0]
when selected is not yet set. So, initially, it will use tabData[0]
, but after setSelected()
gets called, it will use that value.
<ng-container *ngIf="let vm$ | async as vm">
<tab-component *ngFor="let tabData of vm.tabData"
[id] = "tabData.id"
[name] = "tabData.name"
(click) = "setSelected(tabData)">
</tab-component>
<child-component [selectedTab]="vm.selectedTab"></child-component>
</ng-container>
I managed to get it so that the passed value on the child side is no longer undefined with an ngIf, so:
<child-component *ngIf=selectedData [selectedData]="selectedData"></child-component>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.