refactor(front): drop auth structural directives in favor of native @if (closes #248)#11
Merged
Merged
Conversation
Closes ticket #248 (tech-debt/front/auth/angular-21). Eliminates the four custom structural directives that wrapped TemplateRef + ViewContainerRef: - *appHasPermission - *appHasAnyPermission - *appHasAllPermissions - *appIfLoggedIn and migrates every consumer to Angular 21's native control flow (`@if`), which is type-safe, lives in the template language (no imports), and closes the family of bugs caused when the directive's @input() name didn't match its structural selector (real incident in the sibling devia-saas repo: an "Admin panel" button never rendered for ADMIN users because the input was `hasPermission` while the selector desugars to `[appHasPermission]`). Changes: - AuthService gains a template-friendly `isLoggedIn()` helper that reads the `token` signal so `@if` re-evaluates automatically. - The single consumer (home.component) now uses `@if (auth.isLoggedIn()) { … } @else { … }` and exposes `auth` as a public injected member. Component becomes OnPush and drops the CommonModule import (no longer needed). - 4 directives + 4 specs + the directives barrel `index.ts` are deleted. The libs/auth root barrel no longer re-exports them. - README.md and USAGE.md under libs/auth/directives rewritten as a before/after migration guide so derived projects can replay the refactor mechanically. Verification: - npm run test:front → 63 / 63 passing (the 31 directive-spec tests removed along with the directives themselves). - npx nx run front:lint → clean. - npx nx run front:build (development) → succeeds. - No remaining occurrences of the four directive selectors in application code; surviving references live only inside the migration docs as historical examples. Breaking change: any external consumer of the lib that still imports HasPermissionDirective / HasAnyPermissionDirective / HasAllPermissionsDirective / IfLoggedInDirective will need to follow the table in directives/USAGE.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Cierra ticket #248 (
tech-debt,front,auth,angular-21, prioridad alta).Elimina las cuatro directivas estructurales custom basadas en
TemplateRef + ViewContainerRefy migra al control flow nativo de Angular 21 (@if).*appHasPermission→@if (auth.hasPermission(...))*appHasAnyPermission→@if (auth.hasAnyPermission([...]))*appHasAllPermissions→@if (auth.hasAllPermissions([...]))*appIfLoggedIn→@if (auth.isLoggedIn()) { ... } @else { ... }Se cazó un bug por el patrón "nombre-del-input ≠ selector estructural" — el azúcar
*appHasPermission="X"desugarea en[appHasPermission]="X", pero el@Input()interno se llamabahasPermission, así que el valor nunca llegaba y el botón "Panel admin" no aparecía nunca. El alias@Input('appHasPermission')mitigaba, pero@ifcierra la categoría entera.Tabla antes → después
<el *appHasPermission=\"Permission.ADMIN\">…</el>@if (auth.hasPermission(Permission.ADMIN)) { <el>…</el> }<el *appHasAnyPermission=\"[A, B]\">…</el>@if (auth.hasAnyPermission([A, B])) { <el>…</el> }<el *appHasAllPermissions=\"[A, B]\">…</el>@if (auth.hasAllPermissions([A, B])) { <el>…</el> }<el *appIfLoggedIn=\"true\">…</el>@if (auth.isLoggedIn()) { <el>…</el> }<el *appIfLoggedIn=\"false\">…</el>@if (!auth.isLoggedIn()) { <el>…</el> }@if (auth.isLoggedIn()) { … } @else { … }Breaking change (API de plantillas)
Cualquier proyecto que importe estas directivas dejará de compilar:
HasPermissionDirectiveHasAnyPermissionDirectiveHasAllPermissionsDirectiveIfLoggedInDirectiveMigración mecánica documentada en
apps/front/src/app/libs/auth/directives/USAGE.md.Cambios
apps/front/src/app/libs/auth/services/auth.service.ts: nuevo helper públicoisLoggedIn()que lee el signaltoken, lo que hace que@ifre-evalúe automáticamente al hacer login/logout o rotar el access token.apps/front/src/app/pages/home/home.component.html: único consumidor real; migrado a@if (auth.isLoggedIn()) { … } @else { … }.apps/front/src/app/pages/home/home.component.ts: standalone +OnPush, exponeauthcomo inyección pública. SacaCommonModuleeIfLoggedInDirectivedeimports.apps/front/src/app/libs/auth/index.ts: deja de re-exportar las 4 directivas.directives/index.ts.apps/front/src/app/libs/auth/directives/README.mdyUSAGE.md: reescritos como guía de migración (tabla antes/después, ejemplos prácticos, explicación del bug histórico).Test plan
npm run test:front→ 63 / 63 verde (los 31 tests removidos son los de las propias directivas borradas).npx nx run front:lint→ clean.npx nx run front:build --configuration=development→ succeeds.apps/front/src/**/*.{html,ts}: 0 ocurrencias deappHasPermission|appHasAnyPermission|appHasAllPermissions|appIfLoggedIn(sólo quedan referencias en docs como tabla histórica).*ngIf/*ngForen plantillas tocadas.HomeComponentsigue standalone +inject()+OnPush./, verificar que el card de "no logueado" aparece; hacer login; verificar que el card de "logueado" reemplaza al anterior sin recargar; logout vuelve al estado inicial.Criterios de aceptación del ticket #248
*ngIf/*ngForen plantillas tocadas.NgModule) coninject().