Angular

Angular

Tutorial Angular: La directiva @for en Angular

Angular directiva @for: sintaxis estructural para bucles. Aprende a usar la directiva @for en Angular para iterar eficientemente sobre colecciones en plantillas.

La directiva @for representa una evolución significativa en la forma de manejar iteraciones y listas en aplicaciones Angular. Esta directiva no solo mejora la claridad del código, sino que también optimiza el rendimiento en la manipulación de datos dinámicos.

Conceptos clave de la directiva @for

Introducción a la directiva @for

 - Las directivas @for representan una evolución en la iteración de colecciones dentro de las plantillas de Angular, ofreciendo una sintaxis intuitiva y eficiente, comparada y en ciertos aspectos superior a la de *ngFor.

 - Permite renderizar de forma repetida un bloque de contenido para cada elemento en una colección, con capacidades avanzadas de seguimiento y optimización de rendimiento.

Sintaxis básica

  - La estructura de @for sigue el patrón de un bucle for-of tradicional, pero está diseñada específicamente para el contexto de las plantillas Angular.
  - Ejemplo básico: 

<ul>
  @for (item of items; track item.id) {
    <li>{{ item.name }}</li>
  }
</ul>

Funcionalidad de seguimiento

  - El seguimiento es crucial para la eficiencia, permitiendo a Angular identificar y reutilizar elementos del DOM de manera eficaz.

  - La expresión track es obligatoria en los bucles @for para optimizar las operaciones DOM al modificar la colección. Esta propiedad permite identificar de manera única cada elemento dentro de la lista para poder manejar eficientemente las actualizaciones del DOM.

Manejo de listas vacías

  - La directiva @for incluye una cláusula @empty para manejar elegantemente el caso de colecciones vacías.

  - Ejemplo:

<ul>
  @for (item of items; track item.id) {
    <li>{{ item.name }}</li>
  } @empty {
    <li>No hay elementos.</li>
  }
</ul>

Variables de contexto en @for

La directiva @for proporciona varias variables de contexto implícitas que pueden ser muy útiles al trabajar con iteraciones. Estas variables ofrecen información adicional sobre cada elemento en la iteración.

  - $index: es una variable implícita que proporciona la posición actual en la iteración. Esto es útil cuando necesitas numerar los elementos o realizar acciones basadas en su posición.

@for (item of items; track item.id; let i = $index) {
    <div> {{ i + 1 }}. {{ item.name }} </div>
}

  - $first y $last: Booleanos que indican si el elemento es el primero o el último de la iteración.

@for (item of items; track item.id; let isFirst = $first; let isLast = $last) {
    <div [class.first-item]="isFirst" [class.last-item]="isLast" [class.middle-item]="!isFirst && !isLast">
        @if (isFirst) {(Primero)}
        {{ item.name }}
        @if (isLast) {(Último)}
    </div>
}

  - $even y $odd: Booleanos que indican si el índice del elemento es par o impar.

@for (item of items; track item.id; let even = $even; let odd = $odd) {
    <div [class.even-row]="even" [class.odd-row]="odd">
        {{ item.name }}
    </div>
}

  - $count: Proporciona el número total de elementos en la iteración.

@for (item of items; track item.id; let i = $index; let total = $count) {
    <div>{{ item.name }} ({{ i + 1 }} de {{ total }})</div>
}

Estas variables de contexto son especialmente útiles para:

  • Numerar elementos
  • Aplicar estilos diferentes a elementos pares e impares
  • Agregar lógica especial para el primer o último elemento
  • Mostrar la posición relativa de un elemento en la lista
  • Implementar paginación o carga incremental basada en el índice

Estas variables permiten un control preciso sobre el renderizado y comportamiento de cada elemento en la iteración.

Ejemplo combinando múltiples variables de contexto:

@for (item of items; 
track item.id; 
      let i = $index; 
      let f = $first; 
      let l = $last; 
      let e = $even; 
      let o = $odd; 
      let c = $count) {
  <div [class]="{ 'first': f, 'last': l, 'even': e, 'odd': o }">
    {{ item.name }} ({{ i + 1 }}/{{ c }}) 
    {{ f ? '🏆' : l ? '🏁' : '' }}
    {{ e ? 'Par' : 'Impar' }}
  </div>
}

