Missing Javascript functionality for TextEditorField and CollectionField on nested forms
PHP 8.3.1 Symfony 6.4.3 EasyAdmin 4.8.12
I have 3 Crud Controllers ContratosTemplates Contratos ContratosCampos
ContratosTemplates has an AssociationField that contains a Contratos Contratos has a TextEditorField and a CollectionField of ContratosCampos
When I add/edit a ContratosTemplates the TextEditorField from Contratos is not rendered and the CollectionField renders poorly
However if I add/edit a Contrato the TextEditorField displays fine and the CollectionField renders fine (collapsible header, remove button and propper field spacing)
I expected ContratosTemplates add/edit to work the same way and so far I haven't been able to find a way to activate this functionality
Here are the ConfigureFields for each CrudController
ContratosTemplatesCrudController.php
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id')->hideOnForm(),
BooleanField::new('principal'),
AssociationField::new('idContratos', 'Contrato')
->renderAsEmbeddedForm(
ContratosCrudController::class,
'create_contratos_inside_a_contratosTemplates',
'edit_contratos_inside_a_contratosTemplates'
)
];
}
ContratosCrudController.php
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id')->hideOnForm(),
BooleanField::new('active'),
TextEditorField::new('contrato'),
CollectionField::new('contratosCampos')
->renderExpanded()
->setEntryIsComplex()
->useEntryCrudForm(ContratosCamposCrudController::class),
];
}
ContratosCamposController.php
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id')->hideOnForm(),
TextField::new('label', 'Nombre de Campo'),
TextField::new('placeholder', 'Texto de Ayuda'),
];
}
I found that if I merge the assets from a TextEditorField on the ContratosTemplatesCrudController.php, the TextEditor renders fine, no luck with the CollectionField though, also I'm not sure that's the best way to go
protected function getFieldAssets(FieldCollection $fieldDtos): AssetsDto
{
$fieldAssetsDto = new AssetsDto();
$currentPageName = $this->getContext()?->getCrud()?->getCurrentPage();
foreach ($fieldDtos as $fieldDto) {
$fieldAssetsDto = $fieldAssetsDto->mergeWith($fieldDto->getAssets()->loadedOn($currentPageName));
}
// add assets
$textEditorField = TextEditorField::new('cualquiercosa');
$fieldAssetsDto = $fieldAssetsDto->mergeWith($textEditorField->getAsDto()->getAssets()->loadedOn($currentPageName));
// doesn't do anything
$collectionField = CollectionField::new('contratosCampos');
$fieldAssetsDto = $fieldAssetsDto->mergeWith($collectionField->getAsDto()->getAssets()->loadedOn($currentPageName));
return $fieldAssetsDto;
}
I found a workaround for these problems
First for the TextEditor I ended up using this so the field would render correctly
protected function getFieldAssets(FieldCollection $fieldDtos): AssetsDto
{
$fieldAssetsDto = new AssetsDto();
$currentPageName = $this->getContext()?->getCrud()?->getCurrentPage();
foreach ($fieldDtos as $fieldDto) {
$fieldAssetsDto = $fieldAssetsDto->mergeWith($fieldDto->getAssets()->loadedOn($currentPageName));
}
// Add TextEditor CSS and JS
$dto= new FieldDto();
$dto->addJsAsset(Asset::fromEasyAdminAssetPackage('field-text-editor.js')->getAsDto());
$dto->addCssAsset(Asset::fromEasyAdminAssetPackage('field-text-editor.css')->getAsDto());
$fieldAssetsDto = $fieldAssetsDto->mergeWith($dto->getAssets()->loadedOn($currentPageName));
return $fieldAssetsDto;
}
For the CollectionField, I realized that if I used a Symfony Form instead of a an EasyAdmin CRUD Form it worked.
ContratosCrudController.php
public function configureFields(string $pageName): iterable
{
return [
IdField::new('id')->hideOnForm(),
BooleanField::new('active'),
TextEditorField::new('contrato'),
CollectionField::new('contratosCampos')
->renderExpanded()
->setEntryIsComplex()
// this works
->setEntryType(ContratosCamposType::class)
// this doesn't
// ->useEntryCrudForm()
// this also doesn't work
// ->useEntryCrudForm(ContratosCamposCrudController::class),
];
}
@javiereguiluz can this bug be fixed in the next release, this is a common use case which is somehow blocking for now :'( Thank you :D
Temporary fix:
Add this code where you're calling the nested form:
->addJsFiles(Asset::fromEasyAdminAssetPackage('field-text-editor.js')->onlyOnForms())
->addCssFiles(Asset::fromEasyAdminAssetPackage('field-text-editor.css')->onlyOnForms())
@Geolim4 Thanks for your workaround.
I had a similar problem with Collection Field and solved it like follow:
Symfony version: 6.4.7 Php version: 8.3 EasyAdmin: ^4.0
- Nested forms worked well in EasyAdmin CRUD::PAGE_NEW create mode
- Nested forms did not work in CRUD::PAGE_EDIT update mode
Here there is my CollectionField code:
$fields[] = CollectionField::new('categories', ucfirst($this->translator->trans('guide.prop.categories')))
->renderExpanded()
->setEntryIsComplex(true)
->setEntryType(GuideDraftCategoryFormType::class)
->setRequired(true)
;
- Create a custom Service reading into manifest.json file:
class AssetManifestService
{
private $manifestPath;
private $manifestData;
public function __construct(ParameterBagInterface $params)
{
$this->manifestPath = $params->get('kernel.project_dir') . '/public/bundles/easyadmin/manifest.json';
$this->loadManifest();
}
#[Returns('void')]
private function loadManifest(): void
{
if (file_exists($this->manifestPath)) {
$jsonContent = file_get_contents($this->manifestPath);
$this->manifestData = json_decode($jsonContent, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Error decoding JSON: ' . json_last_error_msg());
}
} else {
throw new Exception('Manifest file not found.');
}
}
#[Returns('string|null')]
public function getAsset(string $assetName): ?string
{
return $this->manifestData[$assetName] ?? null;
}
}
- Call service into a custom EasyAdmin 4 CRUD configure assets method:
/**
* Add custom assets
*/
#[Param(assets: 'Assets')]
#[Returns('Assets')]
public function configureAssets(Assets $assets): Assets
{
$jsFile = $this->assetManifestService->getAsset('field-collection.js');
$cssFile = $this->assetManifestService->getAsset('field-text-editor.css');
// Add here other $assets if you need
if ($jsFile) {
$assets->addJsFile('/bundles/easyadmin/' . $jsFile);
}
if ($cssFile) {
$assets->addCssFile('/bundles/easyadmin/' . $cssFile);
}
return $assets;
}
The logic above retrieve paths and file names after webpack compile from manifest.json file, then apply js and css file contents into EasyAdmin 4 back-office entities.
Maybe this is a just a temporary workaround, but collection field work well also in edit environement and solution is stable.
Thank you for the workaround. Very annoying bug...
->addJsFiles(Asset::fromEasyAdminAssetPackage('field-text-editor.js')->onlyOnForms()) ->addCssFiles(Asset::fromEasyAdminAssetPackage('field-text-editor.css')->onlyOnForms())
Thanks to your reply among others, I was able to fix this. But in a cleaner way. This is how I fixed the CollectionField.
yield CollectionField::new('sections')
// This still won't work:
//->useEntryCrudForm(DocumentSectionCrudController::class, 'embed_new', 'embed_edit')
->setEntryIsComplex()
->setEntryType(EmbeddedDocumentSectionType::class)
->addAssetMapperEntries(
Asset::fromEasyAdminAssetPackage('field-text-editor.js'),
Asset::fromEasyAdminAssetPackage('field-text-editor.css')
)
->renderExpanded();
And the form:
$builder
->add('title', TextType::class)
->add('content', TextEditorType::class)
;
I did not need the additional service.