Skip to content

Retrospective #4

nguyenkim42 edited this page Nov 9, 2017 · 11 revisions

Test effect which is using store:

Effect gets data from store using select method

a = store.select(getSomeData);

One important note to remember is that: The store's select method needs to be mocked before the effect instance is created. Otherwise, it is undefined and causes error.

Angular Material library

We are moving to version 2.0.0-beta.12. This contains new components and the documents are for it.

rxjs concatMap

When testing with jasmine-marbles, remember to add complete (|) event to the marble of a service calls. Otherwise, the observable never complete after emitting an event. In normal scenario, do not add (|) does not cause any error. However, with concatMap, it causes an error.

const source = cold(  '-a--a--a');
const child = cold(   '-bbbbb');
const expected = cold('--bbbbb-bbbbb-bbbbb');
const concatMap = source.concatMap(() => child);
expect(concatMap).toBeObservable(expected);

This test fails, the output is '--bbbbb------'. The reason is that the child never complete so that concatMap waits forever.
The fix is const child = cold( '-bbbb(b|)');

ChangeDetectionStrategy.OnPush

This will inform Angular that our component only depends on its inputs and that any object that is passed to it should be considered immutable.

  • ChangeDetectionStrategy.Default: any changes of inputs, ex: instance, properties input change => change detection runs
  • ChangeDetectionStrategy.OnPush: instance of inputs change only => change detection runs

Live Demo

ExpressionChangedAfterItHasBeenCheckedError error

Change detection

View State

  • FirstCheck
  • ChecksEnabled
  • Errored
  • Destroyed

!ChecksEnabled || Errored || Destroyed => Change detection is skipped.

By default, all views are initialized with ChecksEnabled unless ChangeDetectionStrategy.OnPush is used.

If a component is set ChangeDetectionStrategy.OnPush, it has only initial change detection runs, it does not depend on any input, no more change detection runs will occur.

Change detection operations

Updating input properties on the child components

Updating DOM elements of the current component

Lifecycle hooks with components hierarchy

Change detection

Components hierarchy: A > B > C

A: AfterContentInit
A: AfterContentChecked
A: Update bindings
    B: AfterContentInit
    B: AfterContentChecked
    B: Update bindings
        C: AfterContentInit
        C: AfterContentChecked
        C: Update bindings
        C: AfterViewInit
        C: AfterViewChecked
    B: AfterViewInit
    B: AfterViewChecked
A: AfterViewInit
A: AfterViewChecked

Ref: https://blog.angularindepth.com/everything-you-need-to-know-about-change-detection-in-angular-8006c51d206f

Causes of Error

Synchronous event broadcasting

The application is designed to have a child component emitting an event and a parent component listening to this event. The event causes some of parent properties to be updated. And these properties are used as input binding for the child component. This is also an indirect parent property update.

Solution

Make event emitter sent event asynchronously. By default, the parameter of constructor is false and it's synchronous

@Output() onBrush = new EventEmitter<Brush>(true);

Read more: https://blog.angularindepth.com/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error-e3fd9ce7dbb4

Prevent state from mutation

ngrx-store-freeze is a meta-reducer that prevents state from being mutated. When state is modified, an exception will be thrown and state will be interrupted.

Install:

npm i --save-dev ngrx-store-freeze

Apply:

export const metaReducers: MetaReducer<State>[] = !environment.production
  ? [logger, storeFreeze]
  : [];

This is not recommended in production, as it uses Object.freeze, which is not supported by all browsers.

Clone this wiki locally