Este ejemplo demuestra cómo se pueden utilizar múltiples variables de contexto en una sola iteración para crear una visualización rica y dinámica de los elementos de la lista.

Cómo se vería este ejemplo en el navegador:

CSS utilizado en este ejemplo:

div {
    padding: 10px;
    margin: 5px 0;
    border-radius: 5px;
    font-family: Arial, sans-serif;
}

.first {
    background-color: #e6f7ff;
    border: 2px solid #1890ff;
    font-weight: bold;
}

.last {
    background-color: #fff1f0;
    border: 2px solid #ff4d4f;
    font-style: italic;
}

.even {
    background-color: #f0f5ff;
}

.odd {
    background-color: #fff7e6;
}

.first.even, .last.even {
    border-left: 5px solid #52c41a;
}

.first.odd, .last.odd {
    border-left: 5px solid #faad14;
}

Ejemplos avanzados de uso

Anidación de directivas estructurales

La directiva @for se puede combinar con otras directivas estructurales para crear lógica más compleja.

<div>
    @for (category of categories; track category.id) {
    <h2>{{ category.name }}</h2>
    <ul>
        @for (item of category.items; track item.id) {
        <li>{{ item.name }}</li>
        }
    </ul>
    }
</div>

Uso con componentes personalizados

La directiva @for se puede utilizar eficientemente con componentes personalizados.

<app-product-grid>
  @for (product of products; track product.id) {
    <app-product-card [product]="product"></app-product-card>
  } @empty {
    <app-no-products-found></app-no-products-found>
  }
</app-product-grid>

Manipulación dinámica de listas

La directiva @for es especialmente útil para manejar listas que cambian dinámicamente.

interface Todo {
  id: number;
  task: string;
}
@Component({
  selector: 'app-todo-list',
  template: `
    <ul>
      @for (todo of todos(); track todo.id) {
        <li>
          {{ todo.task }}
          <button (click)="removeTodo(todo.id)">Eliminar</button>
        </li>
      } @empty {
        <li>No hay tareas pendientes.</li>
      }
    </ul>
    <button (click)="addTodo()">Agregar tarea</button>
  `
})
export class TodoListComponent {
  todos = signal<Todo[]>([]);

  addTodo() {
    this.todos.update(todos => [...todos, { id: Date.now(), task: 'Nueva tarea' }]);
  }

  removeTodo(id: number) {
    this.todos.update(todos => todos.filter(todo => todo.id !== id));
  }
}

En este ejemplo, la directiva @for se actualiza automáticamente cuando se modifican los todos, gracias al uso de señales (signals) para manejar el estado.

Observables

La directiva @for puede trabajar eficientemente con observables, aprovechando el pipe async para manejar la suscripción y desuscripción automática. El bucle se ejecuta cada vez que el observable emite un nuevo valor.

import { Component } from '@angular/core';
import { Observable, interval } from 'rxjs';
import { map, take } from 'rxjs/operators';

@Component({
  selector: 'app-observable-example',
  template: `
    <ul>
      @for (item of items$ | async; track item.id) {
        <li>{{ item.name }}</li>
      } @empty {
        <li>Cargando items...</li>
      }
    </ul>
  `
})
export class ObservableExampleComponent {
  items$: Observable<Array<{id: number, name: string}>>;

  constructor() {
    this.items$ = interval(1000).pipe(
      map(i => [
        {id: i + 1, name: `Item ${i + 1}`},
        {id: i + 2, name: `Item ${i + 2}`},
        {id: i + 3, name: `Item ${i + 3}`}
      ]),
      take(5)
    );
  }
}

En este ejemplo, items$ es un observable que emite un nuevo array de items cada segundo, durante 5 segundos. La directiva @for, combinada con el pipe async, maneja automáticamente la suscripción al observable y la actualización de la vista cada vez que se emite un nuevo valor. La cláusula @empty se muestra mientras el observable no ha emitido ningún valor, proporcionando una experiencia de carga fluida.

Uso de @for con métodos y expresiones complejas

La directiva @for no se limita a iterar sobre arrays simples; también puede trabajar con métodos, funciones y expresiones más complejas, tanto en la fuente de datos como en la cláusula track. Esto permite una mayor flexibilidad, como filtrar elementos dinámicamente o implementar lógica personalizada para el tracking.

