Explicación: Firebase
Requisitos de finalización
4. Login
LoginComponent
Crea el Guard: src/app/core/auth/auth.guard.ts
import { CanActivateFn, Router } from '@angular/router';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
// Si aún está cargando el estado, dejamos pasar y que el componente decida (simple)
if (auth.loading()) return true;
if (auth.isLoggedIn()) return true;
router.navigateByUrl('/login');
return false;
};
Crea LoginComponent (ReactiveForms)
src/app/pages/login/
ng g c pages/login --standalone
src/app/pages/login/login.ts
import { Component,inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../../core/auth/auth.service';
@Component({
selector: 'app-login',
imports: [ReactiveFormsModule],
templateUrl: './login.html',
styleUrl: './login.css',
})
export class LoginComponent {
private fb = inject(FormBuilder);
private auth = inject(AuthService);
private router = inject(Router);
error = '';
form = this.fb.nonNullable.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]],
});
get email() { return this.form.controls.email; }
get password() { return this.form.controls.password; }
async onLogin() {
this.error = '';
if (this.form.invalid) {
this.form.markAllAsTouched();
return;
}
const { email, password } = this.form.getRawValue();
try {
await this.auth.login(email, password);
this.router.navigateByUrl('/admin');
} catch (e: any) {
this.error = e?.message ?? 'Error en login';
}
}
async onRegister() {
this.error = '';
if (this.form.invalid) {
this.form.markAllAsTouched();
return;
}
const { email, password } = this.form.getRawValue();
try {
await this.auth.register(email, password);
this.router.navigateByUrl('/admin');
} catch (e: any) {
this.error = e?.message ?? 'Error en registro';
}
}
}
src/app/pages/login/login.component.html
<h2>Login</h2>
<form [formGroup]="form" (ngSubmit)="onLogin()" class="card">
<label>
Email
<input type="email" formControlName="email" />
</label>
@if (email.touched && email.errors?.['required']) {
<small class="err">Email obligatorio</small>
}
@if (email.touched && email.errors?.['email']) {
<small class="err">Email no válido</small>
}
<label>
Contraseña
<input type="password" formControlName="password" />
</label>
@if (password.touched && password.errors?.['required']) {
<small class="err">Contraseña obligatoria</small>
}
@if (password.touched && password.errors?.['minlength']) {
<small class="err">Mínimo 6 caracteres</small>
}
<button type="submit" [disabled]="form.invalid">Entrar</button>
</form>
<div class="row">
<button type="button" (click)="onRegister()" [disabled]="form.invalid">
Crear cuenta
</button>
</div>
@if (error) {
<p class="err">{{ error }}</p>
}
src/app/pages/login/login.component.css
.card { display: grid; gap: 10px; max-width: 360px; padding: 16px; border: 1px solid #ddd; border-radius: 10px; }
label { display: grid; gap: 6px; }
input { padding: 10px; border: 1px solid #ccc; border-radius: 8px; }
button { padding: 10px; border-radius: 8px; border: 1px solid #ccc; cursor: pointer; }
button[disabled] { opacity: 0.6; cursor: not-allowed; }
.row { margin-top: 10px; display: flex; gap: 10px; }
.err { color: #b00020; margin-top: 10px; }