or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

action-generation.mdcomponent-store.mdcontainer-components.mddata-services.mdeffect-generation.mdentity-management.mdfeature-generation.mdindex.mdngrx-push-migration.mdreducer-generation.mdselector-generation.mdstore-setup.mdutility-functions.md

feature-generation.mddocs/

0

# Feature Generation

1

2

NgRx feature generation schematic that creates complete feature modules with actions, reducers, effects, and selectors in a single command. This schematic provides a convenient way to scaffold an entire state management feature with all necessary NgRx components.

3

4

## Capabilities

5

6

### Feature Schematic

7

8

Generates a complete NgRx feature with all related state management files and proper module registration.

9

10

```bash

11

# Basic feature generation

12

ng generate @ngrx/schematics:feature User

13

14

# Feature with API actions

15

ng generate @ngrx/schematics:feature User --api

16

17

# Feature with creator functions

18

ng generate @ngrx/schematics:feature User --creators

19

```

20

21

```typescript { .api }

22

/**

23

* Feature schematic configuration interface

24

*/

25

interface FeatureSchema {

26

/** Name of the feature (typically entity or domain name) */

27

name: string;

28

/** Path where feature files should be generated */

29

path?: string;

30

/** Angular project to target */

31

project?: string;

32

/** Generate files without creating a folder */

33

flat?: boolean;

34

/** Group feature files within folders */

35

group?: boolean;

36

/** Module file to register feature in */

37

module?: string;

38

/** Path to existing reducers file */

39

reducers?: string;

40

/** Generate as feature state */

41

feature?: boolean;

42

/** Use creator functions */

43

creators?: boolean;

44

/** Generate API success and failure actions */

45

api?: boolean;

46

}

47

```

48

49

### Complete Feature Structure

50

51

The feature schematic generates a comprehensive set of files for state management:

52

53

```

54

src/app/user/

55

├── user.actions.ts # Action definitions

56

├── user.reducer.ts # Reducer with state interface

57

├── user.effects.ts # Effects for side effects

58

├── user.selectors.ts # Selectors for state access

59

└── index.ts # Barrel exports

60

```

61

62

**Usage Examples:**

63

64

```bash

65

# Generate complete user feature

66

ng generate @ngrx/schematics:feature User --creators --api

67

68

# Generate product feature with custom path

69

ng generate @ngrx/schematics:feature Product --path=src/app/catalog --creators

70

71

# Generate feature and register in specific module

72

ng generate @ngrx/schematics:feature Order --module=order/order.module.ts

73

```

74

75

### Generated Actions

76

77

Creates comprehensive action definitions with optional API patterns:

78

79

```typescript

80

// Generated feature actions

81

import { createAction, props } from '@ngrx/store';

82

83

// Load actions

84

export const loadUsers = createAction('[User/API] Load Users');

85

86

export const loadUsersSuccess = createAction(

87

'[User/API] Load Users Success',

88

props<{ users: User[] }>()

89

);

90

91

export const loadUsersFailure = createAction(

92

'[User/API] Load Users Failure',

93

props<{ error: any }>()

94

);

95

96

// CRUD actions (when --api flag is used)

97

export const createUser = createAction(

98

'[User/API] Create User',

99

props<{ user: User }>()

100

);

101

102

export const createUserSuccess = createAction(

103

'[User/API] Create User Success',

104

props<{ user: User }>()

105

);

106

107

export const createUserFailure = createAction(

108

'[User/API] Create User Failure',

109

props<{ error: any }>()

110

);

111

112

// Selection actions

113

export const selectUser = createAction(

114

'[User] Select User',

115

props<{ userId: string }>()

116

);

117

118

export const clearSelection = createAction('[User] Clear Selection');

119

```

120

121

### Generated Reducer

122

123

Creates a complete reducer with state interface and action handlers:

124

125

```typescript

126

// Generated feature reducer

127

import { createReducer, on } from '@ngrx/store';

128

import * as UserActions from './user.actions';

129

130

export const userFeatureKey = 'user';

131

132

export interface UserState {

133

users: User[];

134

loading: boolean;

135

error: string | null;

136

selectedUserId: string | null;

137

}

138

139

export const initialState: UserState = {

140

users: [],

141

loading: false,

142

error: null,

143

selectedUserId: null

144

};

145

146

export const userReducer = createReducer(

147

initialState,

148

149

on(UserActions.loadUsers, (state) => ({

150

...state,

151

loading: true,

152

error: null

153

})),

154

155

on(UserActions.loadUsersSuccess, (state, { users }) => ({

156

...state,

157

users,

158

loading: false

159

})),

160

161

on(UserActions.loadUsersFailure, (state, { error }) => ({

162

...state,

163

loading: false,

164

error

165

})),

166

167

on(UserActions.selectUser, (state, { userId }) => ({

168

...state,

169

selectedUserId: userId

170

})),

171

172

on(UserActions.clearSelection, (state) => ({

173

...state,

174

selectedUserId: null

175

}))

176

);

177

```

178

179

### Generated Effects

180

181

Creates effects for handling side effects and API calls:

182

183

