Explicación de Inyección de dependencias, formularios y validaciones
| Sitio: | Campus virtual DAW - damiansu |
| Curso: | Desarrollo Web en Entorno Cliente |
| Libro: | Explicación de Inyección de dependencias, formularios y validaciones |
| Imprimido por: | Invitado |
| Día: | jueves, 22 de enero de 2026, 05:06 |
1. Descripción
Lo que vamos a realizar a continuación es:
-
Proyecto nuevo (ng new tema7-angular --standalone).
-
Servicio ClientesService inyectado en un componente (ListaColoresComponent) usando inject() y @for.
-
Formulario Template-Driven con FormsModule, ngForm, ngModel y @if para errores.
-
Formulario Reactivo básico con ReactiveFormsModule, FormGroup, FormControl.
-
Formulario Reactivo con validaciones:
-
Built-in (required, email, pattern).
-
Personalizado (rangoEdadValidator(18, 65)).
-
Errores visibles en plantilla.
-
Botón deshabilitado si el formulario no es válido.
-
Uso de estados (valid, invalid, touched, etc.) como en el tema.
-
2. Crear el proyecto Angular
Creamos el proyecto
npm install -g @angular/cli
ng new tema7-angular --standalone
cd tema7-angular
ng serve
src/main.ts (te lo genera Angular)
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
bootstrapApplication(AppComponent)
.catch(err => console.error(err));
Lo ponemos un poco básico:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: true,
template: `
<h1>Tema 7 – Angular</h1>
<p>Vamos a ir añadiendo componentes aquí.</p>
`
})
export class AppComponent {}3. Inyección de dependencias (DI)
Inyección de dependencias (DI) – Servicio + Componente
3.1. Crear el servicio
Creamos un servicio que tenga un array:
ng generate service services/clientes --skip-tests
Ponemos un array de colores
import { Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class ClientesService {
private colores: string[] = ['blue', 'red', 'violet', 'yellow', 'green'];
getColores(): string[] {
return this.colores;
}
}3.2. Crear el componente que inyecta el servicio
Creamos el componente:
ng generate component componentes/lista-colores --standalone --skip-tests
Aquí ya estamos aplicando inyección de dependencias: el componente no crea los datos, los pide al servicio
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ClientesService } from '../../services/clientes.service';
@Component({
selector: 'app-lista-colores',
standalone: true,
imports: [CommonModule],
template: `
<h2>Inyección de dependencias (colores)</h2>
@for (color of colores; track color) {
<p>{{ color }}</p>
}
`
})
export class ListaColoresComponent {
// Inyección moderna
private clientesService = inject(ClientesService);
colores = this.clientesService.getColores();
}3.3. Mostrarlo en AppComponent
Vamos a nuestro src/app/app.component.ts
import { Component } from '@angular/core';
import { ListaColoresComponent } from './componentes/lista-colores/lista-colores.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [ListaColoresComponent],
template: `
<h1>Tema 7 – Angular</h1>
<app-lista-colores></app-lista-colores>
`
})
export class AppComponent {}4. Formularios de tipo Template
En Angular moderno, en vez de meter FormsModule en un módulo, lo importamos en el componente standalone.
4.1. Crear el componente de formulario template
Creamos el compoenente
ng generate component componentes/form-template --standalone --skip-tests
Y retocamos src/app/componentes/form-template/form-template.component.ts
import { Component } from '@angular/core';
import { FormsModule, NgForm } from '@angular/forms';
@Component({
selector: 'app-form-template',
standalone: true,
imports: [FormsModule],
template: `
<h2>Formulario Template-driven</h2>
<form #personaForm="ngForm" (ngSubmit)="onSubmit(personaForm)">
<div>
<label>Nombre</label>
<input
type="text"
name="nombre"
ngModel
required
>
@if (personaForm.submitted && personaForm.controls['nombre']?.invalid) {
<small style="color:red">El nombre es obligatorio</small>
}
</div>
<div>
<label>Email</label>
<input
type="email"
name="email"
ngModel
>
</div>
<button type="submit">Enviar</button>
</form>
<h3>Datos del formulario</h3>
<pre>{{ personaForm.value | json }}</pre>
`
})
export class FormTemplateComponent {
onSubmit(form: NgForm) {
console.log('Template form enviado:', form.value);
}
}4.2. Añadirlo a AppComponent
Lo añadimos
import { ListaColoresComponent } from './componentes/lista-colores/lista-colores.component';
import { FormTemplateComponent } from './componentes/form-template/form-template.component';
@Component({
...
imports: [ListaColoresComponent, FormTemplateComponent],
template: `
<h1>Tema 7 – Angular moderno</h1>
<app-lista-colores></app-lista-colores>
<hr>
<app-form-template></app-form-template>
`
})
export class AppComponent {}5. Formularios de tipo Model (Reactive Forms)
Aquí pasamos a formularios reactivos, donde la definición está en la clase (TS).
Creamos el componente
ng generate component componentes/form-reactivo --standalone --skip-tests
Retocamos src/app/componentes/form-reactivo/form-reactivo.component.ts
import { Component } from '@angular/core';
import { ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms';
@Component({
selector: 'app-form-reactivo',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<h2>Formulario Reactivo (Model-driven)</h2>
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div>
<label>Nombre</label>
<input type="text" formControlName="nombre">
</div>
<div>
<label>Apellidos</label>
<input type="text" formControlName="apellidos">
</div>
<div>
<label>Edad</label>
<input type="number" formControlName="edad">
</div>
<div>
<label>Dirección</label>
<input type="text" formControlName="direccion">
</div>
<button type="submit">Enviar</button>
</form>
<h3>Datos del formulario</h3>
<pre>{{ form.value | json }}</pre>
`
})
export class FormReactivoComponent {
form = new FormGroup({
nombre: new FormControl(''),
apellidos: new FormControl(''),
edad: new FormControl(''),
direccion: new FormControl('')
});
onSubmit() {
console.log('Formulario reactivo:', this.form.value);
}
}5.1. Añadirlo a AppComponent
Lo añadimos
import { FormReactivoComponent } from './componentes/form-reactivo/form-reactivo.component';
@Component({
...
imports: [
ListaColoresComponent,
FormTemplateComponent,
FormReactivoComponent
],
template: `
<h1>Tema 7 – Angular moderno</h1>
<app-lista-colores></app-lista-colores>
<hr>
<app-form-template></app-form-template>
<hr>
<app-form-reactivo></app-form-reactivo>
`
})
export class AppComponent {}6. Validaciones en Angular (Built-in y personalizadas)
Ahora ampliamos el formulario reactivo con:
-
Validadores built-in (required, email, pattern, etc.)
-
Validador personalizado para la edad
-
Mostrar errores con @if
-
Deshabilitar el botón si el formulario es inválido
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');
}
}
}7.1. (Opcional) CSS para ng-valid / ng-invalid
Editamos (si no está lo creamos) src/app/componentes/form-validado/form-validado.component.css,
Angular añade clases CSS como ng-invalid, ng-valid, ng-touched, etc
input.ng-invalid.ng-touched {
border: 1px solid red;
}
input.ng-valid.ng-touched {
border: 1px solid green;
}7.2. Añadirlo a AppComponent
Lo añadimos
import { FormValidadoComponent } from './componentes/form-validado/form-validado.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [
ListaColoresComponent,
FormTemplateComponent,
FormReactivoComponent,
FormValidadoComponent
],
template: `
<h1>Tema 7 – Angular moderno</h1>
<h2>1. Inyección de dependencias</h2>
<app-lista-colores></app-lista-colores>
<hr>
<h2>2. Formulario Template-driven</h2>
<app-form-template></app-form-template>
<hr>
<h2>3. Formulario Reactivo básico</h2>
<app-form-reactivo></app-form-reactivo>
<hr>
<h2>4. Formulario Reactivo con validaciones</h2>
<app-form-validado></app-form-validado>
`
})
export class AppComponent {}