symfony / ux

Symfony UX initiative: a JavaScript ecosystem for Symfony
https://ux.symfony.com/
MIT License
851 stars 313 forks source link

[LiveComponent] Calling a property using a getter (i.e. computed) doesn't update component state (another component property)? #1529

Closed gremo closed 8 months ago

gremo commented 8 months ago

In my AttendancePanel I'm using getLastState to fetch a remote resource and, as soon as the method ends, I update the property lastUpdated (set it to the current date/time).

In Twig, I'm using this.lastState (or computed.lastState). Either way lastUpdated is null, despite the fact that the method actually update the variable:

<div {{ attributes }}>
    <p>Last action returned: {{ this.lastState ? 'object' : 'null' }}</p>
    <p>Last updated: {{ lastUpdated ? lastUpdated|format_datetime() : 'never' }}</p>
</div>

On the contrary, if I call record() using a button (data-action-name="record") the last update time is updated just fine:

<?php

namespace App\Twig\Components\Attendance;

use App\Entity\User;
use App\Messenger\Attendance\GetLastAttendanceStateMessage;
use App\Messenger\Attendance\RecordAttendanceMessage;
use App\Model\Attendance\AttendanceRecord;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\HandledStamp;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent]
class AttendancePanel extends AbstractController
{
    use DefaultActionTrait;

    #[LiveProp]
    public User $user;

    public ?\DateTimeImmutable $lastUpdated = null;

    public function __construct(
        private readonly MessageBusInterface $messageBus,
    ) {
    }

    #[LiveAction]
    public function record()
    {
        $this->messageBus->dispatch(new RecordAttendanceMessage(
            $this->user->getProfile(),
            $this->user
        ));

        $this->lastUpdated = new \DateTimeImmutable();  // Value will be updated
    }

    public function getLastState(): ?AttendanceRecord
    {
        $evevelope = $this->messageBus->dispatch(new GetLastAttendanceStateMessage(
            $this->user->getProfile(),
            $this->user,
        ));

        $this->lastUpdated = new \DateTimeImmutable();  // Value will NOT be updated

        return $evevelope->last(HandledStamp::class)->getResult();
    }
}
smnandre commented 8 months ago

Did you forget #[LiveProp] on $lastUpdated ?

And #[LiveAction] on getLastState ?

gremo commented 8 months ago

@smnandre #[LiveProp] has no effect. About #[LiveAction], why do I need it if this is just a getter? Thanks

smnandre commented 8 months ago

LiveProp is the way LiveComponent keep a property "state".

LiveAction is the way LiveComponent can act on its model while keeping previous "state".

If you use neither LiveAction nor LiveProp, your property will not be updated with its new value.

gremo commented 8 months ago

Oh I see, thank you. Sorry for being so naive, it was not clear to me!

smnandre commented 8 months ago

No problem :)