Lazy loading tabs
Goal: Load tabs dynamically
Only load data for a tab when it is selected.
Please replace all occurences of exampleDetailsTab with the actual name in the following code snippets |
1. State & viewmodel
Add additional member for the tabs in your state and viewmodel:
Adapt in Files: <feature>-details.state.ts
...
activeTab: 'ExampleDetailsTab' | ... | 'Other';
...
Adapt in Files: <feature>-search.viewmodel.ts
...
exampleDetailsTab: exampleDetailsTabViewModel;
...
2. Actions
Create following actions to handle the events regarding autocomplete:
Adapt in File: <feature>-details.actions.ts
...
events: {
'other tab selected': emptyProps(),
'example details tab selected': emptyProps(),
'example details received': props<{
exampleDetails: ExampleResponse;
}>(),
'example details loading failed': props<{ error: string | null }>(),
},
...
3. HTML
Adapt in File: <feature>-details.component.html
<ocx-portal-page *ngrxLet="viewModel$; let vm">
<p-tabView
[(activeIndex)]="activeTabIndex"
(onChange)="handleTabChange($event)"
>
<p-tabPanel
[header]="'FEATURE_DETAILS.TABS.EXAMPLE_DETAILS' | translate"
>
<div *ngIf="vm" >
<app-example-details-tab
[vm]="vm?.exampleDetails"
></app-example-details-tab>
</div>
</p-tabPanel>
</p-tabView>
</ocx-portal-page>
4. Component
Adapt in File: <feature>-details.component.ts
handleTabChange(event: { originalEvent: Event; index: number }) {
switch (event.index) {
case 0:
this.store.dispatch(FeatureDetailsActions.exampleDetailsTabSelected());
break;
...
default:
this.store.dispatch(FeatureDetailsActions.otherTabSelected());
}
}
5. Reducers
In the reducers file you need to define the functions:
Adapt in File: <feature>-search.reducers.ts
...
on(
<%= featureClassName %>DetailsActions.exampleDetailsTabSelected,
(state: <%= featureClassName %>DetailsState): <%= featureClassName %>DetailsState => ({
...state,
activeTab: 'ExampleDetailsTab',
})
),
on(
<%= featureClassName %>DetailsActions.exampleDetailsReceived,
(state: <%= featureClassName %>DetailsState, { exampleDetails }): <%= featureClassName %>DetailsState => ({
...state,
exampleDetails: exampleDetails,
})
),
on(
<%= featureClassName %>DetailsActions.exampleDetailsLoadingFailed,
(state: <%= featureClassName %>DetailsState): <%= featureClassName %>DetailsState => ({
...state,
exampleDetails: undefined,
})
),
...
6. Selectors
Add the missing selectors:
Adapt in File: <feature>-details.selectors.ts
...
export const select<%= featureClassName %>DetailsViewModel = createSelector(
...
selectExampleDetailsTabViewModel,
...
(
...
exampleDetailsTab,
...
): <%= featureClassName %>DetailsViewModel => ({
...
exampleDetailsTab,
...
}),
);
...
7. Effects
Adapt in File: <feature>-details.effects.ts
exampleDetailsTab$ = createEffect(() => {
return this.actions$.pipe(
ofType(
<%= featureClassName %>DetailsActions.exampleDetailsTabSelected,
),
concatLatestFrom(() =>
this.store.select(<%= featureClassName %>DetailsSelectors.selectActiveTab),
),
filter(([, activeTab]) => activeTab === 'ExampleDetailsTab'),
concatLatestFrom(() =>
this.store.select(<%= featureClassName %>DetailsSelectors.selectSomePropertyContainingId),
),
switchMap(([, id]) => {
return this.<%= featureClassName %>DetailsService
.getExampleDetailsTabDataById(id)
.pipe(
map(({ exampleDetailsTabData }) => {
return <%= featureClassName %>DetailsActions.exampleDetailsReceived({
exampleDetails: exampleDetailsTabData,
});
}),
catchError((error) =>
of(
<%= featureClassName %>DetailsActions.exampleDetailsLoadingFailed({
error,
}),
),
),
);
}),
);
});
Don’t forget to add the translations to your de.json and en.json. |