You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I work on a component for editing a Prestation entity. Because this component will be complex (with multiple tabs, each one dedicated to editing a subset of the entity's properties), I can't easily use one unique form for this.
I figured out I probably had two solutions :
create a child component for each of these tabs, which is probably the best solution but also a bit tedious
use one unique component with several custom LiveModels instead of using ComponentWithFormTrait
In order to avoid the creation of multiple forms and multiple child components (and also to satisfy my curiosity regarding the possibilities), I decided to try the second solution. But things got complicated when I wanted to update Prestation.products, which is a collection of related Product entities.
The documentation recommends to use a form with a CollectionType for that kind of scenario. But, like I tried to explain above, using a ComponentWithFormTrait would force me to have one unique form in my component when I'd actually need several (one per tab). So I tried to recreate a way to update my collection using only LiveProps, without any form.
This is what I did so far (note: for better readability I simplified the following code as much as possible by removing most things which are not related to my current issue) :
#[AsLiveComponent]
classEditPrestationextendsAbstractController
{
useDefaultActionTrait;
#[LiveProp(
writable : [ 'name' ],
onUpdated : [ 'name' => 'onNameUpdated' ],
)]
publicPrestation$prestation;
/** @var array<int, array{ * name : string, * supplyMode : string|null, * }> $products */
#[LiveProp(writable: true, onUpdated: 'onProductsUpdated')]
publicarray$products = [];
#[LiveProp(writable: true)]
publicstring$tab = 'general';
publicMenuDefinition$menu;
/** @var SupplyMode[] */publicarray$supplyModes = [];
publicfunction__construct(
protected readonly EntityManagerInterface$manager,
) {
}
publicfunction__invoke(): void
{
$this->menu = $this->createMenu();
$this->supplyModes = SupplyMode::cases();
}
publicfunctionmount(
Prestation$prestation,
string$tab = 'general',
): void {
$this->prestation = $prestation;
$this->tab = $tab;
foreach ($prestation->getProducts() as$product) {
$this->products[$product->getId()] = [
'name' => $product->getName(),
'supplyMode' => $product->getSupplyMode()?->value,
];
}
$this->__invoke();
}
publicfunctiononNameUpdated(): void
{
// This method is an example of how I manage the edition of a simple string property of my Prestation entity.// This works well and is not related to my current issue.$this->manager->persist($this->prestation);
$this->manager->flush();
$this->addFlash('success', 'Prestation modifiée');
}
publicfunctiononProductsUpdated(): void
{
// This is where I would normally hydrate and persist Product entities, based on the $this->products LiveProp// Problem: this method seems to never be calledreturn;
}
#[LiveAction]
publicfunctionaddProduct(): void
{
// This method handles the "Add Product" button.// This works well and is not related to my current issue.$nb = \count($this->prestation->getProducts()) + 1;
$product = newProduct(
name : "Produit N°$nb",
prestation : $this->prestation,
supplyMode : null,
);
$this->prestation->addProduct($product);
$this->manager->persist($this->prestation);
$this->manager->persist($product);
$this->manager->flush();
$this->products[$product->getId()] = [
'name' => $product->getName(),
'supplyMode' => $product->getSupplyMode()?->value,
];
$this->addFlash('success', "Produit N°$nb ajouté");
$this->__invoke();
}
protectedfunctioncreateMenu(): MenuDefinition
{
// This is where I create the menu with the different tabs. // This works well and is not related to my current issue.
}
}
<div {{ attributes.defaults({ class : 'prestation' }) }}>
<twig:UI:Flashes :flashes="app.flashes" />
<divclass="prestation_aside">
<twig:UI:Menu :menu="menu" />
</div>
<divclass="prestation_main">
{% iftab=='general' %}
{# This section of the component is not directly related to my current issue. I kept it as an example of how I manage the edition of the Prestation.name property which is a simple string. #}
<twig:UI:Title:Section>Général</twig:UI:Title:Section>
<divclass="form_wrapper">
<divclass="form_grid form_grid-2_columns">
<divclass="form_label">
<labelfor="prestation_name"class="label">Nom de la Prestation</label>
</div>
<divclass="form_field">
<inputtype="text"id="prestation_name"value="{{ prestation.name }}"data-model="prestation.name"
>
</div>
</div>
</div>
{% elseiftab=='produits' %}
{# Below is the section related to my issue. Notice the data-model attributes on the <input> and <select> tags: #}
<twig:UI:Title:Section>Produits</twig:UI:Title:Section>
<twig:UI:ButtonliveAction="addProduct">Ajouter un Produit</twig:UI:Button>
{% ifproducts|length>0 %}
<divclass="form_wrapper">
<divclass="form_grid form_grid-2_columns">
{% forproductId, productDatainproducts %}
<divclass="form_label">
<labelfor="product_{{ productId }}_name"class="label">Nom</label>
</div>
<divclass="form_field">
<inputtype="text"id="product_{{ productId }}_name"value="{{ productData.name }}"data-model="products.{{ productId }}.name"
>
</div>
<divclass="form_label">
<labelfor="product_{{ productId }}_supplyMode"class="label">Mode d'Appro.</label>
</div>
<divclass="form_field">
<selectid="product_{{ productId }}_supplyMode"data-model="products.{{ productId }}.supplyMode"
>
<optionvalue="null"
{% ifproductData.supplyMode==null %}selected{% endif %}
>Indéterminé</option>
{% forsupplyModeinsupplyModes %}
<optionvalue="{{ supplyMode.value }}"
{% ifsupplyMode.value==productData.supplyMode %}selected{% endif %}
>{{ supplyMode.description }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% endif %}
</div>
</div>
This kind of works... except the onProductsUpdated() method is never called. I'm not sure I did right by using data-model="products.{{ productId }}.name" and data-model="products.{{ productId }}.supplyMode". Is this actually possible, or is there another way?
If this is not possible, I'll accept it and break my component into several child components, each with its own dedicated form. But in the meantime I'd like to know if there is a way to achieve what I'm trying to do here.
Thanks for your help.
The text was updated successfully, but these errors were encountered:
Hello,
I work on a component for editing a
Prestation
entity. Because this component will be complex (with multiple tabs, each one dedicated to editing a subset of the entity's properties), I can't easily use one unique form for this.I figured out I probably had two solutions :
LiveModels
instead of usingComponentWithFormTrait
In order to avoid the creation of multiple forms and multiple child components (and also to satisfy my curiosity regarding the possibilities), I decided to try the second solution. But things got complicated when I wanted to update
Prestation.products
, which is a collection of relatedProduct
entities.The documentation recommends to use a form with a
CollectionType
for that kind of scenario. But, like I tried to explain above, using aComponentWithFormTrait
would force me to have one unique form in my component when I'd actually need several (one per tab). So I tried to recreate a way to update my collection using only LiveProps, without any form.This is what I did so far (note: for better readability I simplified the following code as much as possible by removing most things which are not related to my current issue) :
This kind of works... except the
onProductsUpdated()
method is never called. I'm not sure I did right by usingdata-model="products.{{ productId }}.name"
anddata-model="products.{{ productId }}.supplyMode"
. Is this actually possible, or is there another way?If this is not possible, I'll accept it and break my component into several child components, each with its own dedicated form. But in the meantime I'd like to know if there is a way to achieve what I'm trying to do here.
Thanks for your help.
The text was updated successfully, but these errors were encountered: