@barcidev/ngx-autogen 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.es.md +366 -0
- package/README.md +366 -0
- package/package.json +61 -0
- package/src/collection.json +15 -0
- package/src/ng-add/index.d.ts +3 -0
- package/src/ng-add/index.js +77 -0
- package/src/ng-add/schema.d.ts +3 -0
- package/src/ng-add/schema.json +14 -0
- package/src/ngrx/store/files/entity/entity.model.ts.template +22 -0
- package/src/ngrx/store/files/store/__name@dasherize__.model.ts.template +14 -0
- package/src/ngrx/store/files/store/__name@dasherize__.service.ts.template +29 -0
- package/src/ngrx/store/files/store/__name@dasherize__.store.ts.template +212 -0
- package/src/ngrx/store/index.d.ts +3 -0
- package/src/ngrx/store/index.js +121 -0
- package/src/ngrx/store/schema.d.ts +6 -0
- package/src/ngrx/store/schema.json +30 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jorge Barcinilla
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.es.md
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
# ngx-autogen
|
|
2
|
+
|
|
3
|
+
[](README.md)
|
|
4
|
+
[](README.es.md)
|
|
5
|
+
|
|
6
|
+
**ngx-autogen** es un conjunto de schematics diseñados para optimizar y estandarizar el flujo de trabajo en proyectos Angular. Esta librería proporciona herramientas de generación de código que siguen las mejores prácticas, permitiendo a los desarrolladores ahorrar tiempo en tareas repetitivas y configuración de arquitectura.
|
|
7
|
+
|
|
8
|
+
## 🚀 Características
|
|
9
|
+
|
|
10
|
+
El proyecto se lanza inicialmente con un enfoque en la gestión de estado, pero está diseñado para crecer:
|
|
11
|
+
|
|
12
|
+
- **Store Schematic**: Nuestro primer schematic disponible. Permite generar automáticamente toda la estructura necesaria para un store basado en signals (NGRX-Signals), facilitando la integración rápida y escalable de la gestión de estado en tus aplicaciones.
|
|
13
|
+
|
|
14
|
+
## 📅 Próximamente
|
|
15
|
+
|
|
16
|
+
**ngx-autogen** es un proyecto en evolución continua. Se irán agregando progresivamente nuevas herramientas y schematics para cubrir más aspectos del desarrollo en Angular, como:
|
|
17
|
+
|
|
18
|
+
- Generación de servicios y utilidades.
|
|
19
|
+
- Scaffolding para componentes avanzados.
|
|
20
|
+
|
|
21
|
+
## 📦 Instalación
|
|
22
|
+
|
|
23
|
+
Puedes instalar el paquete en tu proyecto Angular mediante angular cli para que se configure automáticamente el proyecto con las dependencias necesarias:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
ng add ngx-autogen
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 🛠️ Uso
|
|
30
|
+
|
|
31
|
+
### Generar un Store
|
|
32
|
+
|
|
33
|
+
#### Propiedades
|
|
34
|
+
|
|
35
|
+
- `name`(obligatorio): nombre del store.
|
|
36
|
+
- `pk`(opcional): nombre de la primary key, si no se especifica se usara la especificada en el proceso de instalacion del schematic, de lo contrario se usara `id`.
|
|
37
|
+
- `path`(opcional): ruta del store, si no se especifica se usara la especificada en el proceso de instalacion del schematic, de lo contrario se usara `src/app/core`.
|
|
38
|
+
|
|
39
|
+
#### Ejemplo
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
ng g app-store --name="user" --pk="cod"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Esto creará los archivos `user.model.ts`, `user.service.ts`, `user.store.ts` dentro de la carpeta `src/app/core/user`, el archivo `entity.model.ts` si no existe, dentro de la carpeta `src/app/core/common/entity` y el archivo `index.ts` dentro de la carpeta `src/app/core`.
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
common/
|
|
49
|
+
└── entity/
|
|
50
|
+
└── entity.model.ts
|
|
51
|
+
user/
|
|
52
|
+
├── user.service.ts
|
|
53
|
+
├── user.model.ts
|
|
54
|
+
└── user.store.ts
|
|
55
|
+
index.ts
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
El archivo `index.ts` exportará todo lo necesario para que el store pueda ser importado y utilizado en cualquier parte de la aplicación.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
/* USER */
|
|
62
|
+
export * from './user/user.model';
|
|
63
|
+
export * from './user/user.service';
|
|
64
|
+
export * from './user/user.store';
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
El archivo `entity.model.ts` contiene las interfaces y tipos necesarios para el manejo de estados y formularios.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
import { FormControl } from '@angular/forms';
|
|
71
|
+
|
|
72
|
+
export interface EntityStatus {
|
|
73
|
+
addError?: Error | null;
|
|
74
|
+
addLoading?: boolean;
|
|
75
|
+
removeError?: Error | null;
|
|
76
|
+
removeLoading?: boolean;
|
|
77
|
+
error: Error | null;
|
|
78
|
+
idsRemoving?: (number | string)[];
|
|
79
|
+
idSelected?: null | number | string;
|
|
80
|
+
idsUpdating?: (number | string)[];
|
|
81
|
+
loaded: boolean;
|
|
82
|
+
loading: boolean;
|
|
83
|
+
selectedError?: Error | null;
|
|
84
|
+
selectedLoading?: boolean;
|
|
85
|
+
updateError?: Error | null;
|
|
86
|
+
updateLoading?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type FormGroupType<T> = {
|
|
90
|
+
[K in keyof T]: FormControl<T[K]>;
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
El archivo `user.model.ts` contiene la interface del modelo de datos.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
import { FormGroupType } from '../common/form/form.model';
|
|
98
|
+
|
|
99
|
+
export interface AddUser {
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export type AddUserForm = FormGroupType<AddUser>;
|
|
103
|
+
|
|
104
|
+
export interface UserDto {
|
|
105
|
+
cod: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type UpdateUser = Partial<UserDto> & Pick<UserDto, 'cod'>;
|
|
109
|
+
|
|
110
|
+
export interface UserRequest{}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
El archivo `user.service.ts` contiene el servicio que se encarga de la lógica de negocio.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
import { Injectable } from '@angular/core';
|
|
117
|
+
import { Observable, of } from 'rxjs';
|
|
118
|
+
import {
|
|
119
|
+
AddUser,
|
|
120
|
+
UserDto,
|
|
121
|
+
UpdateUser
|
|
122
|
+
} from './user.model';
|
|
123
|
+
|
|
124
|
+
@Injectable({
|
|
125
|
+
providedIn: 'root'
|
|
126
|
+
})
|
|
127
|
+
export class UserService {
|
|
128
|
+
|
|
129
|
+
addUser$(entity: AddUser): Observable<number> {
|
|
130
|
+
return of(0);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
removeUser$(cod: number): Observable<boolean> {
|
|
134
|
+
return of(true);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
getUsers$(): Observable<UserDto[]> {
|
|
138
|
+
return of([]);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
updateUser$(entity: UpdateUser): Observable<boolean> {
|
|
142
|
+
return of(true);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
El archivo `user.store.ts` contiene el store que se encarga de la gestión de estado.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
import { computed, inject } from '@angular/core';
|
|
151
|
+
import { patchState, signalStore, type, withComputed, withHooks, withMethods, withState } from '@ngrx/signals';
|
|
152
|
+
import {
|
|
153
|
+
addEntity,
|
|
154
|
+
entityConfig,
|
|
155
|
+
removeEntity,
|
|
156
|
+
setAllEntities,
|
|
157
|
+
updateEntity,
|
|
158
|
+
withEntities
|
|
159
|
+
} from '@ngrx/signals/entities';
|
|
160
|
+
import { rxMethod } from '@ngrx/signals/rxjs-interop';
|
|
161
|
+
import { catchError, of, pipe, switchMap, tap } from 'rxjs';
|
|
162
|
+
|
|
163
|
+
import { EntityStatus } from '../common/entity/entity.model';
|
|
164
|
+
import {
|
|
165
|
+
UserDto,
|
|
166
|
+
AddUser,
|
|
167
|
+
UpdateUser
|
|
168
|
+
} from './user.model';
|
|
169
|
+
import { UserService } from './user.service';
|
|
170
|
+
|
|
171
|
+
const initialStatus: EntityStatus = {
|
|
172
|
+
error: null,
|
|
173
|
+
loaded: false,
|
|
174
|
+
loading: false,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const config = entityConfig({
|
|
178
|
+
entity: type<UserDto>(),
|
|
179
|
+
selectId: (entity) => entity.cod,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
export const UserStore = signalStore(
|
|
183
|
+
withEntities(config),
|
|
184
|
+
withState({
|
|
185
|
+
_status: initialStatus,
|
|
186
|
+
}),
|
|
187
|
+
withComputed(({ entityMap, _status }) => ({
|
|
188
|
+
users: computed(() => Object.values(entityMap())),
|
|
189
|
+
userSeleccionado: computed(() => {
|
|
190
|
+
const cod = _status().idSelected;
|
|
191
|
+
return cod ? entityMap()[cod] : null;
|
|
192
|
+
}),
|
|
193
|
+
error: computed(() => _status().error),
|
|
194
|
+
loaded: computed(() => _status().loaded),
|
|
195
|
+
loading: computed(() => _status().loading),
|
|
196
|
+
loadingRemove: computed(
|
|
197
|
+
() => (cod?: number) =>
|
|
198
|
+
(cod ? _status().idsRemoving?.includes(cod) : _status().removeLoading) || false
|
|
199
|
+
),
|
|
200
|
+
loadingUpdate: computed(
|
|
201
|
+
() => (cod?: number) =>
|
|
202
|
+
(cod ? _status().idsUpdating?.includes(cod) : _status().updateLoading) || false
|
|
203
|
+
),
|
|
204
|
+
})),
|
|
205
|
+
withMethods((store, userService = inject(UserService)) => ({
|
|
206
|
+
addUser: rxMethod<AddUser>(
|
|
207
|
+
pipe(
|
|
208
|
+
tap(() => {
|
|
209
|
+
patchState(store, { _status: { ...store._status(), addLoading: true } });
|
|
210
|
+
}),
|
|
211
|
+
switchMap((entity) => {
|
|
212
|
+
return userService.addUser$(entity).pipe(
|
|
213
|
+
tap((cod) => {
|
|
214
|
+
patchState(store, addEntity({ ...entity, cod }, config), {
|
|
215
|
+
_status: {
|
|
216
|
+
...store._status(),
|
|
217
|
+
addLoading: false,
|
|
218
|
+
error: null,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}),
|
|
222
|
+
catchError(() => {
|
|
223
|
+
patchState(store, {
|
|
224
|
+
_status: {
|
|
225
|
+
...store._status(),
|
|
226
|
+
addLoading: false,
|
|
227
|
+
error: new Error('Error al agregar user'),
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
return of(entity);
|
|
231
|
+
})
|
|
232
|
+
);
|
|
233
|
+
})
|
|
234
|
+
)
|
|
235
|
+
),
|
|
236
|
+
loadUsers: rxMethod<void>(
|
|
237
|
+
pipe(
|
|
238
|
+
tap(() => {
|
|
239
|
+
patchState(store, { _status: { ...store._status(), loading: true } });
|
|
240
|
+
}),
|
|
241
|
+
switchMap(() => {
|
|
242
|
+
return userService.getUsers$().pipe(
|
|
243
|
+
tap((response) => {
|
|
244
|
+
patchState(store, setAllEntities(response, config), {
|
|
245
|
+
_status: {
|
|
246
|
+
...store._status(),
|
|
247
|
+
error: null,
|
|
248
|
+
loaded: true,
|
|
249
|
+
loading: false,
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}),
|
|
253
|
+
catchError(() => {
|
|
254
|
+
patchState(store, {
|
|
255
|
+
_status: {
|
|
256
|
+
...store._status(),
|
|
257
|
+
error: new Error('Error al cargar users'),
|
|
258
|
+
loading: false,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
return of([]);
|
|
262
|
+
})
|
|
263
|
+
);
|
|
264
|
+
})
|
|
265
|
+
)
|
|
266
|
+
),
|
|
267
|
+
removeUser: rxMethod<number>(
|
|
268
|
+
pipe(
|
|
269
|
+
tap((cod) => {
|
|
270
|
+
patchState(store, {
|
|
271
|
+
_status: {
|
|
272
|
+
...store._status(),
|
|
273
|
+
removeLoading: true,
|
|
274
|
+
idsRemoving: [...(store._status().idsRemoving || []), cod],
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
}),
|
|
278
|
+
switchMap((cod) => {
|
|
279
|
+
return userService.removeUser$(cod).pipe(
|
|
280
|
+
tap((success) => {
|
|
281
|
+
if (success) {
|
|
282
|
+
const idsRemoving = store._status().idsRemoving || [];
|
|
283
|
+
patchState(store, removeEntity(cod), {
|
|
284
|
+
_status: {
|
|
285
|
+
...store._status(),
|
|
286
|
+
removeLoading: false,
|
|
287
|
+
error: null,
|
|
288
|
+
idsRemoving: idsRemoving.filter((idRemoving) => idRemoving !== cod),
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
} else {
|
|
292
|
+
throw new Error('Error al eliminar user');
|
|
293
|
+
}
|
|
294
|
+
}),
|
|
295
|
+
catchError(() => {
|
|
296
|
+
const idsRemoving = store._status().idsRemoving || [];
|
|
297
|
+
patchState(store, {
|
|
298
|
+
_status: {
|
|
299
|
+
...store._status(),
|
|
300
|
+
removeLoading: false,
|
|
301
|
+
error: new Error('Error al eliminar user'),
|
|
302
|
+
idsRemoving: idsRemoving.filter((idRemoving) => idRemoving !== cod),
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
return of(false);
|
|
306
|
+
})
|
|
307
|
+
);
|
|
308
|
+
})
|
|
309
|
+
)
|
|
310
|
+
),
|
|
311
|
+
updateUser: rxMethod<UpdateUser>(
|
|
312
|
+
pipe(
|
|
313
|
+
tap((entity) => {
|
|
314
|
+
patchState(store, {
|
|
315
|
+
_status: {
|
|
316
|
+
...store._status(),
|
|
317
|
+
idsUpdating: [...(store._status().idsUpdating || []), entity.cod],
|
|
318
|
+
updateLoading: true,
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
}),
|
|
322
|
+
switchMap((entity) => {
|
|
323
|
+
return userService.updateUser$(entity).pipe(
|
|
324
|
+
tap((success) => {
|
|
325
|
+
if (success) {
|
|
326
|
+
const idsUpdating = store._status().idsUpdating || [];
|
|
327
|
+
patchState(store, updateEntity({ changes: entity, id: entity.cod }, config), {
|
|
328
|
+
_status: {
|
|
329
|
+
...store._status(),
|
|
330
|
+
error: null,
|
|
331
|
+
idsUpdating: idsUpdating.filter((idUpdating) => idUpdating !== entity.cod),
|
|
332
|
+
updateLoading: false,
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
} else {
|
|
336
|
+
throw new Error('Error al actualizar user');
|
|
337
|
+
}
|
|
338
|
+
}),
|
|
339
|
+
catchError(() => {
|
|
340
|
+
const idsUpdating = store._status().idsUpdating || [];
|
|
341
|
+
patchState(store, {
|
|
342
|
+
_status: {
|
|
343
|
+
...store._status(),
|
|
344
|
+
error: new Error('Error al actualizar user'),
|
|
345
|
+
idsUpdating: idsUpdating.filter((idUpdating) => idUpdating !== entity.cod),
|
|
346
|
+
updateLoading: false,
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
return of(false);
|
|
350
|
+
})
|
|
351
|
+
);
|
|
352
|
+
})
|
|
353
|
+
)
|
|
354
|
+
),
|
|
355
|
+
})),
|
|
356
|
+
withHooks({
|
|
357
|
+
onInit: (store) => {
|
|
358
|
+
store.loadUsers();
|
|
359
|
+
},
|
|
360
|
+
})
|
|
361
|
+
);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## 📄 Licencia
|
|
365
|
+
|
|
366
|
+
Este proyecto está bajo la licencia [MIT](LICENSE).
|