From 42097f00a03511a7fb8063092f0120d93a0eb0ac Mon Sep 17 00:00:00 2001 From: Brandon Roberts Date: Fri, 19 Apr 2019 08:43:13 -0500 Subject: [PATCH] feat(schematics): add support for reducer creators Closes #1764, 1766 --- .../__name@dasherize__.actions.ts.template | 11 +-- modules/schematics/src/action/index.spec.ts | 2 +- modules/schematics/src/action/index.ts | 2 +- modules/schematics/src/action/schema.json | 6 +- modules/schematics/src/action/schema.ts | 6 +- .../__name@dasherize__.effects.ts.template | 20 ++--- modules/schematics/src/effect/index.spec.ts | 10 +-- modules/schematics/src/effect/index.ts | 18 ++-- modules/schematics/src/effect/schema.json | 7 +- modules/schematics/src/effect/schema.ts | 5 +- ...herize@group-actions__.actions.ts.template | 74 ++++++++++------ ...erize@group-reducers__.reducer.ts.template | 85 ++++++++----------- modules/schematics/src/entity/index.spec.ts | 13 +-- modules/schematics/src/entity/index.ts | 2 +- modules/schematics/src/entity/schema.json | 6 +- modules/schematics/src/entity/schema.ts | 6 +- modules/schematics/src/feature/index.ts | 6 +- modules/schematics/src/feature/schema.json | 2 +- modules/schematics/src/feature/schema.ts | 2 +- .../__name@dasherize__.reducer.ts.template | 23 ++--- modules/schematics/src/reducer/index.spec.ts | 55 ++++++++++++ modules/schematics/src/reducer/index.ts | 2 +- modules/schematics/src/reducer/schema.json | 6 +- modules/schematics/src/reducer/schema.ts | 6 +- .../content/guide/schematics/action.md | 7 ++ .../content/guide/schematics/effect.md | 8 +- .../content/guide/schematics/entity.md | 7 ++ .../content/guide/schematics/feature.md | 2 +- .../content/guide/schematics/reducer.md | 7 ++ 29 files changed, 242 insertions(+), 164 deletions(-) diff --git a/modules/schematics/src/action/creator-files/__name@dasherize@if-flat__/__name@dasherize__.actions.ts.template b/modules/schematics/src/action/creator-files/__name@dasherize@if-flat__/__name@dasherize__.actions.ts.template index 399ba7a7bd..523e4ec919 100644 --- a/modules/schematics/src/action/creator-files/__name@dasherize@if-flat__/__name@dasherize__.actions.ts.template +++ b/modules/schematics/src/action/creator-files/__name@dasherize@if-flat__/__name@dasherize__.actions.ts.template @@ -1,14 +1,5 @@ -import { createAction, union } from '@ngrx/store'; +import { createAction } from '@ngrx/store'; export const load<%= classify(name) %>s = createAction('[<%= classify(name) %>] Load <%= classify(name) %>s'); <% if (api) { %>export const load<%= classify(name) %>sSuccess = createAction('[<%= classify(name) %>] Load <%= classify(name) %>s Success');<% } %> <% if (api) { %>export const load<%= classify(name) %>sFailure = createAction('[<%= classify(name) %>] Load <%= classify(name) %>s Failure');<% } %> - -const all = union({ - load<%= classify(name) %>s, -<% if (api) { %> - load<%= classify(name) %>sSuccess, - load<%= classify(name) %>sFailure -<% } %> -}); -export Union = typeof all; diff --git a/modules/schematics/src/action/index.spec.ts b/modules/schematics/src/action/index.spec.ts index bb7e0f736a..ff7476a816 100644 --- a/modules/schematics/src/action/index.spec.ts +++ b/modules/schematics/src/action/index.spec.ts @@ -117,7 +117,7 @@ describe('Action Schematic', () => { }); describe('action creators', () => { - const creatorOptions = { ...defaultOptions, actionCreators: true }; + const creatorOptions = { ...defaultOptions, creators: true }; it('should create a const for the action creator', () => { const tree = schematicRunner.runSchematic( diff --git a/modules/schematics/src/action/index.ts b/modules/schematics/src/action/index.ts index 2dc24fe074..444bb86c35 100644 --- a/modules/schematics/src/action/index.ts +++ b/modules/schematics/src/action/index.ts @@ -30,7 +30,7 @@ export default function(options: ActionOptions): Rule { options.path = parsedPath.path; const templateSource = apply( - url(options.actionCreators ? './creator-files' : './files'), + url(options.creators ? './creator-files' : './files'), [ options.spec ? noop() diff --git a/modules/schematics/src/action/schema.json b/modules/schematics/src/action/schema.json index 645105ad10..8e975f1e15 100644 --- a/modules/schematics/src/action/schema.json +++ b/modules/schematics/src/action/schema.json @@ -46,12 +46,12 @@ "Specifies if api success and failure actions should be generated.", "aliases": ["a"] }, - "actionCreators": { + "creators": { "type": "boolean", "default": false, "description": - "Specifies whether to generate action creators instead of action classes.", - "aliases": ["ac"] + "Specifies whether to use creator functions for handling actions and reducers.", + "aliases": ["c"] } }, "required": [] diff --git a/modules/schematics/src/action/schema.ts b/modules/schematics/src/action/schema.ts index 44aed0ec40..c90701e4a7 100644 --- a/modules/schematics/src/action/schema.ts +++ b/modules/schematics/src/action/schema.ts @@ -37,8 +37,8 @@ export interface Schema { api?: boolean; /** - * Specifies whether to generate action creators - * instead of action classes. + * Specifies whether to use creator functions for + * handling actions and reducers. */ - actionCreators?: boolean; + creators?: boolean; } diff --git a/modules/schematics/src/effect/files/__name@dasherize@if-flat__/__name@dasherize__.effects.ts.template b/modules/schematics/src/effect/files/__name@dasherize@if-flat__/__name@dasherize__.effects.ts.template index d0ecf1376c..b1bdf2c160 100644 --- a/modules/schematics/src/effect/files/__name@dasherize@if-flat__/__name@dasherize__.effects.ts.template +++ b/modules/schematics/src/effect/files/__name@dasherize@if-flat__/__name@dasherize__.effects.ts.template @@ -2,18 +2,18 @@ import { Injectable } from '@angular/core'; import { Actions, <%= effectMethod %><% if (feature) { %>, ofType<% } %> } from '@ngrx/effects'; <% if (feature && api) { %>import { catchError, map, concatMap } from 'rxjs/operators'; import { EMPTY, of } from 'rxjs'; -<% if (!effectCreators) {%>import { Load<%= classify(name) %>sFailure, Load<%= classify(name) %>sSuccess, <%= classify(name) %>ActionTypes, <%= classify(name) %>Actions } from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> -<% if (effectCreators) {%>import * as <%= classify(name) %>Actions from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> +<% if (!creators) {%>import { Load<%= classify(name) %>sFailure, Load<%= classify(name) %>sSuccess, <%= classify(name) %>ActionTypes, <%= classify(name) %>Actions } from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> +<% if (creators) {%>import * as <%= classify(name) %>Actions from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> <% } %> <% if (feature && !api) { %>import { concatMap } from 'rxjs/operators'; import { EMPTY } from 'rxjs'; -<% if (!effectCreators) {%>import { <%= classify(name) %>ActionTypes, <%= classify(name) %>Actions } from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> -<% if (effectCreators) {%>import * as <%= classify(name) %>Actions from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> +<% if (!creators) {%>import { <%= classify(name) %>ActionTypes, <%= classify(name) %>Actions } from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> +<% if (creators) {%>import * as <%= classify(name) %>Actions from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> <% } %> @Injectable() export class <%= classify(name) %>Effects { -<% if (feature && api && !effectCreators) { %> +<% if (feature && api && !creators) { %> <%= effectStart %> ofType(<%= classify(name) %>ActionTypes.Load<%= classify(name) %>s), concatMap(() => @@ -23,7 +23,7 @@ export class <%= classify(name) %>Effects { catchError(error => of(new Load<%= classify(name) %>sFailure({ error })))) ) <%= effectEnd %> -<% } else if (feature && api && effectCreators) { %> +<% } else if (feature && api && creators) { %> <%= effectStart %> ofType(<%= classify(name) %>Actions.load<%= classify(name) %>s), concatMap(() => @@ -34,22 +34,22 @@ export class <%= classify(name) %>Effects { ) <%= effectEnd %> <% } %> -<% if (feature && !api && !effectCreators) { %> +<% if (feature && !api && !creators) { %> <%= effectStart %> ofType(<%= classify(name) %>ActionTypes.Load<%= classify(name) %>s), /** An EMPTY observable only emits completion. Replace with your own observable API request */ concatMap(() => EMPTY) <%= effectEnd %> -<% } else if (feature && !api && effectCreators) { %> +<% } else if (feature && !api && creators) { %> <%= effectStart %> ofType(<%= classify(name) %>Actions.load<%= classify(name) %>s), /** An EMPTY observable only emits completion. Replace with your own observable API request */ concatMap(() => EMPTY) <%= effectEnd %> <% } %> -<% if (feature && !effectCreators) { %> +<% if (feature && !creators) { %> constructor(private actions$: Actions<<%= classify(name) %>Actions>) {} -<% } else if (feature && effectCreators) { %> +<% } else if (feature && creators) { %> constructor(private actions$: Actions) {} <% } else { %> constructor(private actions$: Actions) {} diff --git a/modules/schematics/src/effect/index.spec.ts b/modules/schematics/src/effect/index.spec.ts index cbdda37ce7..18b76f6385 100644 --- a/modules/schematics/src/effect/index.spec.ts +++ b/modules/schematics/src/effect/index.spec.ts @@ -27,7 +27,7 @@ describe('Effect Schematic', () => { feature: false, root: false, group: false, - effectCreators: false, + creators: false, }; const projectPath = getTestProjectPath(); @@ -319,7 +319,7 @@ describe('Effect Schematic', () => { }); it('should create an effect using creator function', () => { - const options = { ...defaultOptions, effectCreators: true, feature: true }; + const options = { ...defaultOptions, creators: true, feature: true }; const tree = schematicRunner.runSchematic('effect', options, appTree); const content = tree.readContent( @@ -334,8 +334,8 @@ describe('Effect Schematic', () => { ); }); - it('should use action creators when effectCreators is enabled in a feature', () => { - const options = { ...defaultOptions, effectCreators: true, feature: true }; + it('should use action creators when creators is enabled in a feature', () => { + const options = { ...defaultOptions, creators: true, feature: true }; const tree = schematicRunner.runSchematic('effect', options, appTree); const content = tree.readContent( @@ -352,7 +352,7 @@ describe('Effect Schematic', () => { it('should create an api effect using creator function', () => { const options = { ...defaultOptions, - effectCreators: true, + creators: true, api: true, feature: true, }; diff --git a/modules/schematics/src/effect/index.ts b/modules/schematics/src/effect/index.ts index 747cd3a2f0..2d1edb91aa 100644 --- a/modules/schematics/src/effect/index.ts +++ b/modules/schematics/src/effect/index.ts @@ -93,19 +93,19 @@ function addImportToNgModule(options: EffectOptions): Rule { }; } -function getEffectMethod(effectCreators?: boolean) { - return effectCreators ? 'createEffect' : 'Effect'; +function getEffectMethod(creators?: boolean) { + return creators ? 'createEffect' : 'Effect'; } -function getEffectStart(name: string, effectCreators?: boolean): string { +function getEffectStart(name: string, creators?: boolean): string { const effectName = stringUtils.classify(name); - return effectCreators + return creators ? `load${effectName}s$ = createEffect(() => this.actions$.pipe(` : '@Effect()\n' + ` load${effectName}s$ = this.actions$.pipe(`; } -function getEffectEnd(effectCreators?: boolean) { - return effectCreators ? '));' : ');'; +function getEffectEnd(creators?: boolean) { + return creators ? '));' : ');'; } export default function(options: EffectOptions): Rule { @@ -131,9 +131,9 @@ export default function(options: EffectOptions): Rule { options.flat ? '' : s, options.group ? 'effects' : '' ), - effectMethod: getEffectMethod(options.effectCreators), - effectStart: getEffectStart(options.name, options.effectCreators), - effectEnd: getEffectEnd(options.effectCreators), + effectMethod: getEffectMethod(options.creators), + effectStart: getEffectStart(options.name, options.creators), + effectEnd: getEffectEnd(options.creators), ...(options as object), } as any), move(parsedPath.path), diff --git a/modules/schematics/src/effect/schema.json b/modules/schematics/src/effect/schema.json index afde9e7136..179382169f 100644 --- a/modules/schematics/src/effect/schema.json +++ b/modules/schematics/src/effect/schema.json @@ -64,11 +64,12 @@ "Specifies if effect has api success and failure actions wired up", "aliases": ["a"] }, - "effectCreators": { + "creators": { "type": "boolean", "default": false, - "description": "Specifies whether to use the effect creators function", - "aliases": ["ec"] + "description": + "Specifies whether to use creator functions for handling actions and reducers.", + "aliases": ["c"] } }, "required": [] diff --git a/modules/schematics/src/effect/schema.ts b/modules/schematics/src/effect/schema.ts index 7fcde3167c..d09896a366 100644 --- a/modules/schematics/src/effect/schema.ts +++ b/modules/schematics/src/effect/schema.ts @@ -50,7 +50,8 @@ export interface Schema { api?: boolean; /** - * Specifies if the effect creation uses 'createEffect' + * Specifies whether to use creator functions for + * handling actions, reducers, and effects. */ - effectCreators?: boolean; + creators?: boolean; } diff --git a/modules/schematics/src/entity/creator-files/__name@dasherize@if-flat__/__name@dasherize@group-actions__.actions.ts.template b/modules/schematics/src/entity/creator-files/__name@dasherize@if-flat__/__name@dasherize@group-actions__.actions.ts.template index 37e114d0dd..421f79d253 100644 --- a/modules/schematics/src/entity/creator-files/__name@dasherize@if-flat__/__name@dasherize@group-actions__.actions.ts.template +++ b/modules/schematics/src/entity/creator-files/__name@dasherize@if-flat__/__name@dasherize@group-actions__.actions.ts.template @@ -1,29 +1,53 @@ -import { createAction, props, union } from '@ngrx/store'; +import { createAction, props } from '@ngrx/store'; import { Update } from '@ngrx/entity'; import { <%= classify(name) %> } from '<%= featurePath(group, flat, "models", dasherize(name)) %><%= dasherize(name) %>.model'; -export const load<%= classify(name) %>s = createAction('[<%= classify(name) %>/API] Load <%= classify(name) %>s', props<{ <%= camelize(name) %>s: <%= classify(name) %>[] }>()); -export const add<%= classify(name) %> = createAction('[<%= classify(name) %>/API] Add <%= classify(name) %>', props<{ <%= camelize(name) %>: <%= classify(name) %> }>()); -export const upsert<%= classify(name) %> = createAction('[<%= classify(name) %>/API] Upsert <%= classify(name) %>', props<{ <%= camelize(name) %>: <%= classify(name) %> }>()); -export const add<%= classify(name) %>s = createAction('[<%= classify(name) %>/API] Add <%= classify(name) %>s', props<{ <%= camelize(name) %>: <%= classify(name) %> }>()); -export const upsert<%= classify(name) %>s = createAction('[<%= classify(name) %>/API] Upsert <%= classify(name) %>s', props<{ <%= camelize(name) %>s: <%= classify(name) %>[] }>()); -export const update<%= classify(name) %> = createAction('[<%= classify(name) %>/API] Update <%= classify(name) %>', props<{ <%= camelize(name) %>: Update<<%= classify(name) %>> }>()); -export const update<%= classify(name) %>s = createAction('[<%= classify(name) %>/API] Update <%= classify(name) %>s', props<{ <%= camelize(name) %>s: Update<<%= classify(name) %>>[] }>()); -export const delete<%= classify(name) %> = createAction('[<%= classify(name) %>/API] Delete <%= classify(name) %>', props<{ id: string }>()); -export const delete<%= classify(name) %>s = createAction('[<%= classify(name) %>/API] Delete <%= classify(name) %>s', props<{ id: string[] }>()); -export const clear<%= classify(name) %>s = createAction('[<%= classify(name) %>/API] Clear <%= classify(name) %>s'); - -const all = union({ - load<%= classify(name) %>s, - add<%= classify(name) %>, - upsert<%= classify(name) %>, - add<%= classify(name) %>s, - upsert<%= classify(name) %>s, - update<%= classify(name) %>, - update<%= classify(name) %>s, - delete<%= classify(name) %>, - delete<%= classify(name) %>s, - clear<%= classify(name) %>s -}); -export type Union = typeof all; \ No newline at end of file +export const load<%= classify(name) %>s = createAction( + '[<%= classify(name) %>/API] Load <%= classify(name) %>s', + props<{ <%= camelize(name) %>s: <%= classify(name) %>[] }>() +); + +export const add<%= classify(name) %> = createAction( + '[<%= classify(name) %>/API] Add <%= classify(name) %>', + props<{ <%= camelize(name) %>: <%= classify(name) %> }>() +); + +export const upsert<%= classify(name) %> = createAction( + '[<%= classify(name) %>/API] Upsert <%= classify(name) %>', + props<{ <%= camelize(name) %>: <%= classify(name) %> }>() +); + +export const add<%= classify(name) %>s = createAction( + '[<%= classify(name) %>/API] Add <%= classify(name) %>s', + props<{ <%= camelize(name) %>: <%= classify(name) %> }>() +); + +export const upsert<%= classify(name) %>s = createAction( + '[<%= classify(name) %>/API] Upsert <%= classify(name) %>s', + props<{ <%= camelize(name) %>s: <%= classify(name) %>[] }>() +); + +export const update<%= classify(name) %> = createAction( + '[<%= classify(name) %>/API] Update <%= classify(name) %>', + props<{ <%= camelize(name) %>: Update<<%= classify(name) %>> }>() +); + +export const update<%= classify(name) %>s = createAction( + '[<%= classify(name) %>/API] Update <%= classify(name) %>s', + props<{ <%= camelize(name) %>s: Update<<%= classify(name) %>>[] }>() +); + +export const delete<%= classify(name) %> = createAction( + '[<%= classify(name) %>/API] Delete <%= classify(name) %>', + props<{ id: string }>() +); + +export const delete<%= classify(name) %>s = createAction( + '[<%= classify(name) %>/API] Delete <%= classify(name) %>s', + props<{ id: string[] }>() +); + +export const clear<%= classify(name) %>s = createAction( + '[<%= classify(name) %>/API] Clear <%= classify(name) %>s' +); diff --git a/modules/schematics/src/entity/creator-files/__name@dasherize@if-flat__/__name@dasherize@group-reducers__.reducer.ts.template b/modules/schematics/src/entity/creator-files/__name@dasherize@if-flat__/__name@dasherize@group-reducers__.reducer.ts.template index 56805cdfb7..e8ee5d5779 100644 --- a/modules/schematics/src/entity/creator-files/__name@dasherize@if-flat__/__name@dasherize@group-reducers__.reducer.ts.template +++ b/modules/schematics/src/entity/creator-files/__name@dasherize@if-flat__/__name@dasherize@group-reducers__.reducer.ts.template @@ -1,3 +1,4 @@ +import { Action, createReducer, on } from '@ngrx/store'; import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { <%= classify(name) %> } from '<%= featurePath(group, flat, "models", dasherize(name)) %><%= dasherize(name) %>.model'; import * as <%= classify(name) %>Actions from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions'; @@ -12,55 +13,41 @@ export const initialState: State = adapter.getInitialState({ // additional entity state properties }); -export function reducer( - state = initialState, - action: <%= classify(name) %>Actions.Union -): State { - switch (action.type) { - case <%= classify(name) %>Actions.add<%= classify(name) %>.type: { - return adapter.addOne(action.<%= camelize(name) %>, state); - } - - case <%= classify(name) %>Actions.upsert<%= classify(name) %>.type: { - return adapter.upsertOne(action.<%= camelize(name) %>, state); - } - - case <%= classify(name) %>Actions.add<%= classify(name) %>s.type: { - return adapter.addMany(action.<%= camelize(name) %>s, state); - } - - case <%= classify(name) %>Actions.upsert<%= classify(name) %>s.type: { - return adapter.upsertMany(action.<%= camelize(name) %>s, state); - } - - case <%= classify(name) %>Actions.update<%= classify(name) %>.type: { - return adapter.updateOne(action.<%= camelize(name) %>, state); - } - - case <%= classify(name) %>Actions.update<%= classify(name) %>s.type: { - return adapter.updateMany(action.<%= camelize(name) %>s, state); - } - - case <%= classify(name) %>Actions.delete<%= classify(name) %>.type: { - return adapter.removeOne(action.id, state); - } - - case <%= classify(name) %>Actions.delete<%= classify(name) %>s.type: { - return adapter.removeMany(action.ids, state); - } - - case <%= classify(name) %>Actions.load<%= classify(name) %>s.type: { - return adapter.addAll(action.<%= camelize(name) %>s, state); - } - - case <%= classify(name) %>Actions.clear<%= classify(name) %>s.type: { - return adapter.removeAll(state); - } - - default: { - return state; - } - } +const <%= camelize(name) %>Reducer = createReducer([ + on(<%= classify(name) %>Actions.add<%= classify(name) %>, + state => adapter.addOne(action.<%= camelize(name) %>, state) + ), + on(<%= classify(name) %>Actions.upsert<%= classify(name) %>, + state => adapter.upsertOne(action.<%= camelize(name) %>, state) + ), + on(<%= classify(name) %>Actions.add<%= classify(name) %>s, + state => adapter.addMany(action.<%= camelize(name) %>s, state) + ), + on(<%= classify(name) %>Actions.upsert<%= classify(name) %>s, + state => adapter.upsertMany(action.<%= camelize(name) %>s, state) + ), + on(<%= classify(name) %>Actions.update<%= classify(name) %>, + state => adapter.updateOne(action.<%= camelize(name) %>, state) + ), + on(<%= classify(name) %>Actions.update<%= classify(name) %>s, + state => adapter.updateMany(action.<%= camelize(name) %>s, state) + ), + on(<%= classify(name) %>Actions.delete<%= classify(name) %>, + state => adapter.removeOne(action.id, state) + ), + on(<%= classify(name) %>Actions.delete<%= classify(name) %>s, + state => adapter.removeMany(action.ids, state) + ), + on(<%= classify(name) %>Actions.load<%= classify(name) %>s, + state => adapter.addAll(action.<%= camelize(name) %>s, state) + ), + on(<%= classify(name) %>Actions.clear<%= classify(name) %>s, + state => adapter.removeAll(state) + ), +], initialState); + +export function reducer(state: State | undefined, action: Action) { + return <%= camelize(name) %>Reducer(state, action); } export const { diff --git a/modules/schematics/src/entity/index.spec.ts b/modules/schematics/src/entity/index.spec.ts index b7d99f18fb..7b2c41e2d6 100644 --- a/modules/schematics/src/entity/index.spec.ts +++ b/modules/schematics/src/entity/index.spec.ts @@ -193,7 +193,7 @@ describe('Entity Schematic', () => { }); describe('action creators', () => { - const creatorOptions = { ...defaultOptions, actionCreators: true }; + const creatorOptions = { ...defaultOptions, creators: true }; it('should create a const for the action creator', () => { const tree = schematicRunner.runSchematic( @@ -204,9 +204,9 @@ describe('Entity Schematic', () => { const fileContent = tree.readContent( `${projectPath}/src/app/foo.actions.ts` ); - expect(fileContent).toMatch( - /export const loadFoos = createAction\(\'\[Foo\/API\] Load Foos\', props<{ foos: Foo\[\] }>\(\)\);/ - ); + expect(fileContent).toMatch(/export const loadFoos = createAction\(/); + expect(fileContent).toMatch(/\[Foo\/API\] Load Foos\'/); + expect(fileContent).toMatch(/props\<\{ foos\: Foo\[\] }>\(\)/); }); it('should use action creator types in the reducer', () => { @@ -221,7 +221,10 @@ describe('Entity Schematic', () => { expect(fileContent).toMatch( /import \* as FooActions from \'\.\/foo.actions\';/ ); - expect(fileContent).toMatch(/case FooActions\.addFoo\.type: \{/); + expect(fileContent).toMatch(/on\(FooActions.addFoo,/); + expect(fileContent).toMatch( + /state => adapter\.addOne\(action.foo, state\)/ + ); }); }); }); diff --git a/modules/schematics/src/entity/index.ts b/modules/schematics/src/entity/index.ts index c6dc81ed86..8e069d4de9 100644 --- a/modules/schematics/src/entity/index.ts +++ b/modules/schematics/src/entity/index.ts @@ -57,7 +57,7 @@ export default function(options: EntityOptions): Rule { ]); const templateSource = apply( - url(options.actionCreators ? './creator-files' : './files'), + url(options.creators ? './creator-files' : './files'), [applyTemplates(templateOptions), move(parsedPath.path)] ); diff --git a/modules/schematics/src/entity/schema.json b/modules/schematics/src/entity/schema.json index ed372355cc..074c4042cc 100644 --- a/modules/schematics/src/entity/schema.json +++ b/modules/schematics/src/entity/schema.json @@ -50,12 +50,12 @@ "Group actions, reducers and effects within relative subfolders", "aliases": ["g"] }, - "actionCreators": { + "creators": { "type": "boolean", "default": false, "description": - "Specifies whether to generate action creators instead of action classes.", - "aliases": ["ac"] + "Specifies whether to use creator functions for handling actions and reducers.", + "aliases": ["c"] } }, "required": [] diff --git a/modules/schematics/src/entity/schema.ts b/modules/schematics/src/entity/schema.ts index af6462d24a..f0ff247850 100644 --- a/modules/schematics/src/entity/schema.ts +++ b/modules/schematics/src/entity/schema.ts @@ -38,8 +38,8 @@ export interface Schema { group?: boolean; /** - * Specifies whether to generate action creators - * instead of action classes. + * Specifies whether to use creator functions for + * handling actions and reducers. */ - actionCreators?: boolean; + creators?: boolean; } diff --git a/modules/schematics/src/feature/index.ts b/modules/schematics/src/feature/index.ts index 6dee068d99..b049c854ff 100644 --- a/modules/schematics/src/feature/index.ts +++ b/modules/schematics/src/feature/index.ts @@ -18,7 +18,7 @@ export default function(options: FeatureOptions): Rule { project: options.project, spec: false, api: options.api, - actionCreators: options.creators, + creators: options.creators, }), schematic('reducer', { flat: options.flat, @@ -31,7 +31,7 @@ export default function(options: FeatureOptions): Rule { reducers: options.reducers, feature: true, api: options.api, - actionCreators: options.creators, + creators: options.creators, }), schematic('effect', { flat: options.flat, @@ -43,7 +43,7 @@ export default function(options: FeatureOptions): Rule { spec: options.spec, feature: true, api: options.api, - effectCreators: options.creators, + creators: options.creators, }), ])(host, context); }; diff --git a/modules/schematics/src/feature/schema.json b/modules/schematics/src/feature/schema.json index 11b7d7be1f..14e4093e35 100644 --- a/modules/schematics/src/feature/schema.json +++ b/modules/schematics/src/feature/schema.json @@ -61,7 +61,7 @@ "type": "boolean", "default": false, "description": - "Specifies if the effects and actions be created using creator functions", + "Specifies if the actions, reducers, and effects should be created using creator functions", "aliases": ["c"] } }, diff --git a/modules/schematics/src/feature/schema.ts b/modules/schematics/src/feature/schema.ts index a6f6ceecb6..d7bf257fef 100644 --- a/modules/schematics/src/feature/schema.ts +++ b/modules/schematics/src/feature/schema.ts @@ -46,7 +46,7 @@ export interface Schema { api?: boolean; /** - * Specifies whether to use creator functions for actions and effects. + * Specifies whether to use creator functions for actions, reducers, and effects. */ creators?: boolean; } diff --git a/modules/schematics/src/reducer/creator-files/__name@dasherize@if-flat__/__name@dasherize__.reducer.ts.template b/modules/schematics/src/reducer/creator-files/__name@dasherize@if-flat__/__name@dasherize__.reducer.ts.template index e70d1dd688..79fd8e8189 100644 --- a/modules/schematics/src/reducer/creator-files/__name@dasherize@if-flat__/__name@dasherize__.reducer.ts.template +++ b/modules/schematics/src/reducer/creator-files/__name@dasherize@if-flat__/__name@dasherize__.reducer.ts.template @@ -1,4 +1,4 @@ -<% if(!feature) { %>import { Action } from '@ngrx/store';<% } %> +import { Action, createReducer, on } from '@ngrx/store'; <% if(feature) { %>import * as <%= classify(name) %>Actions from '<%= featurePath(group, flat, "actions", dasherize(name)) %><%= dasherize(name) %>.actions';<% } %> export interface State { @@ -9,19 +9,14 @@ export const initialState: State = { }; -export function reducer(state = initialState, action: <% if(feature) { %><%= classify(name) %>Actions.Union<% } else { %>Action<% } %>): State { - switch (action.type) { +const <%= camelize(name) %>Reducer = createReducer([ <% if(feature) { %> - case <%= classify(name) %>Actions.load<%= classify(name) %>s.type: - return state; -<% if(api) { %> - case <%= classify(name) %>Actions.load<%= classify(name) %>sSuccess.type: - return state; - - case <%= classify(name) %>Actions.load<%= classify(name) %>sFailure.type: - return state; + on(<%= classify(name) %>Actions.load<%= classify(name) %>s, state => state), +<% if(api) { %> on(<%= classify(name) %>Actions.load<%= classify(name) %>sSuccess, state => state), + on(<%= classify(name) %>Actions.load<%= classify(name) %>sFailure, state => state), <% } %><% } %> - default: - return state; - } +], initialState); + +export function reducer(state: State | undefined, action: Action) { + return <%= camelize(name) %>Reducer(state, action); } diff --git a/modules/schematics/src/reducer/index.spec.ts b/modules/schematics/src/reducer/index.spec.ts index f060c579d3..54201c34dd 100644 --- a/modules/schematics/src/reducer/index.spec.ts +++ b/modules/schematics/src/reducer/index.spec.ts @@ -188,4 +188,59 @@ describe('Reducer Schematic', () => { expect(fileContent).toMatch(/case FooActionTypes\.LoadFoosFailure/); expect(fileContent).not.toMatch(/import { Action } from '@ngrx\/store'/); }); + + it('should create an reducer function using createReducer', () => { + const tree = schematicRunner.runSchematic( + 'reducer', + { ...defaultOptions, creators: true }, + appTree + ); + const fileContent = tree.readContent( + `${projectPath}/src/app/foo.reducer.ts` + ); + + expect(fileContent).toMatch( + /export function reducer\(state: State | undefined, action: Action\) {/ + ); + expect(fileContent).toMatch(/const fooReducer = createReducer\(/); + }); + + it('should create an reducer function in a feature using createReducer', () => { + const tree = schematicRunner.runSchematic( + 'reducer', + { ...defaultOptions, creators: true, feature: true }, + appTree + ); + const fileContent = tree.readContent( + `${projectPath}/src/app/foo.reducer.ts` + ); + + expect(fileContent).toMatch( + /export function reducer\(state: State | undefined, action: Action\) {/ + ); + expect(fileContent).toMatch(/const fooReducer = createReducer\(/); + expect(fileContent).toMatch(/on\(FooActions.loadFoos, state => state\)/); + }); + + it('should create an reducer function in an api feature using createReducer', () => { + const tree = schematicRunner.runSchematic( + 'reducer', + { ...defaultOptions, creators: true, feature: true, api: true }, + appTree + ); + const fileContent = tree.readContent( + `${projectPath}/src/app/foo.reducer.ts` + ); + + expect(fileContent).toMatch( + /export function reducer\(state: State | undefined, action: Action\) {/ + ); + expect(fileContent).toMatch(/const fooReducer = createReducer\(/); + expect(fileContent).toMatch( + /on\(FooActions.loadFoosSuccess, state => state\)/ + ); + expect(fileContent).toMatch( + /on\(FooActions.loadFoosFailure, state => state\)/ + ); + }); }); diff --git a/modules/schematics/src/reducer/index.ts b/modules/schematics/src/reducer/index.ts index 81d9f12081..d16f7ea301 100644 --- a/modules/schematics/src/reducer/index.ts +++ b/modules/schematics/src/reducer/index.ts @@ -56,7 +56,7 @@ export default function(options: ReducerOptions): Rule { ]); const templateSource = apply( - url(options.actionCreators ? './creator-files' : './files'), + url(options.creators ? './creator-files' : './files'), [applyTemplates(templateOptions), move(parsedPath.path)] ); diff --git a/modules/schematics/src/reducer/schema.json b/modules/schematics/src/reducer/schema.json index 3a1ab03e87..d2eeb56841 100644 --- a/modules/schematics/src/reducer/schema.json +++ b/modules/schematics/src/reducer/schema.json @@ -61,12 +61,12 @@ "Specifies if api success and failure actions should be added to the reducer", "aliases": ["a"] }, - "actionCreators": { + "creators": { "type": "boolean", "default": false, "description": - "Specifies whether to generate action creators instead of action classes.", - "aliases": ["ac"] + "Specifies whether to use creator functions for handling actions and reducers.", + "aliases": ["c"] } }, "required": [] diff --git a/modules/schematics/src/reducer/schema.ts b/modules/schematics/src/reducer/schema.ts index 735daf27b9..a564d5d7d1 100644 --- a/modules/schematics/src/reducer/schema.ts +++ b/modules/schematics/src/reducer/schema.ts @@ -51,8 +51,8 @@ export interface Schema { api?: boolean; /** - * Specifies whether to generate action creators - * instead of action classes. + * Specifies whether to use creator functions for + * handling actions and reducers. */ - actionCreators?: boolean; + creators?: boolean; } diff --git a/projects/ngrx.io/content/guide/schematics/action.md b/projects/ngrx.io/content/guide/schematics/action.md index 25351d67fd..3fae3ec913 100644 --- a/projects/ngrx.io/content/guide/schematics/action.md +++ b/projects/ngrx.io/content/guide/schematics/action.md @@ -27,6 +27,13 @@ Provide the project name where the action files will be created. - Alias: `-p` - Type: `string` +Use creator functions for actions + +- `--creators` + - Alias: `-c` + - Type: `boolean` + - Default: `false` + Nest the actions file within a folder based on the action `name`. - `--flat` diff --git a/projects/ngrx.io/content/guide/schematics/effect.md b/projects/ngrx.io/content/guide/schematics/effect.md index 5fea3ce5c0..ef2dbd0fed 100644 --- a/projects/ngrx.io/content/guide/schematics/effect.md +++ b/projects/ngrx.io/content/guide/schematics/effect.md @@ -26,12 +26,12 @@ Provide the project name where the effect files will be created. - Alias: `-p` - Type: `string` -Use effect creators to register effects instead of the `Effect` decorator. +Use creator functions for actions and effects. -- `--effectCreators` - - Alias: `-ec` +- `--creators` + - Alias: `-c` - Type: `boolean` - - Default: `true` + - Default: `false` Nest the effects file within a folder based by the effect `name`. diff --git a/projects/ngrx.io/content/guide/schematics/entity.md b/projects/ngrx.io/content/guide/schematics/entity.md index 7f518100b5..c7a92899fe 100644 --- a/projects/ngrx.io/content/guide/schematics/entity.md +++ b/projects/ngrx.io/content/guide/schematics/entity.md @@ -26,6 +26,13 @@ Provide the project name where the entity files will be created. - Alias: `-p` - Type: `string` +Use creator functions for actions, reducers, and effects. + +- `--creators` + - Alias: `-c` + - Type: `boolean` + - Default: `false` + Nest the effects file within a folder based on the entity `name`. - `--flat` diff --git a/projects/ngrx.io/content/guide/schematics/feature.md b/projects/ngrx.io/content/guide/schematics/feature.md index 9ef813b4c0..49df4dad31 100644 --- a/projects/ngrx.io/content/guide/schematics/feature.md +++ b/projects/ngrx.io/content/guide/schematics/feature.md @@ -26,7 +26,7 @@ Provide the project name where the feature files will be created. - Alias: `-p` - Type: `string` -Use creator functions for actions and effects. +Use creator functions for actions, reducers, and effects. - `--creators` - Alias: `-c` diff --git a/projects/ngrx.io/content/guide/schematics/reducer.md b/projects/ngrx.io/content/guide/schematics/reducer.md index 3f40776241..edb5255d18 100644 --- a/projects/ngrx.io/content/guide/schematics/reducer.md +++ b/projects/ngrx.io/content/guide/schematics/reducer.md @@ -27,6 +27,13 @@ Provide the project name where the reducer files will be created. - Alias: `-p` - Type: `string` +Use creator functions to generate the actions and reducer. + +- `--creators` + - Alias: `-c` + - Type: `boolean` + - Default: `false` + Nest the reducer file within a folder based on the reducer `name`. - `--flat`