1.- Uso de métodos para la fuente de datos

import { Component, signal } from '@angular/core';

interface Item {
  id: number;
  name: string;
  category: string;
}

@Component({
  selector: 'app-filtered-list',
  template: `
    <h2>Lista Filtrada</h2>
    <input [value]="filterText()" (input)="updateFilter($event)" placeholder="Filtrar por nombre">
    @for (item of getFilteredItems(); track getItemId(item)) {
      <div class="item">{{item.name}} ({{item.category}})</div>
    } @empty {
      <div>No se encontraron items.</div>
    }
  `
})
export class FilteredListComponent {
  items = signal<Item[]>([
    { id: 1, name: 'Manzana', category: 'Frutas' },
    { id: 2, name: 'Banana', category: 'Frutas' },
    { id: 3, name: 'Zanahoria', category: 'Verduras' },
    { id: 4, name: 'Brócoli', category: 'Verduras' },
    { id: 5, name: 'Leche', category: 'Lácteos' }
  ]);

  filterText = signal('');

  getFilteredItems() {
    const filter = this.filterText().toLowerCase();
    return this.items().filter(item => 
      item.name.toLowerCase().includes(filter)
    );
  }

  getItemId(item: Item) {
    return item.id;
  }

  updateFilter(event: Event) {
    const target = event.target as HTMLInputElement;
    this.filterText.set(target.value);
  }
}

En este ejemplo, getFilteredItems() se usa como fuente de datos para @for. Este método filtra dinámicamente los items basándose en el término de búsqueda actual.

2.- Expresiones complejas en la cláusula track

@Component({
  selector: 'app-complex-list',
  template: `
    <ul>
      @for (item of complexItems(); track getUniqueIdentifier(item)) {
        <li>{{ item.name }} ({{ item.category }})</li>
      }
    </ul>
  `
})
export class ComplexListComponent {
  complexItems = signal<Array<{name: string, category: string, subId: number}>>([
    {name: 'Item 1', category: 'A', subId: 101},
    {name: 'Item 2', category: 'B', subId: 102},
    {name: 'Item 3', category: 'A', subId: 103}
  ]);

  getUniqueIdentifier(item: {name: string, category: string, subId: number}) {
    return `${item.category}-${item.subId}`;
  }
}

Aquí, getUniqueIdentifier() se utiliza en la cláusula track para generar un identificador único para cada item, combinando la categoría y el subId.

3.- Uso de funciones de filtrado y ordenación

@Component({
  selector: 'app-sorted-filtered-list',
  template: `
    <ul>
      @for (item of getSortedAndFilteredItems(); track item.id) {
        <li>{{ item.name }} - ${{ item.price }}</li>
      } @empty {
        <li>No hay productos disponibles.</li>
      }
    </ul>
  `
})
export class SortedFilteredListComponent {
  items = signal<Array<{id: number, name: string, price: number, inStock: boolean}>>([
    {id: 1, name: 'Producto A', price: 10, inStock: true},
    {id: 2, name: 'Producto B', price: 15, inStock: false},
    {id: 3, name: 'Producto C', price: 20, inStock: true}
  ]);

  getSortedAndFilteredItems() {
    return this.items()
      .filter(item => item.inStock)
      .sort((a, b) => a.price - b.price);
  }
}

En este caso, getSortedAndFilteredItems() filtra los productos para mostrar solo los que están en stock y los ordena por precio.

Estos ejemplos demuestran cómo @for puede utilizarse con métodos y expresiones complejas para crear listas dinámicas y flexibles. Al combinar @for con funciones personalizadas, puedes implementar lógica de negocio sofisticada directamente en tus plantillas, manteniendo al mismo tiempo un código limpio y mantenible.

Certifícate en Angular con CertiDevs PLUS

Ejercicios de esta lección La directiva @for en Angular

Evalúa tus conocimientos de esta lección La directiva @for en Angular con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Signals en Angular

Angular
Puzzle

Guards funcionales

Angular
Test

Decodificar JWT en Angular

Angular
Test

Servicio con HttpClient

Angular
Código

Ciclo de vida de componentes en Angular

Angular
Test

Gestión de productos de Fake Store API

Angular
Proyecto

Data binding en Angular

Angular
Test

Routes sin módulos en Angular

Angular
Código

Router en Angular

