El registro de usuarios es una parte fundamental de muchas aplicaciones web. En este tutorial de Login y Registro con Angular paso a paso, exploraremos cómo construir un sistema de registro de usuarios en Angular, utilizando formularios, servicios.
Configuracion del proyecto
Para crear un nuevo proyecto en angular puedes revisar nuestro link que te guiará paso a paso.
https://tenocode.com/crear-un-nuevo-proyecto-angular/
Rutas y modulos del proyecto
Luego de tener todo configurado, nos movemos a nuestro proyecto y dentro de src/app/modules crearemos un nuevo modulo llamado auth, en donde crearemos nuestros componentes para login y registro, luego creamos nuestro modulo admin, el cual contendrá nuestro sitio privado (debes entrar por consola a la ruta)
ng g m auth --routing
ng g m admin --routing
Esto hará que se nos cree una nueva carpeta llamada auth y dentro tendremos un archivo de modulo y otro de routing, debemos ingresar a la carpeta y luego ejecutar estos comandos para grear nuestras páginas de login y registro.
ng g c login
ng g c register
Ahora entramos a admin y generamos el componente home
ng g c home
Los archivos spec.ts no los ocuparemos en este tutorial ya que son para pruebas unitarias, los eliminamos.
En el siguiente paso crearemos las rutas en nuestro archivo auth-routing.modules.ts y admin-routing.modules.ts por lo que nuestros archivos debieran quedar así:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
const routes: Routes = [
{path:'', component:HomeComponent}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule { }
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
const routes: Routes = [
{path:'login',component:LoginComponent},
{path:'register',component:RegisterComponent}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AuthRoutingModule { }
Como puedes ver, las rutas login y register estan creadas! Seguimos agregando la ruta hija a nuestro archivo de rutas principal app-routing.modules.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{path:'auth',loadChildren:() => import('./modules/auth/auth.module').then(m=>m.AuthModule)},
{path:'admin',loadChildren:() => import('./modules/admin/admin.module').then(m=>m.AdminModule)}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Acá le decimos a nuestro aplicativo que tendremos una subruta llamara auth y de ellas cuelgan las rutas hijas de auth
Modificamos nuestro archivo app.component.html, borramos todo lo que hay dentro (por defecto angular crea un html para que visualices cuando levantas la aplicación), y copias el siguiente código.
<router-outlet></router-outlet>
Rutas listas, comprobamos en nuestro browser las siguientes rutas:
http://localhost:4200/auth/login
Deberías ver algo como: login works!
http://localhost:4200/auth/register
Deberías ver algo como: register works!
http://localhost:4200/admin
Deberías ver algo como: home works!
Por último importa este módulo HttpClientModule a tu archivo app.module.ts, esto es importante para hacer peticiones a tu backend.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
y a tu archivo auth.module.ts FormsModule y ReactiveFormsModule, estos módulos son importantes para poder utilizar formularios en angular.
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AuthRoutingModule } from './auth-routing.module';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';
import { FormsModule,ReactiveFormsModule } from '@angular/forms';
@NgModule({
declarations: [
LoginComponent,
RegisterComponent
],
imports: [
CommonModule,
AuthRoutingModule,
FormsModule,
ReactiveFormsModule
]
})
export class AuthModule { }
Un poco de estilos
Antes de crear el login instalaremos bootstrap a nuestro proyecto, nos posicionamos en la raiz y ejecutamos
Instalamos los recursos en nuestro proyecto
npm install bootstrap
npm install bootstrap-icons
Luego dentro de src/ encontramos el archivo styles.scss e importamos bootstrap
@import "~bootstrap/dist/css/bootstrap.min.css";
@import '~bootstrap-icons/font/bootstrap-icons.css';
Servicios y Modelos
Necesitamos agregar a nuestro proyecto, servicios y modelos de login y register, todos los servicios los escribiremos en un archivo llamado auth.services.ts, y otro servicio para las validaciones. Creamos una directorio llamado core y dentro agregaremos las carpetas services y models
Dentro de services creamos auth.services.ts con el siguiente comando
ng g s auth
dentro de nuestro archivo auth.service.ts creamos las siguientes funciones
login (loginModel:LoginModel){
this.httpClient.post <string>(`${this.URL}/login`,loginModel).subscribe(receivedItem => {
localStorage.setItem('access_token',JSON.stringify(receivedItem));
this.goAdminPage();
},err => {
this.msg$.next(err.error);
}
)
}
goAdminPage(){
this.router.navigate(['/admin']);
}
register(user:UserModel){
return this.httpClient.post(`${this.URL}/register`,user,{observe: 'response', responseType: 'json'});
}
creamos manualmente el archivo customvalidation.service.ts (este archivo contiene funciones para validar nuestros formularios) y copiamos el siguiente código dentro de él.
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class CustomvalidationService {
constructor() { }
integerPattern(){
return '^([1-9][0-9]*)$';
}
integerPatternWithOneBlank(){
return '^([1-9][0-9]*)((\\s)?([0-9]))*$'
}
integerPatternCero(){
return '^((0)|([1-9][0-9]*))$';
}
urlPatternBusiness(){
return '^[a-z\\d]+-?[a-z\\d]+$';
}
namePatterBusiness(){
return '^[a-zA-z]+((\\s[a-zA-Z\\d]+)+)?$';
}
categoryPatternName(){
return '^[\\w\-\\s\\ñ\\áéíóúýÁÉÍÓÚ]+$';
}
emailValidation(){
return '^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,4}$'
}
booleanValidation(){
return '^(0|1)$';
}
wspUrlApi(){
return '^(https:\/\/api.whatsapp.com\/message\/).*$';
}
instagramValidation(){
return '^(https:\/\/www.instagram.com\/).*$';
}
facebookValidation(){
return '^(https:\/\/www.facebook.com\/).*$'
}
}
Los modelos Login y User
Primero creamos login.model.ts manualmente en la carpeta models y luego copiamos este código
export interface LoginModel {
user: {
email:string,
password:string
}
}
Luego user.model.ts
export interface UserModel {
id: string,
name:string,
last_name:string,
email:string,
email_verified_at:string,
password:string,
password_confirmation:string,
}
Finalmente empezamos a crear las vistas
Register
Copia este código html en el archivo register.component.html
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow-lg p-3 mb-5 bg-white rounded">
<div class="card-header text-center bg-success text-white">
<h3>Registro de Usuario</h3>
</div>
<div class="card-body">
<form [formGroup]="formRegister" (ngSubmit)="register()">
<div class="mb-3">
<label for="nombre" class="form-label">Nombre:</label>
<input type="text" formControlName="name" class="form-control" placeholder="Ingresa tu nombre" required maxlength="30">
<span class="text-danger" *ngIf="formRegister.get('name')?.invalid && showValidationErrors">{{getErrorMessage('name')}}</span>
</div>
<div class="mb-3">
<label for="apellidos" class="form-label">Apellidos:</label>
<input type="text" formControlName="lastName" class="form-control" placeholder="Ingresa tus Apellidos" maxlength="60">
<span class="text-danger" *ngIf="this.formRegister.get('lastName')?.invalid && showValidationErrors">{{getErrorMessage('lastName')}}</span>
</div>
<div class="mb-3">
<label for="correo" class="form-label">Correo Electrónico:</label>
<input type="email" formControlName="email" class="form-control" placeholder="Ingresa tu correo electrónico" maxlength="60">
<span class="text-danger" *ngIf="formRegister.get('email')?.invalid && showValidationErrors">{{getErrorMessage('email')}}</span>
</div>
<div class="mb-3">
<label for="contrasena" class="form-label">Contraseña:</label>
<div class="input-group">
<input type="password" formControlName="password" class="form-control" placeholder="Ingresa tu contraseña" #passwordInput maxlength="20">
<button class="btn btn-outline-secondary" (click)="showPass()" type="button"><i class="bi {{eyeClass}}"></i></button>
</div>
<span class="text-danger" *ngIf="formRegister.get('password')?.invalid && showValidationErrors">{{getErrorMessage('password')}}</span>
</div>
<div class="mb-3">
<label for="confirmarContrasena" class="form-label">Confirmar Contraseña:</label>
<div class="input-group">
<input type="password" formControlName="passwordConfirm" class="form-control" placeholder="Confirma tu contraseña" #passwordInput_confirm maxlength="20">
</div>
<span class="text-danger" *ngIf="formRegister.get('passwordConfirm')?.invalid && showValidationErrors">{{getErrorMessage('passwordConfirm')}}</span>
</div>
<div class="mb-3 text-danger" *ngIf="msgErrorS!=''">
<p>{{msgErrorS}}</p>
</div>
<div class="text-center">
<button type="submit" class="btn btn-success">Registrarse</button>
</div>
</form>
</div>
<div class="card-footer text-muted text-center">
¿Ya tienes una cuenta? <a href="auth/login">Inicia Sesión</a>
</div>
</div>
</div>
</div>
</div>
En tu archivo register.component.ts copiamos lo siguiente
import { Component, ElementRef, ViewChild } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { UserModel } from 'src/app/core/models/user.model';
import { AuthService } from 'src/app/core/services/auth.service';
import { CustomvalidationService } from 'src/app/core/services/customvalidation.service';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.scss']
})
export class RegisterComponent {
@ViewChild('passwordInput') passwordInput:ElementRef|any;
@ViewChild('passwordInput_confirm') passwordInput_confirm:ElementRef|any;
eyeClass:string = 'bi-eye-fill';
isShowing:boolean = false;
showValidationErrors:boolean = false;
msgErrorS:string = '';
formRegister = new FormGroup({
name: new FormControl('',[Validators.required,Validators.maxLength(50)]),
lastName: new FormControl('',[Validators.required,Validators.maxLength(50)]),
email: new FormControl('',[Validators.required,Validators.pattern(this.customValidator.emailValidation()),Validators.maxLength(150)]),
password: new FormControl('',[Validators.required,Validators.minLength(6),Validators.maxLength(20)]),
passwordConfirm: new FormControl('',[Validators.required,Validators.minLength(6),Validators.maxLength(20)]),
});
userModel:UserModel|any = {} as UserModel;
constructor(private readonly authService:AuthService, private customValidator:CustomvalidationService,) { }
register(){
if(this.formRegister.value.passwordConfirm!='' && this.formRegister.value.password!=this.formRegister.value.passwordConfirm){
this.msgErrorS = "Las contraseñas no coinciden";
return;
}else
this.msgErrorS = "";
if (this.formRegister.valid) {
this.userModel.name = this.formRegister.value.name;
this.userModel.last_name = this.formRegister.value.lastName;
this.userModel.email = this.formRegister.value.email;
this.userModel.password = this.formRegister.value.password;
this.userModel.password_confirmation = this.formRegister.value.password;
this.authService.register(this.userModel).subscribe(receivedItem => {
//si el server envía algún dato
console.log(receivedItem);
},(error) => {
if(error.error.email!=undefined){
}
});
this.showValidationErrors = false;
}else
this.showValidationErrors = true;
}
showPass(){
if(this.isShowing){
this.passwordInput.nativeElement.type='password';
this.passwordInput_confirm.nativeElement.type='password';
this.eyeClass='bi-eye-fill';
this.isShowing=false;
}else{
this.passwordInput.nativeElement.type='text';
this.passwordInput_confirm.nativeElement.type='text';
this.eyeClass='bi-eye-slash-fill';
this.isShowing=true;
}
}
getErrorMessage(field:string) {
if (field=='name'&&this.formRegister.get('name')?.hasError('required'))
return '*Nombre es requerido';
if (field=='lastName'&&this.formRegister.get('lastName')?.hasError('required'))
return '*Apellido es requerido';
if ((field=='email')&&(this.formRegister.get('email')?.hasError('required')||this.formRegister.get('email')?.hasError('pattern')))
return '*Email no es válido';
if ((field=='password')&&(this.formRegister.get('password')?.hasError('required')||
this.formRegister.get('password')?.hasError('maxlength')||
this.formRegister.get('password')?.hasError('minlength')))
return '*Contraseña entre 6 y 20 caracteres';
if(field=='passwordConfirm'&&(this.formRegister.get('passwordConfirm')?.hasError('required')))
return 'Repite la contraseña';
if(field=='terms'&&this.formRegister.get('terms')?.hasError('required'))
return 'Debe aceptar los Términos y Condiciones';
return '';
}
}
El Login
Bueno ahora copiaremos el html del login en el archivo src/app/auth/login/login.component.html
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow-lg p-3 mb-5 bg-white rounded">
<div class="card-header text-center bg-primary text-white">
<h3>Iniciar Sesión</h3>
</div>
<div class="card-body">
<form [formGroup]="form" (ngSubmit) = "login()">
<div class="mb-3">
<label for="usuario" class="form-label">Email:</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-envelope"></i></span>
<input type="text" class="form-control" formControlName="email" placeholder="Ingresa tu email">
</div>
<span class="text-danger" *ngIf="form.get('email')?.invalid && showValidationErrors">{{getErrorMessage('email')}}</span>
</div>
<div class="mb-3">
<label for="contrasena" class="form-label">Contraseña:</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-lock"></i></span>
<input type="password" class="form-control" #passwordInput formControlName="password" id="contrasena" placeholder="Ingresa tu contraseña">
<button class="btn btn-outline-secondary" (click)="showPass()" type="button"><i class="bi {{eyeClass}}"></i></button>
</div>
<span class="text-danger" *ngIf="form.get('password')?.invalid && showValidationErrors">{{getErrorMessage('password')}}</span>
</div>
<div *ngIf="msg!=''" class="text-center">
<p class="text-danger">{{msg}}</p>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary">Iniciar Sesión</button>
</div>
</form>
</div>
<div class="card-footer text-muted text-center">
¿No tienes una cuenta? <a href="auth/register">Regístrate</a>
</div>
</div>
</div>
</div>
</div>
Para revisar el codigo más detalladamente puedes ingresar al proyecto GitHub en donde podrás levantar el proyecto en tu local y hacer pruebas