```typescript

184

// Generated feature effects

185

import { Injectable } from '@angular/core';

186

import { Actions, createEffect, ofType } from '@ngrx/effects';

187

import { catchError, map, switchMap } from 'rxjs/operators';

188

import { of } from 'rxjs';

189

import * as UserActions from './user.actions';

190

import { UserService } from './user.service';

191

192

@Injectable()

193

export class UserEffects {

194

195

loadUsers$ = createEffect(() =>

196

this.actions$.pipe(

197

ofType(UserActions.loadUsers),

198

switchMap(() =>

199

this.userService.getUsers().pipe(

200

map(users => UserActions.loadUsersSuccess({ users })),

201

catchError(error => of(UserActions.loadUsersFailure({ error })))

202

)

203

)

204

)

205

);

206

207

createUser$ = createEffect(() =>

208

this.actions$.pipe(

209

ofType(UserActions.createUser),

210

switchMap(({ user }) =>

211

this.userService.createUser(user).pipe(

212

map(createdUser => UserActions.createUserSuccess({ user: createdUser })),

213

catchError(error => of(UserActions.createUserFailure({ error })))

214

)

215

)

216

)

217

);

218

219

constructor(

220

private actions$: Actions,

221

private userService: UserService

222

) {}

223

}

224

```

225

226

### Generated Selectors

227

228

Creates comprehensive selectors for accessing feature state:

229

230

```typescript

231

// Generated feature selectors

232

import { createFeatureSelector, createSelector } from '@ngrx/store';

233

import { UserState } from './user.reducer';

234

235

export const selectUserState = createFeatureSelector<UserState>('user');

236

237

export const selectUsers = createSelector(

238

selectUserState,

239

(state: UserState) => state.users

240

);

241

242

export const selectUsersLoading = createSelector(

243

selectUserState,

244

(state: UserState) => state.loading

245

);

246

247

export const selectUsersError = createSelector(

248

selectUserState,

249

(state: UserState) => state.error

250

);

251

252

export const selectSelectedUserId = createSelector(

253

selectUserState,

254

(state: UserState) => state.selectedUserId

255

);

256

257

export const selectSelectedUser = createSelector(

258

selectUsers,

259

selectSelectedUserId,

260

(users, selectedId) => users.find(user => user.id === selectedId)

261

);

262

```

263

264

### Module Integration

265

266

The feature schematic automatically registers the feature in the specified module:

267

268

```typescript

269

// Module registration

270

import { StoreModule } from '@ngrx/store';

271

import { EffectsModule } from '@ngrx/effects';

272

import * as fromUser from './user.reducer';

273

import { UserEffects } from './user.effects';

274

275

@NgModule({

276

imports: [

277

StoreModule.forFeature(fromUser.userFeatureKey, fromUser.userReducer),

278

EffectsModule.forFeature([UserEffects])

279

]

280

})

281

export class UserModule {}

282

```

283

284

### Barrel Exports

285

286

Creates comprehensive barrel exports for easy importing:

287

288

```typescript

289

// Generated index.ts

290

export * from './user.actions';

291

export * from './user.reducer';

292

export * from './user.effects';

293

export * from './user.selectors';

294

```

295

296

### Creator Functions Support

297

298

When using `--creators` flag, all generated code uses NgRx creator functions:

299

300

```typescript { .api }

301

/**

302

* Creator function patterns used in generated code

303

*/

304

interface CreatorPatterns {

305

/** Actions with createAction */

306

actions: 'createAction(type, props<PayloadType>())';

307

/** Reducers with createReducer */

308

reducers: 'createReducer(initialState, on(action, reducer))';

309

/** Effects with createEffect */

310

effects: 'createEffect(() => source$)';

311

/** Selectors with createSelector */

312

selectors: 'createSelector(input, projector)';

313

}

314

```

315

316

### Feature Configuration

317

318

The generated feature includes proper TypeScript configuration and type safety:

319

320

```typescript

321

// Type-safe feature configuration

322

export interface AppState {

323

[fromUser.userFeatureKey]: fromUser.UserState;

324

}

325

326

// Feature key constant

327

export const userFeatureKey = 'user';

328

329

// State interface

330

export interface UserState {

331

users: User[];

332

loading: boolean;

333

error: string | null;

334

selectedUserId: string | null;

335

}

336

```

337

338

### Testing Setup

339

340

Generated features include testing utilities and examples:

341

342

```typescript

343

// Feature testing setup

344

describe('User Feature', () => {

345

let store: MockStore;

346

let effects: UserEffects;

347

let service: jasmine.SpyObj<UserService>;

348

349

beforeEach(() => {

350

TestBed.configureTestingModule({

351

providers: [

352

provideMockStore({ initialState }),

353

UserEffects,

354

{

355

provide: UserService,

356

useValue: jasmine.createSpyObj('UserService', ['getUsers'])

357

}

358

]

359

});

360

361

store = TestBed.inject(MockStore);

362

effects = TestBed.inject(UserEffects);

363

service = TestBed.inject(UserService) as jasmine.SpyObj<UserService>;

364

});

365

366

it('should load users', () => {

367

const users = [{ id: '1', name: 'John' }];

368

store.dispatch(UserActions.loadUsers());

369

370

store.select(selectUsers).subscribe(result => {

371

expect(result).toEqual(users);

372

});

373

});

374

});

375

```

376

377

### Performance Considerations

378

379

Generated features include performance optimizations:

380

381

```typescript

382

// Optimized state updates

383

on(UserActions.updateUserSuccess, (state, { user }) => {

384

const index = state.users.findIndex(u => u.id === user.id);

385

if (index === -1) return state;

386

387

const users = [...state.users];

388

users[index] = user;

389

390

return { ...state, users };

391

});

392

393

// Memoized selectors

394

export const selectUserById = (id: string) => createSelector(

395

selectUsers,

396

(users) => users.find(user => user.id === id)

397

);

398

```