Skip to content

Commit

Permalink
fix: quick quantity change on item lists (#1391)
Browse files Browse the repository at this point in the history
Co-authored-by: Marcel Eisentraut <meisentraut@intershop.de>
  • Loading branch information
SGrueber and Eisie96 authored Mar 30, 2023
1 parent 1651456 commit b019bb2
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 6 deletions.
33 changes: 33 additions & 0 deletions docs/guides/angular-component-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,39 @@ In this case the condition should look like this:
<ng-container *ngIf="observable$ | async as synchronized; else loading">
```

## Pattern for Loops (ngFor) with Changing Data in Component Templates

Looping through an array in a component may sometimes be accompanied by side effects if the data within the array are changing.

```html
<ng-container *ngFor="let element of array$ | async">
<another-component [element]="element"></another-component>
</ng-container>
```

In case the values of the array$ observable are varying somehow during the lifetime of the component (reordering elements, add/ delete elements, changing properties), all children DOM elements are destroyed and re-initialized with the new data.

To avoid these many and expensive DOM manipulations and persist the children DOM elements the loop elements has to be uniquely identified in the `NgFor` directive.
This can be achieved by using custom [`trackBy`](https://angular.io/api/core/TrackByFunction) functions within the `ngFor` directive.

```typescript
@Component({
...
template: `
<ng-container *ngFor="let element of array$ | async; trackBy: customTrackByFn">
<another-component [element]="element"></another-component>
</ng-container>`
})
export class AnyComponent implements OnInit, OnDestroy {
...
customTrackByFn(index, element) {
return element.id;
}
}
```

The custom trackBy function needs to return unique values for all unique inputs.

## Do Not Unsubscribe, Use Destroy Observable and takeUntil Instead

Following the ideas of the article [RxJS: Don’t Unsubscribe](https://benlesh.medium.com/rxjs-dont-unsubscribe-6753ed4fda87), the following pattern is used for ending subscriptions to observables that are not handled via async pipe in the templates.
Expand Down
3 changes: 3 additions & 0 deletions docs/guides/migrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ Please run for each configured Angular project (e.g. 'intershop-pwa') the follow

> **NOTE:** Not all scenarios are taken into consideration for the `formly-migrate` schematic, where a deprecated property could be found. Please check and adapt manually your code for additional changes. For further information look into the [formly migration guide](https://formly.dev/docs/guide/migration/).
The templates of `account-order-template-detail-page.component.ts`, `quote-line-item-list.component.ts`, `quoting-basket-line-items.component.ts` and `account-wishlist-detail-page.component.ts` are updated to ensure correct DOM element updates for `ngFor` loop changes.
A [trackBy function](https://angular.io/api/core/TrackByFunction) will now be used.

Obsolete functionality that is no longer needed with the current state of the Intershop PWA was removed from the project source code.

- removed outdated `kubernetes-deployment` schematic that could be used to create Kubernetes charts, use the official [Intershop PWA Helm Chart repository](https://github.com/intershop/helm-charts/tree/main/charts/pwa) instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ <h1 class="clearfix">
</div>
</div>
<div class="list-body">
<ng-container *ngFor="let item of orderTemplate.items; let i = index">
<ng-container *ngFor="let item of orderTemplate.items; let i = index; trackBy: trackByFn">
<div class="list-item-row list-item-row-big">
<ish-account-order-template-detail-line-item
ishProductContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { HttpError } from 'ish-core/models/http-error/http-error.model';
import { mapToProperty } from 'ish-core/utils/operators';

import { OrderTemplatesFacade } from '../../facades/order-templates.facade';
import { OrderTemplate } from '../../models/order-template/order-template.model';
import { OrderTemplate, OrderTemplateItem } from '../../models/order-template/order-template.model';

@Component({
selector: 'ish-account-order-template-detail-page',
Expand Down Expand Up @@ -36,4 +36,8 @@ export class AccountOrderTemplateDetailPageComponent implements OnInit {
id: orderTemplateName,
});
}

trackByFn(_: number, item: OrderTemplateItem) {
return item.id;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</div>
<div class="list-body">
<ish-quote-line-item-list-element
*ngFor="let pli of lineItems$ | async"
*ngFor="let pli of lineItems$ | async; trackBy: trackByFn"
[lineItem]="pli"
ishProductContext
[allowZeroQuantity]="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ export class QuoteLineItemListComponent implements OnInit {
);
this.editable$ = this.context.select('editable');
}

trackByFn(_: number, item: Pick<QuoteRequestItem, 'productSKU' | 'quantity'>) {
return item.productSKU;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<ng-container *ngFor="let item of lineItems$ | async">
<ng-container *ngFor="let item of lineItems$ | async; trackBy: trackByFn">
<div class="row" *ngIf="item[0] !== 'undefined'">
<h2 class="col-12">
{{ 'quote.basket_items.label' | translate : { '0': getName(item[0]) | async } }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ export class QuotingBasketLineItemsComponent implements OnInit {
onDeleteQuote(quoteId: string) {
this.quotingFacade.deleteQuoteFromBasket(quoteId);
}

trackByFn(_: number, element: [string, LineItem[]]) {
// quoteId
return element[0];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ <h1>{{ wishlist?.title }}</h1>
</div>
</div>
<div class="list-body">
<ng-container *ngFor="let item of wishlist.items">
<ng-container *ngFor="let item of wishlist.items; trackBy: trackByFn">
<div class="list-item-row">
<ish-account-wishlist-detail-line-item
ishProductContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Observable } from 'rxjs';
import { HttpError } from 'ish-core/models/http-error/http-error.model';

import { WishlistsFacade } from '../../facades/wishlists.facade';
import { Wishlist } from '../../models/wishlist/wishlist.model';
import { Wishlist, WishlistItem } from '../../models/wishlist/wishlist.model';

@Component({
selector: 'ish-account-wishlist-detail-page',
Expand All @@ -30,4 +30,8 @@ export class AccountWishlistDetailPageComponent implements OnInit {
id: wishlistName,
});
}

trackByFn(_: number, item: WishlistItem) {
return item.id;
}
}

0 comments on commit b019bb2

Please sign in to comment.