Explicación de Inyección de dependencias, formularios y validaciones
Requisitos de finalización
7. Crear componente de formulario validado
Creamos el componente
ng generate component componentes/form-validado --standalone --skip-tests
Retocamos src/app/componentes/form-validado/form-validado.component.ts.
Más un validador custom que devuelve null si todo va bien o un objeto con el mensaje de error si no.
Angular añade clases CSS como ng-invalid, ng-valid, ng-touched, etc
ng-invalid -> campo inválido
ng-valid -> campo válido
ng-touched -> campo tocado
ng-pristine -> no modificado
import { Component } from '@angular/core';
import {
ReactiveFormsModule,
FormGroup,
FormControl,
Validators,
AbstractControl,
ValidationErrors
} from '@angular/forms';
@Component({
selector: 'app-form-validado',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<h2>Formulario Reactivo con Validaciones</h2>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<!-- NOMBRE -->
<div>
<label>Nombre</label>
<input type="text" formControlName="nombre">
@if (nombre.invalid && nombre.touched) {
@if (nombre.errors?.['required']) {
<small style="color:red">El nombre es obligatorio.</small>
}
@if (nombre.errors?.['pattern']) {
<small style="color:red">El nombre contiene caracteres no válidos.</small>
}
}
</div>
<!-- EMAIL -->
<div>
<label>Email</label>
<input type="email" formControlName="email">
@if (email.invalid && email.touched) {
@if (email.errors?.['required']) {
<small style="color:red">El email es obligatorio.</small>
}
@if (email.errors?.['email']) {
<small style="color:red">El email no tiene formato válido.</small>
}
}
</div>
<!-- EDAD (validador personalizado) -->
<div>
<label>Edad</label>
<input type="number" formControlName="edad">
@if (edad.invalid && edad.touched) {
@if (edad.errors?.['required']) {
<small style="color:red">La edad es obligatoria.</small>
}
@if (edad.errors?.['rangoEdad']) {
<small style="color:red">
{{ edad.errors['rangoEdad'].message }}
</small>
}
}
</div>
<button type="submit" [disabled]="form.invalid">
Enviar
</button>
</form>
<hr>
<h3>Estado del control "nombre"</h3>
<pre>
dirty: {{ nombre.dirty }}
pristine: {{ nombre.pristine }}
touched: {{ nombre.touched }}
untouched: {{ nombre.untouched }}
valid: {{ nombre.valid }}
invalid: {{ nombre.invalid }}
</pre>
`
})
export class FormValidadoComponent {
form = new FormGroup({
nombre: new FormControl('', [
Validators.required,
Validators.pattern('[\\w\\-\\s\\/]+')
]),
email: new FormControl('', [
Validators.required,
Validators.email
]),
edad: new FormControl('', [
Validators.required,
this.rangoEdadValidator(18, 65)
])
});
get nombre() { return this.form.controls.nombre; }
get email() { return this.form.controls.email; }
get edad() { return this.form.controls.edad; }
// Validador personalizado (igual idea que en el PDF)
rangoEdadValidator(min: number, max: number) {
return (control: AbstractControl): ValidationErrors | null => {
const raw = control.value;
if (raw === null || raw === '') return null; // lo gestiona required
const edad = Number(raw);
if (Number.isNaN(edad)) {
return { rangoEdad: { message: 'La edad debe ser un número' } };
}
if (edad >= min && edad <= max) {
return null;
} else {
return { rangoEdad: { message: `La edad debe estar entre ${min} y ${max}` } };
}
};
}
onSubmit() {
if (this.form.valid) {
console.log('Formulario válido:', this.form.value);
} else {
console.log('Formulario inválido');
}
}
}