# Angular Avanzado — Ejemplos Completos

## Resource API (v19+) — Datos Asíncronos como Signals

```typescript
// Resource API — alternativa a RxJS para carga de datos simple
import { resource, linkedSignal } from "@angular/core";

@Component({ standalone: true, ... })
export class UsuariosComponent {
  private http = inject(HttpClient);

  // Filtro reactivo
  readonly busqueda = signal("");
  readonly pagina   = signal(1);

  // Resource — carga datos cuando cambia el request
  readonly usuariosResource = resource({
    request: () => ({
      q: this.busqueda(),
      page: this.pagina(),
    }),

    loader: ({ request }) =>
      firstValueFrom(
        this.http.get<PaginatedResponse<Usuario>>("/api/usuarios", {
          params: { q: request.q, page: String(request.page) },
        })
      ),
  });

  // Signals derivados del resource
  readonly usuarios    = computed(() => this.usuariosResource.value()?.items ?? []);
  readonly cargando    = computed(() => this.usuariosResource.isLoading());
  readonly error       = computed(() => this.usuariosResource.error());
  readonly totalPaginas = computed(() => this.usuariosResource.value()?.totalPaginas ?? 0);
}
```

```html
<!-- Template con @defer y resource -->
<div class="usuarios-lista">
  @if (cargando()) {
    <mat-progress-bar mode="indeterminate" />
  } @else if (error()) {
    <p class="error">Error al cargar: {{ error()?.message }}</p>
  } @else {
    @for (usuario of usuarios(); track usuario.id) {
      <app-tarjeta-usuario [usuario]="usuario" />
    } @empty {
      <p class="vacio">No se encontraron usuarios</p>
    }
  }
</div>
```

---

## Guards, Resolvers e Interceptors Funcionales

```typescript
// guards/auth.guard.ts — funcional, sin clase
import { inject } from "@angular/core";
import { CanActivateFn, Router } from "@angular/router";
import { AuthService } from "../services/auth.service";

export const authGuard: CanActivateFn = (route, state) => {
  const auth   = inject(AuthService);
  const router = inject(Router);

  if (auth.estaAutenticado()) {
    return true;
  }

  return router.createUrlTree(["/login"], {
    queryParams: { returnUrl: state.url },
  });
};

// Guard con roles — composable
export const roleGuard = (rolesPermitidos: string[]): CanActivateFn =>
  () => {
    const auth   = inject(AuthService);
    const router = inject(Router);

    if (rolesPermitidos.includes(auth.usuarioActual()?.rol ?? "")) {
      return true;
    }

    return router.createUrlTree(["/sin-permisos"]);
  };

// resolvers/usuario.resolver.ts
import { ResolveFn } from "@angular/router";
import { inject } from "@angular/core";
import { UsuariosService } from "../services/usuarios.service";

export const usuarioResolver: ResolveFn<Usuario> = (route) => {
  return inject(UsuariosService).obtenerPorId(route.paramMap.get("id")!);
};

// interceptors/auth.interceptor.ts
import { HttpInterceptorFn } from "@angular/common/http";
import { inject } from "@angular/core";
import { AuthService } from "../services/auth.service";

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const auth = inject(AuthService);
  const token = auth.token();

  if (!token) return next(req);

  return next(req.clone({
    setHeaders: { Authorization: `Bearer ${token}` },
  }));
};

// app.config.ts — registrar todo funcionalmente
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes, withComponentInputBinding()),
    provideHttpClient(withInterceptors([authInterceptor, errorInterceptor])),
    provideAnimationsAsync(),
  ],
};
```

---

## SSR / SSG con Angular Universal

```typescript
// Detectar si estamos en el servidor o el navegador
import { isPlatformBrowser, isPlatformServer } from "@angular/common";
import { PLATFORM_ID, inject } from "@angular/core";

@Injectable({ providedIn: "root" })
export class PlatformService {
  private platformId = inject(PLATFORM_ID);

  readonly esBrowser = isPlatformBrowser(this.platformId);
  readonly esServidor = isPlatformServer(this.platformId);
}

// Uso en componente — evitar acceso a APIs del browser en servidor
@Component({ standalone: true, ... })
export class MapaComponent implements OnInit {
  private platform = inject(PlatformService);

  ngOnInit(): void {
    if (!this.platform.esBrowser) return;

    import("leaflet").then(L => {
      const mapa = L.map("mapa").setView([19.4326, -99.1332], 13);
      L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(mapa);
    });
  }
}

// Transferir estado del servidor al cliente — evitar doble petición
import { TransferState, makeStateKey } from "@angular/core";

const CLAVE_PRODUCTOS = makeStateKey<Producto[]>("productos");

@Injectable({ providedIn: "root" })
export class ProductosService {
  private http = inject(HttpClient);
  private transferState = inject(TransferState);

  obtenerTodos(): Observable<Producto[]> {
    if (this.transferState.hasKey(CLAVE_PRODUCTOS)) {
      const productos = this.transferState.get(CLAVE_PRODUCTOS, []);
      this.transferState.remove(CLAVE_PRODUCTOS);
      return of(productos);
    }

    return this.http.get<Producto[]>("/api/productos").pipe(
      tap(productos => this.transferState.set(CLAVE_PRODUCTOS, productos))
    );
  }
}
```

---

## Angular Zoneless (Experimental v18+)

```typescript
// app.config.ts — habilitar zoneless
import { provideExperimentalZonelessChangeDetection } from "@angular/core";

export const appConfig: ApplicationConfig = {
  providers: [
    provideExperimentalZonelessChangeDetection(),
  ],
};

// Con zoneless, la detección de cambios sólo ocurre cuando:
// 1. Un signal cambia (automático)
// 2. Se llama markForCheck() manualmente (para casos especiales)
// 3. Un async pipe emite un valor

// Componente completamente reactivo — cero zone.js
@Component({
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <p>Contador: {{ contador() }}</p>
    <button (click)="incrementar()">+</button>
  `,
})
export class ContadorComponent {
  readonly contador = signal(0);

  incrementar(): void {
    this.contador.update(n => n + 1);
  }
}
```
