Este componente de formulario dinámico en Angular utiliza FormArray para manejar grupos de campos repetibles de manera correcta, eficiente y flexible.
- Formularios Reactivos: Utiliza
FormBuilder
yFormArray
para manejar formularios reactivos. - Campos Dinámicos: Permite agregar y eliminar dinámicamente grupos de campos de formulario.
- Validación: Incluye validación básica de formularios.
- Reutilizable: Fácilmente adaptable para diferentes casos de uso.
- Registro de Usuarios: Un formulario donde se pueden agregar múltiples usuarios, cada uno con su conjunto de campos (nombre, email, etc.).
- Lista de Tareas: Un formulario para agregar y gestionar una lista de tareas.
- Inventario de Productos: Un formulario para agregar productos a un inventario, con campos como nombre del producto, cantidad, precio, etc.
- Encuestas o Cuestionarios: Un formulario para crear encuestas donde cada pregunta puede tener múltiples opciones de respuesta.
-
Clona el repositorio:
git clone https://github.com/tu-usuario/angular-dynamic-form-FormArray.git
-
Navega al directorio del proyecto:
cd angular-dynamic-form-FormArray
-
Instala las dependencias:
npm install
-
Copia los archivos del componente (
user-form.component.html
,user-form.component.ts
) a tu proyecto Angular. -
Importa y declara el componente en tu módulo:
import { UserFormComponent } from './user-form/user-form.component'; @NgModule({ declarations: [ UserFormComponent, // otros componentes ], // otros módulos }) export class AppModule { }
-
Usa el componente en tu plantilla:
<app-user-form></app-user-form>
Para adaptar el componente a diferentes casos de uso, modifica los campos en la plantilla HTML y el método createItemGroup
en el archivo TypeScript.
<form #f="ngForm" [formGroup]="form" novalidate>
<div class="form-array" formArrayName="items">
<ng-container *ngFor="let item of items.controls; let i = index" [formGroupName]="i" class="form-item">
<div class="mb-4 p-4">
<div class="d-flex justify-content-between align-items-center">
<label class="form-label">
Usuario <strong>Nro. {{ i + 1 }}</strong>
</label>
<div (click)="removeItem(i)" *ngIf="items.length > 1" class="remove-button" title="Eliminar usuario">
<i class="ri-delete-bin-5-line"></i>
</div>
</div>
<div class="form-group">
<label class="form-label">Nombre</label>
<input formControlName="nombre" class="form-control" required />
<div class="invalid-feedback" *ngIf="f.form.get(['items', i, 'nombre'])?.invalid">
Campo requerido
</div>
</div>
<div class="form-group">
<label class="form-label">Email</label>
<input formControlName="email" class="form-control" required />
<div class="invalid-feedback" *ngIf="f.form.get(['items', i, 'email'])?.invalid">
Campo requerido
</div>
</div>
</div>
</ng-container>
</div>
<button type="button" (click)="addItem()" class="btn btn-primary">Agregar Usuario</button>
<button type="submit" class="btn btn-success">Enviar</button>
</form>
/**
* @fileoverview Componente de formulario dinámico en Angular utilizando FormArray para manejar grupos de campos repetibles.
*
* @autor Dennys Jose Marquez Reyes - dennysjmarquez
* Contacto: dennysjmarquez@gmail.com
* Sitio Web: https://dennysjmarquez.dev/
*/
import {
AfterViewInit,
ChangeDetectorRef,
Component,
OnDestroy,
OnInit,
ViewChild,
} from '@angular/core';
import { FormArray, FormBuilder, FormGroup, NgForm } from '@angular/forms';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-user-form',
templateUrl: './user-form.component.html',
styleUrls: ['./user-form.component.scss'],
})
export class UserFormComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('f') f!: NgForm;
formEventSubscription$!: Subscription;
form!: FormGroup;
constructor(private fb: FormBuilder, private cd: ChangeDetectorRef) {}
get items(): FormArray {
return this.form.get('items') as FormArray;
}
ngOnInit(): void {
this.form = this.fb.group({
items: this.fb.array([]),
});
this.addItem(); // Agregar un grupo de campos al inicio
}
ngAfterViewInit(): void {
// Aquí puedes agregar cualquier lógica adicional que necesites después de que la vista se haya inicializado
}
createItemGroup(): FormGroup {
return this.fb.group({
nombre: [null],
email: [null],
});
}
addItem(): void {
this.items.push(this.createItemGroup());
this.cd.detectChanges();
}
removeItem(index: number): void {
this.items.removeAt(index);
this.cd.detectChanges();
}
ngOnDestroy(): void {
if (this.formEventSubscription$) {
this.formEventSubscription$.unsubscribe();
}
}
}
-
<form #f="ngForm" [formGroup]="form" novalidate>
#f="ngForm"
: Esta directiva localiza el formulario en la plantilla y lo asigna a la variablef
, que se puede usar para acceder a las propiedades y métodos del formulario en la plantilla.[formGroup]="form"
: Esta directiva vincula el formulario HTML alFormGroup
definido en el componente TypeScript.form
es el nombre de la propiedad delFormGroup
en el componente.
-
<div class="form-array" formArrayName="items">
formArrayName="items"
: Vincula elFormArray
llamadoitems
delFormGroup
al contenedor. Esto permite iterar sobre los controles delFormArray
.
-
<ng-container *ngFor="let item of items.controls; let i = index" [formGroupName]="i" class="form-item">
*ngFor="let item of items.controls; let i = index"
: Itera sobre los controles delFormArray
items
y asigna cada control aitem
con su índicei
.[formGroupName]="i"
: Vincula cada elemento delFormArray
a unFormGroup
específico basado en su índicei
.
-
<label class="form-label">Nombre</label>
- Etiqueta para el campo de entrada "Nombre".
-
<input formControlName="nombre" class="form-control" required />
formControlName="nombre"
: Vincula el campo de entrada al control de formulario llamadonombre
dentro delFormGroup
actual.
-
<div class="invalid-feedback" *ngIf="f.form.get(['items', i, 'nombre'])?.invalid">
*ngIf="f.form.get(['items', i, 'nombre'])?.invalid"
: Muestra el mensaje de error si el control de formularionombre
es inválido.f.form.get(['items', i, 'nombre'])
accede al controlnombre
dentro delFormArray
en el índicei
.
La expresión ['items', i, 'nombre']
se usa para acceder a un control de formulario específico dentro de un FormArray
anidado en un FormGroup
. Aquí está el desglose de cada elemento en la expresión:
-
'items'
:- Este es el nombre del
FormArray
dentro delFormGroup
principal. ElFormArray
items
contiene múltiplesFormGroup
o controles de formulario individuales.
- Este es el nombre del
-
i
:i
es el índice del elemento actual en elFormArray
items
. Este índice se obtiene del bucle*ngFor="let item of items.controls; let i = index"
, dondei
representa la posición delFormGroup
o control de formulario actual dentro delFormArray
.
-
'nombre'
:- Este es el nombre del control de formulario dentro del
FormGroup
en la posicióni
delFormArray
. En este caso, es el control de formulario que maneja el campo de entrada para "Nombre".
- Este es el nombre del control de formulario dentro del
¡Las contribuciones son bienvenidas! Si tienes alguna mejora o corrección, por favor abre un issue o envía un pull request.
Este proyecto está licenciado bajo la Licencia MIT - consulta el archivo LICENSE para más detalles.