Autenticación y Autorización
En el Backend implementamos la autenticacion con JWT Token y la autorizacion lo controlamos con modelos que implican desde el password hasta el manejo de roles, permisos y rutas.
Implementacion Auth
Creamos el modelo en src/app/models/auth.ts
export interface LoginI {
email: string;
password: string;
}
export interface LoginResponseI {
token: string;
user: {
id: number;
username: string;
email: string;
password: string;
is_active: "ACTIVE" | "INACTIVE";
avatar: string;
};
}
export interface RegisterI {
username: string;
email: string;
password: string;
}
export interface RegisterResponseI {
token: string;
user: {
id: number;
username: string;
email: string;
password: string;
is_active: "ACTIVE" | "INACTIVE";
avatar: string;
};
}
export interface UserI {
id: number;
username: string;
email: string;
password: string;
is_active: "ACTIVE" | "INACTIVE";
avatar: string;
}
Luego, creamos el servicio en src/app/services/auth.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, tap } from 'rxjs';
import { LoginI, LoginResponseI, RegisterI, RegisterResponseI} from '../models/auth';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private baseUrl = 'http://localhost:4000/api';
private tokenKey = 'auth_token';
constructor(private http: HttpClient) {}
login(credentials: LoginI): Observable<LoginResponseI> {
return this.http.post<LoginResponseI>(`${this.baseUrl}/login`, credentials)
.pipe(
tap(response => {
if (response.token) {
this.setToken(response.token);
}
})
);
}
register(userData: RegisterI): Observable<RegisterResponseI> {
return this.http.post<RegisterResponseI>(`${this.baseUrl}/register`, userData)
.pipe(
tap(response => {
if (response.token) {
this.setToken(response.token);
}
})
);
}
logout(): void {
localStorage.removeItem(this.tokenKey);
}
getToken(): string | null {
return localStorage.getItem(this.tokenKey);
}
setToken(token: string): void {
localStorage.setItem(this.tokenKey, token);
}
isLoggedIn(): boolean {
return !!this.getToken();
}
}
Ajustamos el Template Base para que aparezca las opciones de:
Iniciar Sesion y
Cerrar Sesion
src/app/app.html
<main class="min-h-screen flex flex-col border-4 border-red-500">
<!-- HEADER -->
<header class="h-16 bg-white border-2 border-blue-500 flex items-center w-full font-bold text-blue-600">
<app-header class="w-full"></app-header>
</header>
<!-- CONTENEDOR CENTRAL (ASIDE + CONTENT) -->
<div class="flex flex-1">
<!-- ASIDE -->
<aside class="w-64 bg-white border-2 border-blue-500 flex items-start justify-start font-bold text-blue-400">
<app-aside></app-aside>
</aside>
<!-- CONTENT -->
<section class="flex-1 bg-white border-2 border-blue-500 flex items-center justify-center font-bold text-blue-600">
<router-outlet />
</section>
</div>
<!-- FOOTER (pegado al fondo) -->
<footer class="h-16 bg-white border-2 border-blue-500 flex items-center justify-center font-bold text-blue-600 mt-auto">
<app-footer></app-footer>
</footer>
</main>
src/app/components/layout/header/header.ts
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { MenuItem } from 'primeng/api';
import { OverlayBadge } from 'primeng/overlaybadge';
import { TieredMenu } from 'primeng/tieredmenu';
import { CommonModule } from '@angular/common';
import { AuthService } from '../../../services/auth.service';
@Component({
selector: 'app-header',
standalone: true,
imports: [CommonModule, OverlayBadge, TieredMenu],
templateUrl: './header.html',
styleUrl: './header.css'
})
export class Header {
items: MenuItem[] = [];
constructor(
private router: Router,
private authService: AuthService
) {
this.items = [
{
label: 'Configuración',
icon: 'pi pi-cog',
command: () => {
console.log('Configuración clicked');
}
},
{
label: 'Información',
icon: 'pi pi-info-circle',
command: () => {
console.log('Información clicked');
}
},
{
separator: true
},
{
label: 'Cerrar sesión',
icon: 'pi pi-sign-out',
command: () => {
this.logout();
}
},
{
label: 'Iniciar sesión',
icon: 'pi pi-sign-in',
command: () => {
this.goToLogin();
}
}
];
}
private logout(): void {
this.authService.logout();
this.router.navigate(['/login']);
}
private goToLogin(): void {
this.router.navigate(['/login']);
}
}
src/app/components/layout/header/header.html
<!-- Botones derechos alineados totalmente a la derecha -->
<div class="w-full flex justify-end items-center gap-2">
<button type="button" class="text-orange-500 bg-gray-100 hover:bg-gray-200 rounded shadow hover:shadow-lg flex items-center justify-center w-10 h-10">
<p-overlaybadge value="2">
<i class="pi pi-bell text-2xl"></i>
</p-overlaybadge>
</button>
<button type="button"
class="text-orange-500 bg-gray-100 hover:bg-gray-200 rounded shadow hover:shadow-lg w-10 h-10 flex items-center justify-center"
(click)="menu.toggle($event)">
<i class="pi pi-users text-xl"></i>
</button>
</div>
<!-- Menú fuera del contenedor de botones -->
<p-tieredmenu #menu [model]="items" [popup]="true" />
Confimamos en src/app.config.ts
import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { providePrimeNG } from 'primeng/config';
import Aura from '@primeuix/themes/aura';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideAnimationsAsync(),
provideHttpClient(),
providePrimeNG({
theme: {
preset: Aura
}
})
]
};
Adicionalmente instalar en el terminal
y el archivo src/styles.cssinsertar al final la linea

