Skip to content

Commit 92810f3

Browse files
Merge pull request #26 from upstateinteractive/feat/ngrx
feat/ngrx
2 parents 532b937 + 3cfb8cc commit 92810f3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+13984
-327
lines changed
Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,41 @@
1-
import {
2-
chain,
3-
externalSchematic,
4-
Rule,
5-
apply,
6-
url,
7-
template,
8-
move,
1+
import {
2+
chain,
3+
externalSchematic,
4+
Rule,
5+
apply,
6+
url,
7+
template,
8+
move,
99
mergeWith,
1010
noop,
1111
} from '@angular-devkit/schematics';
1212

1313
import { strings } from '@angular-devkit/core';
1414
import { DomainOptions } from './schema';
15-
import { addDomainToLintingRules } from '../utils/update-linting-rules';
15+
import {
16+
addDomainToLintingRules,
17+
addNgrxImportsToApp,
18+
addNgRxToPackageJson,
19+
} from '../rules';
1620

17-
export default function(options: DomainOptions): Rule {
21+
export default function (options: DomainOptions): Rule {
1822
const libFolder = strings.dasherize(options.name);
1923

2024
const templateSource = apply(url('./files'), [
2125
template({}),
22-
move(`libs/${libFolder}/domain/src/lib`)
26+
move(`libs/${libFolder}/domain/src/lib`),
2327
]);
2428

29+
const appFolderName = strings.dasherize(options.name);
30+
const appPath = `apps/${appFolderName}/src/app`;
31+
const appModulePath = `${appPath}/app.module.ts`;
32+
33+
if (options.ngrx && !options.addApp) {
34+
throw new Error(
35+
`The 'ngrx' option may only be used when the 'addApp' option is used.`
36+
)
37+
}
38+
2539
return chain([
2640
externalSchematic('@nrwl/angular', 'lib', {
2741
name: 'domain',
@@ -34,12 +48,25 @@ export default function(options: DomainOptions): Rule {
3448
}),
3549
addDomainToLintingRules(options.name),
3650
mergeWith(templateSource),
37-
(!options.addApp) ?
38-
noop() :
39-
externalSchematic('@nrwl/angular', 'app', {
40-
name: options.name,
41-
tags: `domain:${options.name},type:app`,
42-
style: 'scss',
43-
}),
51+
!options.addApp
52+
? noop()
53+
: externalSchematic('@nrwl/angular', 'app', {
54+
name: options.name,
55+
tags: `domain:${options.name},type:app`,
56+
style: 'scss',
57+
}),
58+
options.addApp && options.ngrx
59+
? chain([
60+
externalSchematic('@ngrx/schematics', 'store', {
61+
project: options.name,
62+
root: true,
63+
minimal: true,
64+
module: 'app.module.ts',
65+
name: 'state',
66+
}),
67+
addNgrxImportsToApp(appModulePath),
68+
addNgRxToPackageJson(),
69+
])
70+
: noop(),
4471
]);
4572
}

libs/ddd/src/schematics/domain/schema.json

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,23 @@
1818
"x-prompt": "Would you like to add an associated application?",
1919
"default": false
2020
},
21+
"ngrx": {
22+
"type": "boolean",
23+
"default": false,
24+
"description": "Add ngrx for the associated app (addApp required)",
25+
"x-prompt": "Would you like to add NgRx to your associated application?"
26+
},
2127
"type": {
2228
"type": "string",
23-
"enum": [
24-
"internal",
25-
"buildable",
26-
"publishable"
27-
],
29+
"enum": ["internal", "buildable", "publishable"],
2830
"description": "A type to determine if and how to build the library.",
2931
"default": "buildable"
32+
},
33+
"ngrx": {
34+
"type": "boolean",
35+
"default": false,
36+
"description": "Add ngrx for the domain",
37+
"x-prompt": "Would you like to add NgRx to your domain?"
3038
}
3139
},
3240
"required": ["name"]

libs/ddd/src/schematics/domain/schema.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ export interface DomainOptions {
1414
* Add an app for the domain?
1515
*/
1616
addApp?: boolean;
17+
/**
18+
* Add ngrx for the associated app (addApp required)
19+
*/
20+
ngrx?: boolean;
1721
/**
1822
* A type to determine if and how to build the library.
1923
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createAction, props } from '@ngrx/store';
2+
import { <%= classify(entity) %> } from '../../entities/<%= dasherize(entity) %>';
3+
4+
export const load<%= classify(entity) %> = createAction(
5+
'[<%= classify(entity) %>] Load <%= classify(entity) %>'
6+
);
7+
8+
export const load<%= classify(entity) %>Success = createAction(
9+
'[<%= classify(entity) %>] Load <%= classify(entity) %> Success',
10+
props<{ <%= camelize(entity) %>: <%= classify(entity) %>[] }>()
11+
);
12+
13+
export const load<%= classify(entity) %>Failure = createAction(
14+
'[<%= classify(entity) %>] Load <%= classify(entity) %> Failure',
15+
props<{ error: any }>()
16+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Injectable } from '@angular/core';
2+
import { createEffect, Actions, ofType } from '@ngrx/effects';
3+
import { catchError, map, switchMap } from 'rxjs/operators';
4+
import { of } from 'rxjs';
5+
import * as <%= classify(entity) %>Actions from './<%= dasherize(entity) %>.actions';
6+
import { <%= classify(entity) %>DataService } from '../../infrastructure/<%= dasherize(entity) %>.data.service';
7+
8+
@Injectable()
9+
export class <%= classify(entity) %>Effects {
10+
load<%= classify(entity) %>$ = createEffect(() =>
11+
this.actions$.pipe(
12+
ofType(<%= classify(entity) %>Actions.load<%= classify(entity) %>),
13+
switchMap((action) =>
14+
this.<%=camelize(entity)%>DataService.load().pipe(
15+
map((<%= camelize(entity) %>) =>
16+
<%= classify(entity) %>Actions.load<%= classify(entity) %>Success({ <%= camelize(entity) %> })
17+
),
18+
catchError((error) =>
19+
of(<%= classify(entity) %>Actions.load<%= classify(entity) %>Failure({ error }))
20+
)
21+
)
22+
)
23+
)
24+
);
25+
26+
constructor(
27+
private actions$: Actions,
28+
private <%=camelize(entity)%>DataService: <%= classify(entity) %>DataService
29+
) { }
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { createReducer, on, Action } from '@ngrx/store';
2+
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
3+
4+
import * as <%= classify(entity) %>Actions from './<%= dasherize(entity) %>.actions';
5+
import { <%= classify(entity) %> } from '../../entities/<%= dasherize(entity) %>';
6+
7+
export const <%= classify(entity).toUpperCase() %>_FEATURE_KEY = '<%= camelize(entity) %>';
8+
9+
export interface State extends EntityState<<%= classify(entity) %>> {
10+
selectedId ?: string | number; // which <%= classify(entity) %> record has been selected
11+
loaded : boolean; // has the <%= classify(entity) %> list been loaded
12+
error ?: string | null; // last known error (if any)
13+
}
14+
15+
export interface <%= classify(entity) %>PartialState {
16+
readonly [<%= classify(entity).toUpperCase() %>_FEATURE_KEY]: State;
17+
}
18+
19+
export const <%= camelize(entity) %>Adapter: EntityAdapter<<%= classify(entity) %>> = createEntityAdapter<<%= classify(entity) %>>();
20+
21+
export const initialState: State = <%= camelize(entity) %>Adapter.getInitialState({
22+
// set initial required properties
23+
loaded : false
24+
});
25+
26+
const <%= camelize(entity) %>Reducer = createReducer(
27+
initialState,
28+
on(<%= classify(entity) %>Actions.load<%= classify(entity) %>,
29+
state => ({ ...state, loaded: false, error: null })
30+
),
31+
on(<%= classify(entity) %>Actions.load<%= classify(entity) %>Success,
32+
(state, { <%= camelize(entity) %> }) => <%= camelize(entity) %>Adapter.upsertMany(<%= camelize(entity) %>, { ...state, loaded: true })
33+
),
34+
on(<%= classify(entity) %>Actions.load<%= classify(entity) %>Failure,
35+
(state, { error }) => ({ ...state, error })
36+
),
37+
);
38+
39+
export function reducer(state: State | undefined, action: Action) {
40+
return <%= camelize(entity) %>Reducer(state, action);
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { createFeatureSelector, createSelector } from '@ngrx/store';
2+
import { <%= classify(entity).toUpperCase() %>_FEATURE_KEY, State, <%= classify(entity) %>PartialState, <%= camelize(entity) %>Adapter } from './<%= dasherize(entity) %>.reducer';
3+
4+
// Lookup the '<%= classify(entity) %>' feature state managed by NgRx
5+
export const get<%= classify(entity) %>State = createFeatureSelector<<%= classify(entity) %>PartialState, State>(<%= camelize(entity).toUpperCase() %>_FEATURE_KEY);
6+
7+
const { selectAll, selectEntities } = <%= camelize(entity) %>Adapter.getSelectors();
8+
9+
export const get<%= classify(entity) %>Loaded = createSelector(
10+
get<%= classify(entity) %>State,
11+
(state: State) => state.loaded
12+
);
13+
14+
export const get<%= classify(entity) %>Error = createSelector(
15+
get<%= classify(entity) %>State,
16+
(state: State) => state.error
17+
);
18+
19+
export const getAll<%= classify(entity) %> = createSelector(
20+
get<%= classify(entity) %>State,
21+
(state: State) => selectAll(state)
22+
);
23+
24+
export const get<%= classify(entity) %>Entities = createSelector(
25+
get<%= classify(entity) %>State,
26+
(state: State) => selectEntities(state)
27+
);
28+
29+
export const getSelectedId = createSelector(
30+
get<%= classify(entity) %>State,
31+
(state: State) => state.selectedId
32+
);
33+
34+
export const getSelected = createSelector(
35+
get<%= classify(entity) %>Entities,
36+
getSelectedId,
37+
(entities, selectedId) => selectedId && entities[selectedId]
38+
);
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Injectable } from '@angular/core';
2+
3+
import { select, Store, Action } from '@ngrx/store';
4+
5+
import * as from<%= classify(entity) %> from '../+state/<%= dasherize(entity) %>/<%= dasherize(entity) %>.reducer';
6+
import * as <%= classify(entity) %>Selectors from '../+state/<%= dasherize(entity) %>/<%= dasherize(entity) %>.selectors';
7+
8+
@Injectable({ providedIn: 'root' })
9+
export class <%= classify(name) %>Facade {
10+
loaded$ = this.store.pipe(select(<%= classify(entity) %>Selectors.get<%= classify(entity) %>Loaded));
11+
<%= camelize(entity) %>List$ = this.store.pipe(select(<%= classify(entity) %>Selectors.getAll<%= classify(entity) %>));
12+
selected<%= classify(entity) %>$ = this.store.pipe(select(<%= classify(entity) %>Selectors.getSelected));
13+
14+
constructor(private store: Store<from<%= classify(entity) %>.<%= classify(entity) %>PartialState>) { }
15+
16+
dispatch(action: Action) {
17+
this.store.dispatch(action);
18+
}
19+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface <%=classify(entity)%> {
2+
id: number;
3+
name: string;
4+
description: string;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import {Injectable} from '@angular/core';
2+
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
3+
import {Observable, of} from 'rxjs';
4+
import {<%=classify(entity)%>} from '../entities/<%=dasherize(entity)%>';
5+
6+
@Injectable({ providedIn: 'root' })
7+
export class <%=classify(entity)%>DataService {
8+
9+
constructor(private http: HttpClient) {
10+
}
11+
12+
load(): Observable<<%=classify(entity)%>[]> {
13+
14+
// Uncomment if needed
15+
/*
16+
const url = '...';
17+
const params = new HttpParams().set('param', 'value');
18+
const headers = new HttpHeaders().set('Accept', 'application/json');
19+
return this.http.get<<%=classify(entity)%>[]>(url, {params, headers});
20+
*/
21+
22+
return of([
23+
{id: 1, name: 'Lorem ipsum', description: 'Lorem ipsum dolor sit amet'},
24+
{id: 2, name: 'At vero eos', description: 'At vero eos et accusam et justo duo dolores'},
25+
{id: 3, name: 'Duis autem', description: 'Duis autem vel eum iriure dolor in hendrerit'},
26+
]);
27+
}
28+
}

0 commit comments

Comments
 (0)