Related Github Documents
Document Properties
KbidX29451
Last Modified08-Jun-2020
Added to KB18-Jun-2020
Public AccessEveryone
StatusOnline
Doc TypeGuidelines, Concepts & Cookbooks
ProductIntershop Progressive Web App

Guide - Intershop Progressive Web App - Angular Component Development

Angular Component Development

Declare Components in the Right NgModule

Angular requires you to declare a component in one and only one NgModule.
Find the right one in the following order:

Your Component is used only on one page? - Add it to the declarations of the corresponding page.module.

Your Component is used among multiple pages? - Declare it in the shared.module and also export it there.

Your Component is used in the application shell (and maybe again on certain pages)? - Declare it in the shell.module and also export it there.

(advanced) Your component relates to a specific B2B extension? - Declare it in that extension module and add it as an entryComponent, add a lazy-loaded component and add that to the extension exports, which are then im-/exported in the shared.module.

When using ng generate, the right module should be found automatically.

Do not use NgRx or Services in Components

Using NgRx or Services directly in components violates our model of abstraction.
Only facades should be used in components, as they provide the simplest access to the business logic.

Delegate Complex Component Logic to Services

There should not be any string or URL manipulation, routing mapping or REST endpoint string handling within components.
This is supposed to be handled by methods of services.
See also Angular Style Guide.

Put as Little Logic Into constructor as Possible - Use ngOnInit

See The essential difference between Constructor and ngOnInit in Angular and Angular constructor versus ngOnInit.

Use Property Binding to Bind Dynamic Values to Attributes or Properties

See Explanation of the difference between an HTML attribute and a DOM property.

There are often two ways to bind values dynamically to attributes or properties: interpolation or property binding.
In the PWA we prefer using property binding since this covers more cases in the same way.
So the code will be more consistent.

There is an exception for direct string value bindings where we use for example routerLink="/logout" instead of [routerLink]="'/logout'".

⚠️ Pattern to avoid

<div attr.data-testing-id="category-{{category.id}}">
  <img src="{{base_url + category.images[0].effectiveUrl}}" />
</div>

✔️ Correct pattern

<div [attr.data-testing-id]="'category-' + category.id">
  <img [src]="base_url + category.images[0].effectiveUrl" />
</div>

Pattern for Conditions (ngif) with Alternative Template (else) in Component Templates

Also for consistency reasons, we want to establish the following pattern for conditions in component templates:

⚠️ Condition in template

<ng-container *ngIf="show; else elseBlock">
 ... (template code for if-branch)
</ng-container>

<ng-template #elseBlock>
  ... (template code for else-branch)
</ng-template>

This pattern provides the needed flexibility if used together with handling observables with *ngIf and the async pipe.
In this case the condition should look like this:

<ng-container *ngIf="(observable$ | async) as synchronized; else loading">

Do Not Unsubscribe, Use Destroy Observable and takeUntil Instead

Following the ideas of the article RxJS: Don’t Unsubscribe, the following pattern is used for ending subscriptions to observables that are not handled via async pipe in the templates.

✔️ 'unsubscribe' via destroy$ Subject

export class AnyComponent implements OnInit, OnDestroy {
  ...
  private destroy$ = new Subject();
  ...
  ngOnInit() {
    ...
    observable$.pipe(takeUntil(this.destroy$))
      .subscribe(/* ... */);
  }
  ...
  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

The TSLint rule rxjs-prefer-angular-takeuntil enforces the usage of a destroy$ Subject with takeUntil when subscribing in an Angular artifact.
You can use the schematic add-destroy to automatically generate the required logic:

$ ng g add-destroy shared/components/common/accordion

UPDATE src/app/shared/components/common/accordion/accordion.component.ts (425 bytes)

Use OnPush Change Detection if Possible

To reduce the number of ChangeDetection computation cycles, all components should have their Component decorator property changeDetection set to ChangeDetectionStrategy.OnPush.

Split Components When Necessary

Consider splitting one into multiple components when:

  • Size: Component code becomes too complex to be easily understandable

  • Separation of concerns: A component serves different concerns that should be separated

  • Reusability: A component should be reused in different contexts. This can introduce a shared component which could be placed in a shared module.

  • Async data: Component relies on async data from the store which makes the component code unnecessarily complex. Use a container component then which resolves the observables at the outside of the child component and passes data in via property bindings. Do not do this for simple cases.

Single-use dumb components are always okay if it improves readability.

Mock Facades in Tests

Angular Artifacts like Components, Directives and Pipes should solely depend on facades to interact with the State Management.
This is enforced with the TSLint rule no-intelligence-in-artifacts which rejects every usage of REST API Services and NgRx Artifacts.

Use ts-mockito for creating and managing these mocks.
Providers for Facades can easily be added by using the VSCode snippet ish-provider-ts-mockito:

ish-provider-ts-mockito

Disclaimer

The information provided in the Knowledge Base may not be applicable to all systems and situations. Intershop Communications will not be liable to any party for any direct or indirect damages resulting from the use of the Customer Support section of the Intershop Corporate Web site, including, without limitation, any lost profits, business interruption, loss of programs or other data on your information handling system.

Customer Support
Knowledge Base
Product Resources
Support Tickets