streakode

Preguntas de entrevista de Angular con respuestas

En Angular moderno se pregunta por signals, inyección de dependencias, plantillas con control de flujo, el router y RxJS. Estas preguntas son de nivel 2, las que tienen trampa, y cada una trae su explicación.

Piensa la respuesta antes de abrirla: la explicación dice por qué fallan las demás opciones. En Streakode tienes 26 preguntas como estas de Angular, y un simulacro de 10 preguntas en 15 minutos, sin corrección hasta el final, para ver cómo irías en una entrevista de verdad.

12 preguntas de Angular

Temas: Control de flujo · Enlace bidireccional · Inyección de dependencias · Plantillas · Router · RxJS · Signals.

  1. 1 · Completa el hueco · nivel 2

    Completa para numerar los usuarios y mostrar un aviso si no hay ninguno.

    @for (user of users(); track user.id; let i = ___) {  <li>{{ i + 1 }}. {{ user.name }}</li>} ___ {  <li>No hay usuarios</li>}
    • $index
    • @empty
    • index
    • $count
    • @else
    • @none
    Ver la respuesta

    Respuesta: $index · @empty

    @for (user of users(); track user.id; let i = $index) {  <li>{{ i + 1 }}. {{ user.name }}</li>} @empty {  <li>No hay usuarios</li>}

    @for ofrece variables implícitas como $index, $count, $first y $last, y se les puede dar un alias con let. El bloque para una colección vacía es @empty.

  2. 2 · Elige la respuesta · nivel 2

    ¿Qué le falta a este bucle para compilar?

    @for (item of items) {  <li>{{ item.name }}</li>}
    • Atrack item.id, o la expresión que identifique cada elemento
    • BUn *ngFor en el <li>
    • CDeclarar let item
    • DNada, compila tal cual
    Ver la respuesta

    Respuesta: A. track item.id, o la expresión que identifique cada elemento

    En @for, track es obligatorio: le dice a Angular cómo identificar cada elemento para reutilizar el DOM en vez de recrearlo.

  3. 3 · Completa el hueco · nivel 2

    Completa un slider cuyo valor se sincroniza en los dos sentidos con el padre.

    // slider.tsexport class Slider {  value = ___(0);} <!-- plantilla del padre --><app-slider ___="volume" />
    • model
    • [(value)]
    • input
    • (value)
    • [value]
    • signal
    Ver la respuesta

    Respuesta: model · [(value)]

    // slider.tsexport class Slider {  value = model(0);} <!-- plantilla del padre --><app-slider [(value)]="volume" />

    model() declara una entrada que el propio componente puede modificar. El padre la enlaza con la «caja de plátanos» [( )], y así recibe los cambios en su signal volume.

  4. 4 · Elige la respuesta · nivel 2

    ¿Qué pasa cuando se llama a load() al pulsar un botón?

    export class Profile {  load() {    const http = inject(HttpClient);    return http.get('/api/me');  }}
    • ALanza un error: inject() solo funciona en un contexto de inyección
    • BFunciona igual que en el constructor
    • CDevuelve undefined
    • DCrea un HttpClient nuevo en cada llamada
    Ver la respuesta

    Respuesta: A. Lanza un error: inject() solo funciona en un contexto de inyección

    inject() solo se puede llamar mientras Angular construye algo: en el constructor, en los inicializadores de campos o en funciones como los guards. Desde un clic falla con NG0203. Hay que inyectarlo en un campo.

  5. 5 · Completa el hueco · nivel 2

    Completa: inyecta HttpClient sin constructor y guarda el usuario en un signal.

    export class Profile {  private http = ___(HttpClient);  user = ___<User | null>(null);}
    • inject
    • signal
    • computed
    • new
    • effect
    • input
    Ver la respuesta

    Respuesta: inject · signal

    export class Profile {  private http = inject(HttpClient);  user = signal<User | null>(null);}

    inject() obtiene una dependencia en un contexto de inyección, como los inicializadores de campos. signal() crea un estado reactivo con valor inicial.

  6. 6 · Elige la respuesta · nivel 2

    ¿Qué hace @let en esta plantilla?

    @let total = price() * qty();<p>{{ total }} €</p>@if (total > 100) {  <p>Envío gratis</p>}
    • ADeclara una variable local de solo lectura en la plantilla
    • BCrea un signal editable
    • CAñade una propiedad nueva a la clase del componente
    • DNada: @let no existe en Angular
    Ver la respuesta

    Respuesta: A. Declara una variable local de solo lectura en la plantilla

    @let guarda el resultado de una expresión para reutilizarlo en la plantilla sin repetir el cálculo. Se actualiza solo cuando cambian sus signals, pero no se puede reasignar.

  7. 7 · Elige la respuesta · nivel 2

    Con provideRouter(routes, withComponentInputBinding()), ¿cómo recibe el componente el :id de la ruta products/:id?

    • ACon una entrada: id = input<string>()
    • BSolo con inject(ActivatedRoute).snapshot.params
    • CComo parámetro del constructor
    • DCon el decorador @Param('id')
    Ver la respuesta

    Respuesta: A. Con una entrada: id = input<string>()

    withComponentInputBinding pasa los parámetros de ruta, los query params y los datos de la ruta a las entradas del componente que tengan el mismo nombre. ActivatedRoute sigue funcionando, pero ya no hace falta.

  8. 8 · Elige la respuesta · nivel 2

    ¿Qué ocurre si el usuario no ha iniciado sesión?

    export const authGuard: CanActivateFn = () => {  const auth = inject(Auth);  const router = inject(Router);  return auth.isLoggedIn() || router.createUrlTree(['/login']);};
    • ARedirige a /login
    • BBloquea la navegación y se queda en la página actual
    • CLanza un error porque un guard debe devolver boolean
    • DNavega a la ruta y después redirige
    Ver la respuesta

    Respuesta: A. Redirige a /login

    Un guard puede devolver true, false o un UrlTree. Si devuelve un UrlTree, el router cancela la navegación y va a esa URL. Es mejor que llamar a router.navigate dentro del guard.

  9. 9 · Elige la respuesta · nivel 2

    ¿Qué ocurre al ejecutar esta línea?

    const numbers$ = of(1, 2, 3).pipe(  tap((n) => console.log(n)));
    • ANo se imprime nada: el Observable no empieza hasta que alguien se suscribe
    • BSe imprimen 1, 2 y 3
    • CSe imprime solo 3, el último valor
    • DLanza un error por no tener subscribe
    Ver la respuesta

    Respuesta: A. No se imprime nada: el Observable no empieza hasta que alguien se suscribe

    Un Observable es perezoso: pipe solo describe lo que pasará con los valores. Hasta que algo llame a subscribe() (o a un async pipe), tap no se ejecuta ni se emite nada.

  10. 10 · Encuentra el fallo · nivel 2

    Al salir de la página, el contador sigue sumando en segundo plano. ¿Qué línea tiene el fallo?

    1export class Ticker {2  count = 0;3 4  constructor() {5    interval(1000).subscribe(() => this.count++);6  }7}
    Ver la respuesta

    El fallo está en la línea 5: interval(1000).subscribe(() => this.count++);

    La suscripción nunca se cancela y sobrevive al componente. Dentro del constructor basta con interval(1000).pipe(takeUntilDestroyed()).subscribe(...). Otra opción es convertirlo en signal con toSignal.

  11. 11 · Encuentra el fallo · nivel 2

    El componente entra en un bucle infinito. ¿Qué línea lo provoca?

    1export class Counter {2  count = signal(0);3 4  constructor() {5    effect(() => {6      this.count.set(this.count() + 1);7    });8  }9}
    Ver la respuesta

    El fallo está en la línea 6: this.count.set(this.count() + 1);

    El effect lee count y a la vez lo modifica, así que cada ejecución programa la siguiente. Para derivar un valor de otro, usa computed. Si de verdad hay que leer sin suscribirse, está untracked().

  12. 12 · Elige la respuesta · nivel 2

    ¿Qué imprime este código?

    const price = signal(10);const double = computed(() => price() * 2);price.set(100);console.log(double());
    • A200
    • B20
    • C10
    • DError: computed es de solo lectura
    Ver la respuesta

    Respuesta: A. 200

    computed guarda qué signals lee y se recalcula cuando alguno cambia. Tras price.set(100), double() vale 200.

Preguntas de entrevista de otras rutas

Ver todos los ejercicios de Angular