Ahora cerramos el ciclo con la creacion de los componentes:
Componente Login
src/app/components/auth/login.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { MessageService } from 'primeng/api';
import { ToastModule } from 'primeng/toast';
import { AuthService } from '../../../services/auth.service';
import { CardModule } from 'primeng/card';
@Component({
selector: 'app-login',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, ButtonModule, InputTextModule, ToastModule, CardModule],
templateUrl: './login.html',
styleUrl: './login.css',
providers: [MessageService, AuthService]
})
export class Login {
form: FormGroup;
loading: boolean = false;
constructor(
private fb: FormBuilder,
private router: Router,
private authService: AuthService,
private messageService: MessageService
) {
this.form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]]
});
}
submit(): void {
if (this.form.valid) {
this.loading = true;
const credentials = this.form.value;
this.authService.login(credentials).subscribe({
next: (response) => {
this.messageService.add({
severity: 'success',
summary: 'Éxito',
detail: 'Sesión iniciada correctamente'
});
setTimeout(() => {
this.router.navigate(['/clients']);
}, 1000);
},
error: (error) => {
console.error('Error logging in:', error);
this.messageService.add({
severity: 'error',
summary: 'Error',
detail: 'Credenciales incorrectas'
});
this.loading = false;
}
});
} else {
this.markFormGroupTouched();
this.messageService.add({
severity: 'warn',
summary: 'Advertencia',
detail: 'Por favor complete todos los campos requeridos'
});
}
}
goToRegister(): void {
this.router.navigate(['/register']);
}
private markFormGroupTouched(): void {
Object.keys(this.form.controls).forEach(key => {
this.form.get(key)?.markAsTouched();
});
}
getFieldError(fieldName: string): string {
const field = this.form.get(fieldName);
if (field?.errors && field?.touched) {
if (field.errors['required']) return `${fieldName} es requerido`;
if (field.errors['email']) return 'Email no válido';
if (field.errors['minlength']) return `${fieldName} debe tener al menos ${field.errors['minlength'].requiredLength} caracteres`;
}
return '';
}
}
src/app/components/auth/login.html
<div class="min-h-screen flex items-center justify-center">
<div class="max-w-md w-full">
<p-card header="Iniciar Sesión" class="shadow-lg">
<form [formGroup]="form" (ngSubmit)="submit()" class="space-y-6">
<div class="flex flex-col">
<label class="block mb-2 font-semibold text-gray-700">Email *</label>
<input
pInputText
type="email"
formControlName="email"
class="w-full"
placeholder="ejemplo@correo.com"
[class.ng-invalid]="form.get('email')?.invalid && form.get('email')?.touched"
/>
<small class="text-red-500 mt-1" *ngIf="getFieldError('email')">
{{ getFieldError('email') }}
</small>
</div>
<div class="flex flex-col">
<label class="block mb-2 font-semibold text-gray-700">Contraseña *</label>
<input
pInputText
type="password"
formControlName="password"
class="w-full"
placeholder="Ingrese su contraseña"
[class.ng-invalid]="form.get('password')?.invalid && form.get('password')?.touched"
/>
<small class="text-red-500 mt-1" *ngIf="getFieldError('password')">
{{ getFieldError('password') }}
</small>
</div>
<div class="flex flex-col space-y-3 mt-6">
<button
pButton
type="submit"
class="p-button bg-blue-500 hover:bg-blue-600 text-white w-full"
label="Iniciar Sesión"
icon="pi pi-sign-in"
[loading]="loading"
[disabled]="loading"
></button>
<button
pButton
type="button"
class="p-button p-button-text w-full"
label="¿No tienes cuenta? Registrarse"
icon="pi pi-user-plus"
(click)="goToRegister()"
[disabled]="loading"
></button>
</div>
</form>
</p-card>
</div>
</div>
<p-toast></p-toast>
Componente Register
src/app/components/auth/register.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators, AbstractControl } from '@angular/forms';
import { Router } from '@angular/router';
import { ButtonModule } from 'primeng/button';
import { InputTextModule } from 'primeng/inputtext';
import { MessageService } from 'primeng/api';
import { ToastModule } from 'primeng/toast';
import { AuthService } from '../../../services/auth.service';
import { CardModule } from 'primeng/card';
@Component({
selector: 'app-register',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, ButtonModule, InputTextModule, ToastModule, CardModule],
templateUrl: './register.html',
styleUrl: './register.css',
providers: [MessageService]
})
export class Register {
form: FormGroup;
loading: boolean = false;
constructor(
private fb: FormBuilder,
private router: Router,
private authService: AuthService,
private messageService: MessageService
) {
this.form = this.fb.group({
username: ['', [Validators.required, Validators.minLength(2)]],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]],
confirmPassword: ['', [Validators.required]]
}, { validators: this.passwordMatchValidator });
}
// Validador personalizado para confirmar contraseñas
passwordMatchValidator(control: AbstractControl) {
const password = control.get('password');
const confirmPassword = control.get('confirmPassword');
if (password && confirmPassword && password.value !== confirmPassword.value) {
confirmPassword.setErrors({ passwordMismatch: true });
return { passwordMismatch: true };
}
if (confirmPassword?.hasError('passwordMismatch')) {
confirmPassword.setErrors(null);
}
return null;
}
submit(): void {
if (this.form.valid) {
this.loading = true;
const { confirmPassword, ...registerData } = this.form.value;
this.authService.register(registerData).subscribe({
next: (response) => {
this.messageService.add({
severity: 'success',
summary: 'Éxito',
detail: 'Usuario registrado correctamente'
});
setTimeout(() => {
this.router.navigate(['/clients']);
}, 1000);
},
error: (error) => {
console.error('Error registering user:', error);
this.messageService.add({
severity: 'error',
summary: 'Error',
detail: error.error?.message || 'Error al registrar usuario'
});
this.loading = false;
}
});
} else {
this.markFormGroupTouched();
this.messageService.add({
severity: 'warn',
summary: 'Advertencia',
detail: 'Por favor complete todos los campos requeridos'
});
}
}
goToLogin(): void {
this.router.navigate(['/login']);
}
private markFormGroupTouched(): void {
Object.keys(this.form.controls).forEach(key => {
this.form.get(key)?.markAsTouched();
});
}
getFieldError(fieldName: string): string {
const field = this.form.get(fieldName);
if (field?.errors && field?.touched) {
if (field.errors['required']) return `${fieldName} es requerido`;
if (field.errors['email']) return 'Email no válido';
if (field.errors['minlength']) return `${fieldName} debe tener al menos ${field.errors['minlength'].requiredLength} caracteres`;
if (field.errors['passwordMismatch']) return 'Las contraseñas no coinciden';
}
return '';
}
}
src/app/components/auth/register.html
<div class="min-h-screen flex items-center justify-center">
<div class="max-w-md w-full">
<p-card header="Registrar Usuario" class="shadow-lg">
<form [formGroup]="form" (ngSubmit)="submit()" class="space-y-6">
<div class="flex flex-col">
<label class="block mb-2 font-semibold text-gray-700">Username *</label>
<input
pInputText
type="text"
formControlName="username"
class="w-full"
placeholder="Ingrese su username"
[class.ng-invalid]="form.get('username')?.invalid && form.get('username')?.touched"
/>
<small class="text-red-500 mt-1" *ngIf="getFieldError('username')">
{{ getFieldError('username') }}
</small>
</div>
<div class="flex flex-col">
<label class="block mb-2 font-semibold text-gray-700">Email *</label>
<input
pInputText
type="email"
formControlName="email"
class="w-full"
placeholder="ejemplo@correo.com"
[class.ng-invalid]="form.get('email')?.invalid && form.get('email')?.touched"
/>
<small class="text-red-500 mt-1" *ngIf="getFieldError('email')">
{{ getFieldError('email') }}
</small>
</div>
<div class="flex flex-col">
<label class="block mb-2 font-semibold text-gray-700">Contraseña *</label>
<input
pInputText
type="password"
formControlName="password"
class="w-full"
placeholder="Ingrese su contraseña"
[class.ng-invalid]="form.get('password')?.invalid && form.get('password')?.touched"
/>
<small class="text-red-500 mt-1" *ngIf="getFieldError('password')">
{{ getFieldError('password') }}
</small>
</div>
<div class="flex flex-col">
<label class="block mb-2 font-semibold text-gray-700">Confirmar Contraseña *</label>
<input
pInputText
type="password"
formControlName="confirmPassword"
class="w-full"
placeholder="Confirme su contraseña"
[class.ng-invalid]="form.get('confirmPassword')?.invalid && form.get('confirmPassword')?.touched"
/>
<small class="text-red-500 mt-1" *ngIf="getFieldError('confirmPassword')">
{{ getFieldError('confirmPassword') }}
</small>
</div>
<div class="flex flex-col space-y-3 mt-6">
<button
pButton
type="submit"
class="p-button bg-blue-500 hover:bg-blue-600 text-white w-full"
label="Registrarse"
icon="pi pi-user-plus"
[loading]="loading"
[disabled]="loading"
></button>
<button
pButton
type="button"
class="p-button p-button-text w-full"
label="¿Ya tienes cuenta? Iniciar Sesión"
icon="pi pi-sign-in"
(click)="goToLogin()"
[disabled]="loading"
></button>
</div>
</form>
</p-card>
</div>
</div>
<p-toast></p-toast>
Ajustamos las rutas
src/app/app.routes.ts
import { Routes } from '@angular/router';
import { Login } from './components/auth/login/login';
import { Register } from './components/auth/register/register';
// Client components with aliases
import { Getall as ClientGetall } from './components/client/getall/getall';
import { Create as ClientCreate } from './components/client/create/create';
import { Update as ClientUpdate } from './components/client/update/update';
import { Delete as ClientDelete } from './components/client/delete/delete';
// Product components with aliases
import { Getall as ProductGetall } from './components/product/getall/getall';
import { Create as ProductCreate } from './components/product/create/create';
import { Update as ProductUpdate } from './components/product/update/update';
import { Delete as ProductDelete } from './components/product/delete/delete';
// Sale components with aliases
import { Getall as SaleGetall } from './components/sale/getall/getall';
import { Create as SaleCreate } from './components/sale/create/create';
import { Update as SaleSaleUpdate } from './components/sale/update/update';
import { Delete as SaleDelete } from './components/sale/delete/delete';
export const routes: Routes = [
{
path: '',
redirectTo: '/',
pathMatch: 'full'
},
{
path: "clients",
component: ClientGetall
},
{
path: "clients/new",
component: ClientCreate
},
{
path: "clients/edit/:id",
component: ClientUpdate
},
{
path: "clients/delete/:id",
component: ClientDelete
},
{
path: "products",
component: ProductGetall
},
{
path: "products/new",
component: ProductCreate
},
{
path: "products/edit/:id",
component: ProductUpdate
},
{
path: "products/delete/:id",
component: ProductDelete
},
{
path: "sales",
component: SaleGetall
},
{
path: "sales/new",
component: SaleCreate
},
{
path: "sales/edit/:id",
component: SaleSaleUpdate
},
{
path: "sales/delete/:id",
component: SaleDelete
},
{
path: "login",
component: Login
},
{
path: "register",
component: Register
},
{
path: "**",
redirectTo: "login",
pathMatch: "full"
}
];
Hasta aqui ya funciona Login, Logout y Register.\ Pero hay que ajustar que cuando hasta que no haga login, no mueste ningun componete ni acceda a ninguna ruta, claro a excepcion de Login y register. Y adicionalmente que haga login en el menu solo aparezca Cerrar Sesion y cuando no haya realizado login solo aparezca Iniciar Sesion.
a. Crear un Guard para proteger las rutas:
src/app/guards/authguard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router
) {}
canActivate(): boolean {
if (this.authService.isLoggedIn()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}
b. Actualizar las rutas con proteccion
import { Routes } from '@angular/router';
import { Login } from './components/auth/login/login';
import { Register } from './components/auth/register/register';
import { AuthGuard } from './guards/authguard';
// Client components with aliases
import { Getall as ClientGetall } from './components/client/getall/getall';
import { Create as ClientCreate } from './components/client/create/create';
import { Update as ClientUpdate } from './components/client/update/update';
import { Delete as ClientDelete } from './components/client/delete/delete';
// Product components with aliases
import { Getall as ProductGetall } from './components/product/getall/getall';
import { Create as ProductCreate } from './components/product/create/create';
import { Update as ProductUpdate } from './components/product/update/update';
import { Delete as ProductDelete } from './components/product/delete/delete';
// Sale components with aliases
import { Getall as SaleGetall } from './components/sale/getall/getall';
import { Create as SaleCreate } from './components/sale/create/create';
import { Update as SaleSaleUpdate } from './components/sale/update/update';
import { Delete as SaleDelete } from './components/sale/delete/delete';
export const routes: Routes = [
{
path: '',
redirectTo: '/login',
pathMatch: 'full'
},
{
path: "login",
component: Login
},
{
path: "register",
component: Register
},
{
path: "clients",
component: ClientGetall,
canActivate: [AuthGuard]
},
{
path: "clients/new",
component: ClientCreate,
canActivate: [AuthGuard]
},
{
path: "clients/edit/:id",
component: ClientUpdate,
canActivate: [AuthGuard]
},
{
path: "clients/delete/:id",
component: ClientDelete,
canActivate: [AuthGuard]
},
{
path: "products",
component: ProductGetall,
canActivate: [AuthGuard]
},
{
path: "products/new",
component: ProductCreate,
canActivate: [AuthGuard]
},
{
path: "products/edit/:id",
component: ProductUpdate,
canActivate: [AuthGuard]
},
{
path: "products/delete/:id",
component: ProductDelete,
canActivate: [AuthGuard]
},
{
path: "sales",
component: SaleGetall,
canActivate: [AuthGuard]
},
{
path: "sales/new",
component: SaleCreate,
canActivate: [AuthGuard]
},
{
path: "sales/edit/:id",
component: SaleSaleUpdate,
canActivate: [AuthGuard]
},
{
path: "sales/delete/:id",
component: SaleDelete,
canActivate: [AuthGuard]
},
{
path: "**",
redirectTo: "/login",
pathMatch: "full"
}
];
c. Actualizar el header para mostrar menu dinamico de Iniciar y Cerrar Sesion
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { MenuItem } from 'primeng/api';
import { OverlayBadge } from 'primeng/overlaybadge';
import { TieredMenu } from 'primeng/tieredmenu';
import { CommonModule } from '@angular/common';
import { AuthService } from '../../../services/auth.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-header',
standalone: true,
imports: [CommonModule, OverlayBadge, TieredMenu],
templateUrl: './header.html',
styleUrl: './header.css'
})
export class Header implements OnInit, OnDestroy {
items: MenuItem[] = [];
isLoggedIn = false;
private authSubscription?: Subscription;
constructor(
private router: Router,
private authService: AuthService
) {}
ngOnInit() {
this.updateMenuItems();
// Escuchar cambios en el estado de autenticación
this.authSubscription = this.authService.authState$.subscribe(() => {
this.updateMenuItems();
});
}
ngOnDestroy() {
if (this.authSubscription) {
this.authSubscription.unsubscribe();
}
}
private updateMenuItems(): void {
this.isLoggedIn = this.authService.isLoggedIn();
if (this.isLoggedIn) {
this.items = [
{
label: 'Configuración',
icon: 'pi pi-cog',
command: () => {
console.log('Configuración clicked');
}
},
{
label: 'Información',
icon: 'pi pi-info-circle',
command: () => {
console.log('Información clicked');
}
},
{
separator: true
},
{
label: 'Cerrar sesión',
icon: 'pi pi-sign-out',
command: () => {
this.logout();
}
}
];
} else {
this.items = [
{
label: 'Iniciar sesión',
icon: 'pi pi-sign-in',
command: () => {
this.goToLogin();
}
},
{
label: 'Registrarse',
icon: 'pi pi-user-plus',
command: () => {
this.goToRegister();
}
}
];
}
}
private logout(): void {
this.authService.logout();
this.router.navigate(['/login']);
}
private goToLogin(): void {
this.router.navigate(['/login']);
}
private goToRegister(): void {
this.router.navigate(['/register']);
}
}
d. Actualizar el AuthService para emitir los cambios de estado
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, tap, BehaviorSubject } from 'rxjs';
import { LoginI, LoginResponseI, RegisterI, RegisterResponseI} from '../models/auth';
@Injectable({
providedIn: 'root'
})
export class AuthService {
private baseUrl = 'http://localhost:4000/api';
private tokenKey = 'auth_token';
private authStateSubject = new BehaviorSubject<boolean>(this.hasValidToken());
public authState$ = this.authStateSubject.asObservable();
constructor(private http: HttpClient) {}
login(credentials: LoginI): Observable<LoginResponseI> {
return this.http.post<LoginResponseI>(`${this.baseUrl}/login`, credentials)
.pipe(
tap(response => {
if (response.token) {
this.setToken(response.token);
this.authStateSubject.next(true);
}
})
);
}
register(userData: RegisterI): Observable<RegisterResponseI> {
return this.http.post<RegisterResponseI>(`${this.baseUrl}/register`, userData)
.pipe(
tap(response => {
if (response.token) {
this.setToken(response.token);
this.authStateSubject.next(true);
}
})
);
}
logout(): void {
localStorage.removeItem(this.tokenKey);
this.authStateSubject.next(false);
}
getToken(): string | null {
return localStorage.getItem(this.tokenKey);
}
setToken(token: string): void {
localStorage.setItem(this.tokenKey, token);
}
isLoggedIn(): boolean {
return this.hasValidToken();
}
private hasValidToken(): boolean {
const token = this.getToken();
if (!token) return false;
// Aquí puedes agregar validación adicional del token si es necesario
// Por ejemplo, verificar si el token no ha expirado
return true;
}
}
e. Actualizar el Header.html para reflejar los estados de Inicio y Cerrar Sesion
<!-- Botones derechos alineados totalmente a la derecha -->
<div class="w-full flex justify-end items-center gap-2">
<!-- Solo mostrar notificaciones si está logueado -->
<button
*ngIf="isLoggedIn"
type="button"
class="text-orange-500 bg-gray-100 hover:bg-gray-200 rounded shadow hover:shadow-lg flex items-center justify-center w-10 h-10">
<p-overlaybadge value="2">
<i class="pi pi-bell text-2xl"></i>
</p-overlaybadge>
</button>
<button type="button"
class="text-orange-500 bg-gray-100 hover:bg-gray-200 rounded shadow hover:shadow-lg w-10 h-10 flex items-center justify-center"
(click)="menu.toggle($event)">
<i class="pi pi-users text-xl"></i>
</button>
</div>
<!-- Menú fuera del contenedor de botones -->
<p-tieredmenu #menu [model]="items" [popup]="true" />
f. actualizar el app.html para mostrar layout solo si está logueado
import { Component, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
import { Aside } from './components/layout/aside/aside';
import { Header } from './components/layout/header/header';
import { Footer } from './components/layout/footer/footer';
import { AuthService } from './services/auth.service';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, Header, Footer, Aside, CommonModule],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App {
protected readonly title = signal('appfront');
constructor(public authService: AuthService) {}
}