Documentación · Referencia · El formato de un proyecto
6. Colecciones
Las colecciones y sus filtros, una página por cada valor y las listas partidas en páginas.
Versión 0.16
Una colección es el conjunto de páginas que comparten una etiqueta. Se declaran
con etiquetas, normalmente desde el _datos.yml de la carpeta. Los
fragmentos entran en las colecciones de sus etiquetas.
Toda página con URL está además en la colección implícita todo. Por eso
todo no se puede usar como etiqueta: si aparece, se avisa y se quita.
En un sitio en varios idiomas, las colecciones están acotadas al idioma de la
página que las consulta, y se puede pedir otro (§15.5). El nombre de una
colección se compara exacto, con mayúsculas y sin recortar espacios:
coleccion('Blog') no es coleccion('blog').
No entran en ninguna colección, tampoco en todo, los borradores, las páginas
con publicar en el futuro y las de listada: no. Este último es el caso de
los textos legales, la página de error o robots.txt: se publican, pero no
salen en listados ni en el sitemap.
Desde una plantilla:
{% for articulo in coleccion('blog')|orden('fecha desc')|limite(6) %}
6.1 Orden de una colección¶
coleccion() devuelve las páginas por fecha, de la más antigua a la más
reciente, y a igual fecha por ruta: la del fichero, con su extensión y su código
de idioma, comparada byte a byte (blog/a-b.md va antes que blog/a.md, porque
- es menor que .). Las que no tienen fecha van al final, también por ruta.
Es el mismo orden que usa Eleventy, salvo en las páginas sin fecha: Eleventy les da la fecha de creación del fichero, que cambia de una máquina a otra, y un proyecto tiene que dar el mismo resultado en cualquiera.
6.2 Filtros¶
orden(criterio)ordena por uno o varios campos separados por comas, cada uno seguido si acaso deascodesc(ascsi no se indica):orden('orden, titulo'). Sin criterio esorden('fecha desc'). Las páginas a las que les falta el campo van al final en las dos direcciones. Los números se comparan como números, las fechas como fechas y los textos sin distinguir mayúsculas ni tildes (con la tabla delslug, §5.2), con lañdespués de lany sin contar los signos¿y¡: «¿Cómo llegar?» va entre las «c», no antes de la «a». Es una comparación del valor de cada carácter, no la de un diccionario entero: un número escrito en un texto se compara como texto («Capítulo 10» va antes que «Capítulo 2») y las letras de otros alfabetos se ordenan por su código. Dos valores de distinto tipo se comparan como texto, y entre dos sí/no elnova antes. A igualdad, las páginas quedan en el orden que tenían. Un criterio que no vale (orden('fecha descendente')) detiene el build;paginar.orden(§6.4), en cambio, avisa y usa el decoleccion().limite(n)deja lasnprimeras.invertirda la vuelta a la lista.sin(pagina)quita una página, normalmente la actual.donde(campo, valor)deja las páginas en las que el campo vale eso. Si el campo es una lista, las que lo contienen:donde('etiquetas', 'novela'). Una fecha se compara con su día (donde('fecha', '2025-03-15')). Si los dos valores parecen números, se comparan como números (donde('codigo', '7')encuentracodigo: "007"); los demás textos se comparan exactos: con mayúsculas, tildes y espacios.anterior(pagina)ysiguiente(pagina)no filtran: devuelven la página que va antes o después de la indicada en la lista, o nada si es la primera, la última o no está. Con el orden decoleccion(), la anterior es la más antigua.
Los filtros aceptan también los alias ingleses de los campos (date,
title).
orden y donde ven cada página como la ven las plantillas (§7.2): los
campos del front matter, con la cascada, y además url, la URL ya resuelta
(vacía en un fragmento), ruta, la del fichero dentro de contenido/, e
idioma, el código del idioma de la página (§15.8).
donde('url', '/contacto/') encuentra la página aunque su URL salga de la
ruta y no esté escrita en el front matter. No cuentan contenido, markdown,
encabezados ni traducciones (§7.2): para filtrar u ordenar por contenido
habría que convertir el cuerpo de todas las páginas de la lista.
Como orden respeta el orden previo en los empates, orden('fecha desc') no
es lo mismo que invertir cuando hay páginas con la misma fecha: la primera
las deja por ruta y la segunda, al revés. Lo que en Eleventy es
collections.blog | reverse | head(6) es aquí
coleccion('blog')|invertir|limite(6).
6.3 Una página por cada valor¶
Un blog con etiquetas abiertas no puede tener una página escrita a mano por
cada etiqueta: la siguiente entrada puede traer una nueva. Para eso, una página
con agrupar no es una página sino una plantilla de páginas: el motor la
repite una vez por cada valor distinto de un campo de las páginas de una
colección.
---
plantilla: etiqueta
agrupar:
coleccion: blog # de dónde salen las páginas; obligatorio
campo: etiquetas # el campo por el que se agrupan; obligatorio
excluir: [blog] # valores que no generan página; opcional
url: "/etiqueta/{{ grupo|slug }}/"
etiquetas: [temas]
---
La página con agrupar no genera fichero, ni sale en colecciones ni en el
sitemap. Cada página que genera lleva los campos de la que la define (con la
cascada del §3, como cualquier otra) y además:
grupo: el nombre del grupo, en texto;paginas: las páginas del grupo, con la forma de las que dacoleccion()(§7.2) y en su mismo orden, así que los filtros del §6.2 valen:pagina.paginas|orden('fecha desc').
Como contenido (§7.2), las dos tapan un campo del front matter que se
llame igual, solo en las páginas generadas.
Reglas:
- Qué entra. Las páginas de la colección, tal como las da
coleccion()(§6): sin borradores, sin las depublicaren el futuro y sin las delistada: no, y en el idioma de la página que define el grupo (§15.5). Por eso un valor que solo usan páginas que aún no se publican no tiene página hasta que sale la primera, y nunca hay un grupo vacío. - Los valores.
campoes un campo de las páginas de la colección, tal como llega a la plantilla (§7.2), con la cascada. Una lista da un valor por elemento; un texto suelto, uno, como enetiquetas(§4). Un valor que no es texto ni número, o que está en blanco, se ignora y se avisa. Si el campo esetiquetashay que acordarse deexcluir: la carpeta pone a cada artículo el nombre de su colección (§3), y sinexcluir: [blog]habría un grupo con todo el blog. - Variantes. Los valores se agrupan por su
slug(§5.2):Cultura pop,cultura-popyCultura Popson un grupo. Es la única regla coherente, porque la URL sale delslugy dos grupos con la misma saldrían al mismo fichero.grupoes la variante que más páginas usan y, a igualdad, la primera en el orden deorden(§6.2); si aun así empatan (Ideaseideas), la de menor código de carácter, para que el nombre no dependa del orden en que llegan las páginas.excluirtambién compara porslug. Un valor cuyoslugsale vacío se ignora y se avisa. - La URL. Tiene que usar
{{ grupo }}, casi siempre como{{ grupo|slug }}(§5.1), que es lo que da un tramo de URL válido: sin el filtro, el valor se escribe tal cual. Sinurl, o con una que no lo use, todas las páginas irían al mismo fichero: se avisa y no se genera ninguna. El resto del §5 vale: el prefijo de idioma (§15.3) y el error si dos páginas van al mismo fichero. - El título. Si la página que define el grupo no tiene
titulo, el de cada página generada es sugrupo, y no avisa. Cualquier otro campo se pasa como está: lo que hay que componer con el grupo (Etiqueta: …) se escribe en la plantilla conpagina.grupo. - Sitemap y
todo. Las páginas generadas salen en el sitemap y entodo, y en las colecciones que declaren susetiquetas. Si la que las define llevalistada: no, lo heredan como cualquier otro campo (§3) y no salen en ninguno de los dos. - El orden. Las páginas generadas no tienen
fechasalvo que la herede la que las define; en una colección van, como cualquier página sin fecha, al final, y entre ellas porslugde su grupo. Conetiquetas: [temas], el índice de todos los grupos escoleccion('temas')y su recuento,tema.paginas|length, sin repetir en un fichero de datos lo que ya dicen las páginas. - Idiomas. Cada idioma tiene su propia página con
agrupar(etiqueta.md,etiqueta.eu.md) y agrupa las páginas de ese idioma. En una página generada,pagina.traduccionessolo trae ella misma (§15.8): los grupos de un idioma no se corresponden con los de otro. - Los avisos y los errores. Un
agruparal que le faltacoleccionocampoavisa, con su línea, y no genera nada. Una clave que no admite se avisa y se ignora. Si ni siquiera es un mapa (agrupar: sí), se avisa, se ignora y la página es una más. Una colección sin páginas no es un error: no hay grupos.excluiradmite, comoetiquetas, un texto suelto.agruparno se hereda: en un_datos.ymlse avisa y se ignora, porque todas las páginas de la carpeta serían plantillas de páginas y ninguna saldría. - Sin vueltas. Una página con
agruparno puede depender de las páginas que genera ella misma ni otra (coleccion: todo, o una colección a la que pertenecen las generadas): el build se detiene con un error que muestra la cadena, como en el §7.2. - No es paginación. Parte un conjunto en grupos; no parte una lista larga
en trozos. Eso es
paginar(§6.4).
grupo y paginas los tienen también las páginas generadas cuando salen de
coleccion() (§7.2), que es lo que hace posible el índice de arriba.
La compilación incremental da siempre lo mismo que la completa. Una página
generada se rehace cuando entra o sale una página de su grupo o cambian los
campos de alguna; si solo cambia el cuerpo de una de ellas, no, salvo que la
plantilla lo muestre. Las páginas que se generan, o dejan de generarse, se
deciden de nuevo en cada compilación. Cómo se consigue es cosa de
docs/compilacion.md.
El esquema (§11) no sabe de agrupar. Es cosa de quien desarrolla el sitio, y
un editor ni la enseña ni la toca; lo que sí hace es que el editor pueda
escribir una etiqueta nueva en una entrada sin que nadie tenga que crear antes
su página.
6.4 Partir una lista en páginas¶
Un listado largo se parte en trozos, uno por página. Una página con paginar
es, como con agrupar, una plantilla de páginas: ella misma es la primera, en
su url, y el motor genera las demás.
---
plantilla: blog
url: /blog/
paginar:
coleccion: blog # de dónde salen las páginas; obligatorio
porPagina: 10 # cuántas lleva cada página; obligatorio
orden: fecha desc # criterio de orden() (§6.2); opcional
url: "/blog/{{ numero }}/" # la de las páginas a partir de la segunda; opcional
---
Una entrada nueva que llene la última página abre la siguiente sin que nadie cree nada, y una que se quita la cierra.
-
Qué entra. Las páginas de la colección, tal como las da
coleccion()(§6): sin borradores, sin las depublicaren el futuro y sin las delistada: no, y en el idioma de la página (§15.5). La propia página que pagina no entra en su colección, aunque lleve su etiqueta, ni tampoco las que genera:blog/index.mdhereda la etiquetablogde su carpeta (§3) y no por eso se lista a sí misma. -
El orden. El de
orden, que se escribe como el criterio del filtroorden(); sin él, el decoleccion()(§6.1), de la más antigua a la más reciente. Un blog suele quererfecha desc. Se aplica antes de partir. -
Los trozos.
porPaginaes un entero mayor que cero. Cada trozo lleva como mucho esa cantidad, y el último, las que sobren. Una colección sin páginas no es un error: hay un solo trozo, vacío, y la página sale, para que un listado sin entradas siga existiendo. UnporPaginaque no es un entero mayor que cero se avisa y la página es una más, sin partir. -
La URL. La primera es la
urlde la página (§5), con las reglas de siempre. Las siguientes, la depaginar.url, que es un patrón del §5.1 con{{ numero }}(el número de la página, desde el 2) y que tiene que usarlo; sinpaginar.url, la de la primera máspagina/2/,pagina/3/… (/blog/da/blog/pagina/2/). Esa forma por defecto exige que laurlde la primera acabe en/: si no es así, y no haypaginar.url, se avisa y la página es una más, sin partir, aunque hoy todo quepa en un trozo (con la entrada que lo llene, la página dejaría de partirse). Sin{{ numero }}en el patrón todas irían al mismo fichero: se avisa y pasa lo mismo. Lo demás del §5 vale: el prefijo de idioma (§15.3) y el error si dos páginas van al mismo fichero.paginar.urlexiste para quien migra y ya tiene otra forma (/blog/2/,/page/2/) y no quiere perder direcciones. -
Lo que ve la plantilla. Cada página, la primera también, lleva los campos de la que la define (con la cascada del §3) y además:
paginas: las páginas de su trozo, con la forma de las que dacoleccion()(§7.2) y en el orden pedido;paginacion:actualytotal(cuántas páginas hay),porPagina,entradas(cuántas hay en la colección, repartidas en esas páginas),primerayultima(sus URL),anteriorysiguiente(la URL, o nada en la primera y en la última; los filtros del mismo nombre, §6.2, devuelven una página) yenlaces, una lista con un elemento por página, connumero,urlyactual(trueen la que se está), para pintar el índice de páginas.
Como
contenido(§7.2), las dos tapan un campo del front matter que se llame igual. El título es el de la página que define: la plantilla añade «Página 2» conpagina.paginacion.actual. -
Colecciones y sitemap. La primera es una página como cualquier otra, con la etiqueta y el sitemap que le toquen. Las que genera no entran en ninguna colección, tampoco en
todo, ni en el sitemap, como las delistada: no(§4): se llega a ellas por los enlaces de la paginación. -
Idiomas. Cada idioma tiene su propia página con
paginar(blog.md,blog.eu.md) y parte las páginas de ese idioma. En una página generada,pagina.traduccionessolo trae ella misma; la primera, que es la que define la página, tiene las suyas (§15.8). -
Con
agrupar. Una página no puede llevar las dos claves: se avisa ypaginarse ignora. Partir cada grupo en páginas queda reservado, sin definir.paginarno se hereda: en un_datos.ymlse avisa y se ignora, porque todas las páginas de la carpeta serían plantillas de páginas. -
Los avisos y los errores. Un
paginaral que le faltacoleccionoporPaginaavisa, con su línea, y no parte nada. Una clave que no admite se avisa y se ignora. Si ni siquiera es un mapa (paginar: sí), se avisa, se ignora y la página es una más. Unordenque no es un criterio válido se avisa y se usa el decoleccion(). En un fragmento (url: false) no hay dónde poner la primera página: se avisa y no se parte nada. -
Sin vueltas. Las páginas que genera no están en ninguna colección, así que ninguna página puede depender de ellas por una colección: no hay cadenas que comprobar, a diferencia de
agrupar(§6.3). La propia página tampoco entra en su trozo, aunque herede la etiqueta de su colección.
La compilación incremental da siempre lo mismo que la completa. Las páginas que
se generan, o dejan de generarse, se deciden de nuevo en cada compilación, y
una se rehace cuando cambia lo que la define, o su trozo: que entre, salga o
cambie de sitio una página, o cambien los campos de alguna de ellas. Cómo se
consigue es cosa de docs/compilacion.md.
El esquema (§11) no sabe de paginar: es cosa de quien desarrolla el sitio, y
un editor ni la enseña ni la toca. Lo que sí consigue es que quien edita añada
una entrada y la lista se reparta sola.