[LiveComponent] How to trigger a re-render of another component from PHP, without emitting?
My OrderWidget live component simply show an order summary. The getOrder will always return a refreshed Order instance:
#[AsLiveComponent]
class OrderWidget extends AbstractController
{
use DefaultActionTrait;
use ComponentBusTrait;
#[LiveListener('refresh')]
public function refresh(): void
{
}
#[ExposeInTemplate]
public function getOrder(): ?Order
{
return $this->query(new FetchCurrentOrderQuery(new FetchOrderArgs( hydrateCustomer: true )));
}
}
Any PHP component in the page can emit a refresh event to make the summary update itself. But having just an empty methhod to trigger a refresh seems weird, doesn't it?!
Is there a way to trigger the re-render of a component via PHP, without emitting to an empty method? This is possible via JavaScript but it's not clear if can be accomplished also from PHP.
Use render ?
Thanks @smnandre , I don't know how to trigger a render from a PHP component to another PHP component, but only via JavaScript. Am I missing something from documentation?
I answered just for the "empty method" as refresh does not bring more than render.
Render directly from PHP to PHP is not something that should be encouraged i guess, as components should have a isolated scope :)
So you confirm that my spot was correct, emitting "refresh" from PHP component A to another PHP component B (which receive the refresh event and have an empty method implementation)... is the only way?
I mean I can't understand "Use render" answer, I bet I'm missing something!
Any PHP component in the page can emit a refresh event to make the summary update itself. But having just an empty methhod to trigger a refresh seems weird, doesn't it?!
You can call the action "render" directly on the front side.. instead of creating this "refersh" event.
To my knowledge, having "PHP" controlling a LiveComponent makes not much sense... as what would happen if it was not a LiveComponent calling ?
In your specific case, could you illustrate in what scenario you want another PHP code force a render of another component ?
Here is the example, a pretty common use case. OrderWidget PHP code from above:
<twig:OrderWidget>
<p>id: {{ order.id }}</p>
<p>Owener: {{ order.owner.fullname }}</p>
</twig:OrderWidget>
<!--
This component will allow to switch user.
When user is changed, the OrderWidget should be updated to show the new owner name.
This is right now accomplished via PHP, emitting "refresh" to component "OrderWidget",
i.e. $this->emit("refresh", [], "OrderWidget")
-->
<twig:UserChoice />
How would you call render from the front site? A manual button "refresh" to rerender? That was not what I've asked btw.
That was not what I've asked btw.
I'm sure this is told in a positive way, but in another time this could be misunderstood and maybe people would not take time on their free time to help you. ;)
LiveComponent are "front-end" component that react to user-interactions / model changes / etc. So the pretty common use case is there :)
How some PHP could could render a component if it does not know its existence in the first place ?
So, a solution i think would have more qualities in term of isolation, separation of concerns, etc.. would be:
When your UserChoice does change a user, it emit an event... but not "orderwidget" as it should not "know" it... a better think would be to emit a "UserSwitched" event...
And knowing who must react is something that can bring you a lot of troubles.... but in the contrary listening to things that are clearly identified is another.
So OrderWidget should listen to "UserSwitcher" to refresh itself.
That beeing said, i strongly advice you to make a full refresh of your webpage after someone logs in or logs out, or you may probably face have other problems in term of sessions, csrf, cookies etc...
I'm sure this is told in a positive way, but in another time this could be misunderstood and maybe people would not take time on their free time to help you. ;)
Yeah, totally :) I don't know you but you are very active and help a lot of people asking questions.. and you don't know me, I'm always positive, just maybe rude in the language as is not my mother tongue.
When your UserChoice does change a user, it emit an event... but not "orderwidget" as it should not "know" it... a better think would be to emit a "UserSwitched" event...
I was thinkink at the exact concept and I'll do... but eventually, this is just a better way of organize things/events/flows. It's OK and thanks for the suggestione.
Back to the question. Assuming that now the widget is:
#[AsLiveComponent]
class OrderWidget extends AbstractController
{
use DefaultActionTrait;
use ComponentBusTrait;
#[LiveListener('userSwitched')] // User has been changed
#[LiveListener('itemChanged')] // Order row has been changed (edited)
#[LiveListener('itemDeleted')] // Order row has been deleted
public function refresh(): void
{
}
#[ExposeInTemplate]
public function getOrder(): ?Order
{
return $this->query(new FetchCurrentOrderQuery(new FetchOrderArgs( hydrateCustomer: true )));
}
}
... I'm still not confortable with the empty refresh method. If you confirm that there isn't any "undocumented" way of doing this I can close this issue.
[..] just maybe rude in the language as is not my mother tongue.
Oh i know.... as i have the same problem.... (and even in my mother tongue ... 😅 )
The empty method: I agree it can look "weird" but in the end it's just like a controller... you can create this controller and it's perfectly valid
class MyController extendsAbstractController
{
#[Route....]
public function __invoke()
{
)
}
And don't look in the DefaultActionTrait you could be surprised 🙊
So right now i don't see better..... i'm thinking we should allow them on the class, as we have a default action
That's make better DX right ?
Despite the fact that my reasoning wasn't good as you pointed out (UserControl shoudl emit userChanged)... just as we can do:
<button data-action="live#$render">
Generate a new number!
</button>
It may be helpful to have an event that, once emitted, can automatically re-render another PHP component (from another PHP component) without having to write an empty method and attach listeners.
As the DefaultActionTrait would have:
#[LiveListener("$render")]
public function __invoke()
{
)
Then $this->emit("$render") would that force the re-rendering of any component in the page, or a given component if component name is provided.
Just midnight ideas after a very long long trip :)
I have the same need in a Symfony app and for the exact same use case: an order form.
I also added an empty __invoke() method and used it together with this: https://symfony.com/bundles/ux-live-component/current/index.html#forcing-a-re-render-explicitly
It's not super pretty ... but it's technically correct and works great.
So, let's close this for now and we can revisit this in the future if more people need to solve this as you said, by sending "a re-render event" from PHP. Thanks!