Open3

[symfony/UX Live Component] Form で ToDoリスト的なデータを扱う際の考察

ピン留めされたアイテム
tanaka_tarotanaka_taro

環境

  • Symfony6, 6.4

概要

UX Live Component と Form を使って 次のようなToDoリスト的なものを作っている。

そして、こう言った形のコンポーネントは、どのように作るのが良いのかを調べている。現在、試してみた方法が次。LiveCollectionTypeを使うが一番きれいに書けるので、出来ればこの方法を使いたい。

名称 特徴
LiveCollectionTypeを使う データの構造が親と子の関係になっている場面でのみ使える。親と子の構造が必要。つまり1つのentityだけを使ったToDoリストは出来ない
コンポーネントを2つ使う 親コンポーネントでentityの配列を持ち、子コンポーネントにentityを渡してフォームを作る。1つのentityだけを使ったToDoリストも作れる

LiveCollectionTypeを使うに関してだけど、恐らくはデータの構造が親と子の関係になっている場面でのみ使えるんじゃあないかって私は考えている(23-11-22)。

1つのentityだけを使ったToDoリストを作るにはコンポーネントを2つ使うの書き方になる。

しかし、この書き方が正しいのかは分かってない。

tanaka_tarotanaka_taro

コンポーネントを2つ使う 方法の不具合、意図した通りに描画されない

先ず、私は次のようなタスクリストを作っている。

問題点.
次の順番で処理すると、表示上の不具合が出る

  1. タスクを追加する
  2. addItemする(空のタスク追加)
  3. 1で追加したタスクを削除する

上記をすると画面上 (2) のものが消えてしまう。
しかし、データ上では (1) で追加したタスクが削除されてる

修正方法.

この情報は次の資料を確認している。

symfony/nested-components

資料には次のようにあった。

if a parent component re-renders, it won't automatically cause the child to re-render
if a model in a child updates, it won't also update that model in its parent

ポイントとしては次。

  • 親コンポーネントが再描写されたとしても、子コンポーネントが自動的に再描画されたりはしない
  • 子の中でモデルを更新しても、親のモデルが更新されたりはしない

この不具合を修正する為に、次の資料が参考になった。

symfony/Rendering Quirks with List of Embedded Components

親コンポーネントの中で、子コンポーネントを読み込む際に key を設定してやることで、不具合は出なくなる。

{% for key, entity in entities %}
+  {{ component('Life:ActivityLogItem', {entity: entity, key: key}) }}
-  {{ component('Life:ActivityLogItem', {entity: entity}) }}
{% endfor %}
tanaka_tarotanaka_taro

コンポーネント同士で連携させる

例えば。
前日の、未達成のタスク一覧の中から、指定したものを今日のタスクに追加したいとする。イメージは次のような感じ。

これは二つのコンポーネントが分離している。

やりたいのは "+Add To Today Log" ボタンを押したときに、下のタスク一覧にそのタスクにリアルタイムに追加されるようにしたい。

live component で、コンポーネント間で連携させたい時には次の資料が参考になった。
https://symfony.com/bundles/ux-live-component/current/index.html#communication-between-components-emitting-events

emit する際の型について

公式のデモとかを見てると、entity をそのまま指定している例を見かけた。

#[LiveListener('line_item:save')]
public function saveLineItem(#[LiveArg] int $key, #[LiveArg] Product $product, #[LiveArg] int $quantity): void
{
    if (!isset($this->lineItems[$key])) {
        
        return;
    }

    $this->lineItems[$key]['productId'] = $product->getId();
    $this->lineItems[$key]['quantity'] = $quantity;
}

デモは次。
https://ux.symfony.com/demos/live-component/invoice

しかし、私の環境にて同様なやり方をすると エラーが起きて、"must be of type array" と言われる。確かに ComponentToolsTrait.php の emit を確認すると array 型になっている。

public function emit(string $eventName, array $data = [], string $componentName = null): void
{
  $this->liveResponder->emit($eventName, $data, $componentName);
}

デモの方のファイルも確認してみると、型はarrayで固定されている。

完全に私の勘違いだった。
LiveListener では次のようにする。

#[LiveListener('Life:ActivityLog:AddPreviousTask')]
public function addPreviousTask(#[LiveArg] LifeActivityLog $lifeActivityLog)

そうしてから、emitする際に送るentityの id 指定してやることで、LiveListener の方では entity を受け取ることが出来た。

$this->emit('Life:ActivityLog:AddPreviousTask', ['lifeActivityLog' => $entity->getId()]);