Angular
Test

Instalación de Angular

Angular
Puzzle

Route Guards basados en interfaces

Angular
Código

La directiva @if en Angular

Angular
Puzzle

Formularios reactivos en Angular

Angular
Código

Servicios en Angular

Angular
Puzzle

Interceptor funcional

Angular
Test

Servicio con Array

Angular
Código

La directiva @for en Angular

Angular
Puzzle

Interceptores HTTP

Angular
Código

Componentes standalone true

Angular
Puzzle

Formularios con ngModel en Angular

Angular
Puzzle

Routes en Angular

Angular
Test

Comunicación entre componentes Angular

Angular
Test

Parámetros en rutas con ActivatedRoute

Angular
Test

CRUD de Restaurantes y Platos

Angular
Proyecto

Tablas en Angular Material

Angular
Puzzle

Formulario de registro de usuarios

Angular
Proyecto

Instalación y uso de NgBoostrap

Angular
Puzzle

Desarrollo de componentes Angular

Angular
Test

JWT en Angular

Angular
Código

Formularios reactivos en Angular

Angular
Puzzle

Formularios en Angular Material

Angular
Puzzle

Layout con Angular Material

Angular
Puzzle

Effects en Angular

Angular
Test

Data binding

Angular
Código

HttpClient en servicios de Angular

Angular
Puzzle

Desarrollo de módulos Angular

Angular
Puzzle

Comandos Angular CLI

Angular
Puzzle

Subir archivo en formularios

Angular
Test

La directiva routerLink en Angular

Angular
Test

Todas las lecciones de Angular

Accede a todas las lecciones de Angular y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Instalación Angular

Angular

Introducción Y Entorno

Comandos Angular Cli

Angular

Introducción Y Entorno

Desarrollo De Componentes Angular

Angular

Componentes

Data Binding En Angular

Angular

Componentes

Ciclo De Vida De Componentes En Angular

Angular

Componentes

Comunicación Entre Componentes Angular

Angular

Componentes

La Directiva @If En Angular

Angular

Componentes

La Directiva @For En Angular

Angular

Componentes

Componentes Standalone

Angular

Componentes

Desarrollo De Módulos Angular

Angular

Módulos

Routes En Angular

Angular

Enrutado Y Navegación

Router En Angular

Angular

Enrutado Y Navegación

La Directiva Routerlink En Angular

Angular

Enrutado Y Navegación

Parámetros En Rutas Con Activatedroute

Angular

Enrutado Y Navegación

Routes Sin Módulos En Angular

Angular

Enrutado Y Navegación

Servicios En Angular

Angular

Servicios E Inyección De Dependencias

Httpclient En Servicios De Angular

Angular

Servicios E Inyección De Dependencias

Formularios Con Ngmodel En Angular

Angular

Formularios

Formularios Reactivos En Angular

Angular

Formularios

Subir Archivo En Formularios

Angular

Formularios

Layout Con Angular Material

Angular

Integración Con Angular Material

Tablas En Angular Material

Angular

Integración Con Angular Material

Formularios En Angular Material

Angular

Integración Con Angular Material

Instalación Y Uso De Ngboostrap

Angular

Integración Con Bootstrap Css

Signals En Angular

Angular

Signals Y Reactividad

Effects En Angular

Angular

Signals Y Reactividad

Route Guards Basados En Interfaces

Angular

Autenticación Y Autorización

Guards Funcionales

Angular

Autenticación Y Autorización

Interceptores Http Basados En Interfaz

Angular

Autenticación Y Autorización

Interceptores Http Funcionales

Angular

Autenticación Y Autorización

Seguridad Jwt En Angular

Angular

Autenticación Y Autorización

Decodificar Tokens Jwt En Angular

Angular

Autenticación Y Autorización

Certificados de superación de Angular

Supera todos los ejercicios de programación del curso de Angular y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

En esta lección

Objetivos de aprendizaje de esta lección

  1. Entender la sintaxis y funcionalidad de @for.
  2. Comprender el uso de la función track en @for para mejorar el rendimiento al actualizar listas.
  3. Conocer las variables de contexto disponibles en @for.
  4. Conocer las aplicaciones avanzadas de @for, incluyendo su uso con observables, métodos de filtrado y ordenación, y expresiones complejas en la cláusula track.