El problema que todo desarrollador Angular conoce
\n\n¿Alguna vez has intentado mostrar en una plantilla de Angular el contenido de un objeto JavaScript normal usando *ngFor y de repente te diste cuenta: "¿Y cómo obtengo la clave y el valor?" Porque *ngFor por defecto solo funciona con arrays. Y un objeto no es un array.
Esta pregunta es una de las más populares en Stack Overflow (miles de visitas y decenas de respuestas). Los desarrolladores no buscan solo una solución, sino la solución correcta, aquella que no mate el rendimiento de la aplicación, especialmente cuando el objeto es grande o se actualiza con frecuencia.
\n\nEn este artículo no solo daremos una respuesta. Analizaremos todos los enfoques principales, desde los simples hasta los avanzados, y mostraremos cómo escribir código que funcione de forma rápida y estable. ¡Allá vamos!
\n\n¿Por qué *ngFor no funciona con objetos "de fábrica"?
\n\nLa directiva *ngFor en Angular está diseñada para estructuras iterables (Iterable). Un array es una estructura iterable. Un objeto no lo es. Cuando escribes:
<div *ngFor="let item of myObject">{{ item }}</div>\n\nAngular mostrará un error: "Cannot find a differ supporting object '...'". Angular simplemente no sabe cómo iterar un objeto.
\n\nPero en la práctica, a menudo necesitamos mostrar datos de un objeto: configuraciones, diccionarios, respuestas de API, donde las claves tienen significado (por ejemplo, { name: 'Ana', age: 25, city: 'Madrid' }).
La forma más simple: Object.keys() y Object.entries()
\n\nLa respuesta más popular y obvia en Stack Overflow es convertir el objeto en un array directamente en la plantilla o en el componente.
\n\nOpción 1: Object.keys() — obtenemos solo las claves
\n\n// En el componente (TypeScript)\nexport class MyComponent {\n myObject = { name: 'Ana', age: 25, city: 'Madrid' };\n}\n\n<!-- En la plantilla -->\n<div *ngFor="let key of objectKeys(myObject)">\n Clave: {{ key }}, Valor: {{ myObject[key] }}\n</div>\n\nPero aquí hay un detalle: objectKeys debe estar disponible en la plantilla. O la hacemos un método del componente, o la colocamos en una propiedad separada:
// Componente\nexport class MyComponent {\n myObject = { name: 'Ana', age: 25, city: 'Madrid' };\n objectKeys = Object.keys; // simplemente una referencia a la función nativa\n}\n\nEsto funciona, pero llama a la función en cada verificación de cambios (change detection). Si el objeto es grande, esto afecta el rendimiento.
\n\nOpción 2: Object.entries() — claves y valores de una vez
\n\n// Componente\nexport class MyComponent {\n myObject = { name: 'Ana', age: 25, city: 'Madrid' };\n get entries() {\n return Object.entries(this.myObject);\n }\n}\n\n<!-- Plantilla -->\n<div *ngFor="let entry of entries">\n Clave: {{ entry[0] }}, Valor: {{ entry[1] }}\n</div>\n\nO con desestructuración (más legible):
\n\n<div *ngFor="let [key, value] of entries">\n Clave: {{ key }}, Valor: {{ value }}\n</div>\n\nVentajas: simple, claro, visual.
\nDesventajas: el método entries se llamará en cada ciclo de detección de cambios. Para objetos estáticos está bien, para objetos dinámicos puede ser un problema.
Rendimiento: ¿por qué es importante?
\n\nImagina que tu objeto contiene 10 000 registros. O que se actualiza cada 100 ms (por ejemplo, datos del servidor en tiempo real). Cada llamada a Object.entries() crea un nuevo array. Angular, en cada ciclo de detección de cambios, verá un array "nuevo" y redibujará toda la lista. Esto provoca parpadeos en la interfaz, pérdida de foco en los inputs y ralentizaciones.
Por eso, para proyectos serios es necesario usar una optimización.