Skip to content

Commit

Permalink
docs: update the style guide to add entities and improve selectors
Browse files Browse the repository at this point in the history
fix: #1
fix: #2
fix: #3
  • Loading branch information
michaeljota committed Oct 9, 2018
1 parent 78cc4d5 commit ddb90fb
Showing 1 changed file with 78 additions and 27 deletions.
105 changes: 78 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ export class Component implements OnInit {
constructor(private readonly store: Store<AppState>) {}

ngOnInit() {
this.heroes = this.store.select(selectHeroesList).pipe(startWith([]));
this.heroes = this.store.select(selectHeroesList);
}
}
```
Expand All @@ -349,6 +349,8 @@ Useful tips for Angular applications with `@ngrx` platform.

*Consider* exporting the actions, the type and the enum of the actions types from an action file.

*Consider* using named properties instead of generic payload

*Why?* Using classes for actions allow to take advantage of the discriminatory types of Typescript.

```typescript
Expand All @@ -373,17 +375,17 @@ export type ComponentActionType =

export class LoadModel implements Action {
public readonly type = ComponentActions.Load;
constructor(public readonly payload: string) {}
constructor(public readonly modelId: string) {}
}

export class LoadModelSuccess implements Action {
public readonly type = ComponentActions.LoadSuccess;
constructor(public readonly payload: Readonly<Model>) {}
constructor(public readonly model: Readonly<Model>) {}
}

export class SaveModel implements Action {
public readonly type = ComponentActions.Save;
constructor(public readonly payload: Readonly<Model>) {}
constructor(public readonly model: Readonly<Model>) {}
}

export class SaveModelSuccess implements Action {
Expand All @@ -403,6 +405,8 @@ export class SaveModelSuccess implements Action {

*Consider* registering the dispatcher in the feature module as a provider.

*Consider* using memoize functions methods.

*Why?* A dispatcher service allows you to take advantage of Angular Dependency Injection.

*Why?* You could change your actions, without modifying your dispatcher.
Expand Down Expand Up @@ -482,17 +486,15 @@ export class ComponentEffects {

@Effect()
public readonly getModel = this.actions.pipe(
ofType(ComponentActions.Load),
switchMap((action: LoadModel) => this.service.get(action.payload)),
ofType<LoadModel>(ComponentActions.Load),
switchMap(({ modelId }) => this.service.get(modelId)),
map(model => this.dispatcher.loadSuccess(model)),
);

@Effect({ dispatch: false })
public readonly saveModel = this.actions.pipe(
ofType(ComponentActions.Save),
switchMap((action: SaveModel) =>
this.service.update(action.payload.id, action.payload),
),
ofType<SaveModel>(ComponentActions.Save),
switchMap(({ model }) => this.service.update(model.id, model)),
);
}
```
Expand Down Expand Up @@ -530,14 +532,14 @@ export class ComponentState {
* @type {ActionReducer<ComponentState,ComponentType>}
*/
export function componentReducer(
state: ComponentState = new ComponentState(),
state: ComponentState,
action: ComponentType,
): ComponentState {
switch (action.type) {
case ComponentActions.LoadSuccess:
return {
...state,
model: action.payload,
model: action.model,
};
default:
return state;
Expand All @@ -550,39 +552,88 @@ export function componentReducer(

*Consider* exporting a feature selector in the store configuration.

*Consider* using selectors to take the property you need.
*Consider* using `select` pipe operator with a function argument to select component state and properties.

*Consider* create a new selector file only if you are using a composed or complex selectors.

*Why?* Selectors are a type safe approach to return an observable of the feature state.

*Why?* pipe operators are typed so they are also a type safe approach to get the properties.

```typescript
// Consider
/* feature-store.module.ts */
...
import { createFeatureSelector } from '@ngrx/store';
...

export const selectFeatureState = createFeatureSelector('feature');

/* component.component.ts */
...
export class ComponentComponent {
models: Observable<Model[]>

constructor(private readonly store: Store) {
this.models = store.select(selectFeatureState).pipe(
select((state) => state.component.models),
);
}
}
```

### Entities

##### Style NGRX-06

*Consider* using the `@ngrx/entity` module to create and handle entities.

*Consider* placing the entity adapter inside the reducer file.

*Consider* exporting the selectors generated from the entity adapter.

*Why?* Selectors are a type safe approach to return an observable property of the state.
*Consider* using `select` pipe operator and the entity adapter selectors to select entities properties.

*Why* The adapter is a function helper to manage the entity state, so you would need it there.

```typescript
// Consider
/* component.selectors.ts */
import { createSelector } from '@ngrx/store';
/* component.reducer.ts */
import {
createEntityAdapter,
EntityState,
Dictionary,
Update,
} from '@ngrx/entity';

import { featureState } from './../feature.module';
import { Model } from '../model';

export const selectComponent = createSelector(
featureState,
state => state.details,
);
const modelAdapter = createEntityAdapter<Model>();

export const modelSelectors = modelAdapter.getSelectors();

export class ComponentState implements EntityState<Model> {
public readonly ids: number[] | string[] = [];
public readonly entities: Dictionary<Model> = {};
public readonly anotherProperty: boolean;
}

export const selectHero = createSelector(
selectComponent,
state => state.hero,
);
```


### Stores

##### Style NGRX-06
##### Style NGRX-07

*Consider* use an Angular barrel module to setup the Store.

*Consider* setup and export the state from the module.

*Consider* setup and export the selector from the module.

*Why?* Barrel modules are a common practice in Angular, and a recommended way to keep your modules cleaner.

*Why?* Your initial state and the store reducers will be easy to find after scaling.
*Why?* The store state, reducers and selectors will be easy to find after scaling.

```typescript
// Avoid
Expand Down

0 comments on commit ddb90fb

Please sign in to comment.