Implementar un widget grid-elements-manager
Objetivo
Sección titulada «Objetivo»Que el usuario gestione los hijos del padre directamente desde la vista de detail del padre. El widget renderiza la lista del hijo (filtrada al padre actual) y un botón ”+ New” que abre el form del hijo en un diálogo — sin salir del padre.
Antes de empezar
Sección titulada «Antes de empezar»- Un proyecto Catalyst con dos módulos frontend ya scaffoldeados — un padre (p. ej.
iam/bounded-context) y un hijo (p. ej.iam/permission). - El YAML del hijo declara una relación
many-to-oneapuntando de vuelta al padre — esa es la FK que el codegen lee para cablear el widget. La mayoría de los módulos hijos ya la tienen. - El
front.detailModedel padre está sin declarar o enview. El modo dialog no puede alojar el widget — el codegen avisa y lo omite para no anidar un diálogo dentro de otro diálogo. - Puedes ejecutar
catalyst generate front module --forcelocalmente.
-
Activa el modo embed en el hijo. En el YAML del hijo, declara
front.embedSupport: true. Sin el flag, el codegen emite los ficheros de siempre y el regen del padre falla al intentar dispatchar el widget.cliter/iam/permission.aurora.yaml front:embedSupport: true -
Regenera el hijo.
Ventana de terminal catalyst generate front module --name=iam/permission --forceAparecen tres artefactos nuevos:
iam-permission-form-embed.component.ts— variante del form cuya FK al padre se inyecta ensubmit(), no se declara comoFormControl. El input requerido[parentValue]lo cablea la lista embebida automáticamente.- Factory
getIamPermissionEmbedColumns(...)eniam-permission.columns.ts— las mismas columnas queStandalone, menos la del FK al padre (todas las filas en vista embed comparten el mismo padre, así que la columna sería redundante). - El list component gana inputs
mode,parentFilter,parentDefaultsy renderiza sin header ni breadcrumb cuandomode="embed".
-
Declara el widget en la property del padre. En el YAML del padre, la property de relación que apunta al hijo recibe
widget.type: grid-elements-manager. Opcionalmente ubícalo dentro de un tab.cliter/iam/bounded-context.aurora.yaml aggregateProperties:- name: permissionstype: relationshiprelationship:type: one-to-manysingularName: permissionaggregateName: IamPermissionmodulePath: iam/permissionwidget:type: grid-elements-managertab: permissions # opcionalwidget.detailSortywidget.isDetailHiddenaplican con normalidad — el widget se trata como un campo lógico a efectos de orden y visibilidad.widget.spanse ignora: el widget siempre renderiza a ancho completo. -
Regenera el padre.
Ventana de terminal catalyst generate front module --name=iam/bounded-context --forceEl codegen lee
iam/permission.aurora.yaml, encuentra la property cuyarelationship.type === 'many-to-one'yrelationship.modulePath === 'iam/bounded-context'(p. ej.boundedContextId) y emite la partial dentro del detail shell del padre — envuelta en@if (mode() === 'edit')para que solo aparezca cuando el padre ya tiene id. La lista embebida recibeparentFilter: { field: 'boundedContextId', value: bcId() }yparentDefaults: { boundedContextId: bcId() }. -
Añade la key de traducción. El título de la card del widget usa una key derivada del nombre plural del agregado hijo:
<bcKebab>.<childModKebab>.<ChildAggregatePluralPascal>Para
iam/bounded-context.permissions→iam.permission.Permissions. Añade la entrada a tus ficheros de traducción; el codegen no genera el valor.
Verifica que funcionó
Sección titulada «Verifica que funcionó»- Abre el detail del padre en modo
edit(/iam/bounded-context/edit/<id>). Debajo del card del form (o dentro del tab declarado) aparece una sección nueva con la lista de filas hijas filtrada al padre actual. - Clica ”+ New” dentro de la lista embebida. Se abre un diálogo con el form-embed; al guardar crea un hijo cuya FK al padre se setea automáticamente — aunque el FK no tenga campo en el form.
- Abre el padre en modo
new(/iam/bounded-context/new). El widget no aparece — el padre todavía no tiene id, así que no hay con qué asociar hijos. - La superficie standalone del hijo (
/iam/permission,/iam/permission/new,/iam/permission/edit/:id) sigue funcionando sin cambios.
Troubleshooting
Sección titulada «Troubleshooting»El regen del padre falla con “target lacks embedSupport: true”.
El codegen lee el YAML del hijo para validar la embedabilidad. Añade front.embedSupport: true al YAML del hijo y regenera el hijo primero (pasos 1–2), después relanza el regen del padre.
El regen falla con “child has no many-to-one back-reference”.
El codegen no encontró la property FK en el YAML del hijo. Confirma que el hijo declara un aggregateProperty con relationship.type: many-to-one y relationship.modulePath apuntando al path del padre.
El widget no aparece.
Comprueba el front.detailMode del padre: si es dialog, el codegen registra un warning y omite el widget para no apilar un diálogo dentro de otro diálogo. Cambia a view y regenera.
El widget aparece pero las filas se ven mal.
La lista embebida reusa la factory getXEmbedColumns(...) del hijo. Si personalizaste las columnas del hijo asumiendo que solo existía la factory standalone, revisa que tus ediciones no rompan la factory embed — ambas viven en el mismo fichero *.columns.ts.
widget.span no afecta al layout.
Es lo esperado. El widget es una sección, no un campo — widget.span se ignora sobre grid-elements-manager y el codegen emite un warning cuando lo declaras.
Relacionado
Sección titulada «Relacionado»- Widget grid-elements-manager — el cambio que introdujo el widget.
- Detail mode: view o dialog — por qué el modo dialog no puede alojar el widget.
- Ancho de campos en formulario — el grid que usa el form del padre; el widget renderiza como sección hermana, no como campo.
- Referencia de
catalyst generate— cada flag y argumento.