Ga door naar de hoofdinhoud

My First Symfony Live Component

As I mentioned yesterday, I want to use Symfony Twig Components (and even better: Symfony Live Components) to rebuild the frontend of our wiezen score app, dikdikdik.

Today I am very happy, because I created my first live component 🎉🎉🎉. It works, and it is very cool, because now I can use PHP to describe the behavior of a front-end component.

So what I am going to do, is the following:

A score sheet shows the names of the known players, and their scores. After every name, there's a little button that you can use to remove or re-add a player. My first component shows this button, and handles the clicks.

score buttons

This is how it looks like in the twig code:

                {{ component('playerSwitch', {
                    playerId: player.playerIdentifier,
                    game: gameNumber.toInteger,
                    tableId: tableIdentifier
                }) }}

So I include a 'playerSwitch' component, and I pass the information it needs: the player identifier, the game number and the table identifier.

For this component, I had to create a twig file components/playerSwitch.html.twig, that looks like this:

{# @var \App\Components\PlayerSwitchComponent this #}

<span {{ init_live_component(this) }} >
    {% if this.active %}
        <button type="button" class="btn btn-link btn-sm"
                data-action="live#action"
                data-action-name="prevent|kick"
        >
            <i class="delete-icon fa fa-trash-o"></i>
        </button>
    {% else %}
        <button type="button" class="btn btn-link btn-sm"
                data-action="live#action"
                data-action-name="prevent|join"
        >🃏</button>
    {% endif %}
</span>

So I show the correct button, depending on the 'active' property of the component. The data-action and data-action-name attributes on the buttons define what will to happen when the user clicks the button.

These actions are in turn defined in the class for the component, PlayerSwitchComponent.

#[AsLiveComponent('playerSwitch')]
final class PlayerSwitchComponent
{
    use DefaultActionTrait;

    // TODO: I want to use objects like PlayerIdentifier here, instead of strings and ints.
    #[LiveProp]
    public string $playerId;
    #[LiveProp]
    public int $game;
    #[LiveProp]
    public string $tableId;
    #[LiveProp]
    public bool $active;

    public function __construct(
        private CommandBus $commandBus,
    ) {
    }

    #[LiveAction]
    public function kick(): void
    {
        $this->commandBus->dispatch(
            new KickPlayer(
                TableIdentifier::fromString($this->tableId),
                PlayerIdentifier::fromString($this->playerId),
                GameNumber::fromInteger($this->game),
            )
        );
    }

    #[LiveAction]
    public function join(): void
    {
        $this->commandBus->dispatch(
            new JoinPlayer(
                TableIdentifier::fromString($this->tableId),
                PlayerIdentifier::fromString($this->playerId),
                GameNumber::fromInteger($this->game),
                null
            )
        );
    }
}

So the actions send the correct commands to the command bus, that I can just inject into this component.

And guess what: it works!

(If you know how I can get this iframe bigger with Nikola, please let me know 😉)

As you might have noticed, I don't change the state of the switch in the component, although this is certainly possible. But in my case, the whole score sheet is updated by Symfony Turbo and Mercure.

Please be aware that all this is work in progress. But I'm glad this works.

PhpStorm screenshot

Commentaar

Comments powered by Disqus