使用 AlpineJS 和 @entangle 指令在 Livewire 中嵌套组件

Nested components in Livewire with AlpineJS and @entangle directive

我目前正在使用 Livewire 和 AlpineJS 开发一个项目。

我必须使用将 contenteditable 设置为“true”的 div 来访问和修改我的数据模型。

我想要的

我想使用 @entangle 在 Livewire 和 Alpine 之间共享状态。

我的代码

我的代码分为两部分。 第一部分依赖于 full-Page component,我只是在其中注册了一个 $fruits 数组。 第二个是一个包含“表单”的组件,允许我通过 @entangle 和一个 div 将 contenteditable 设置为 true.

来访问和修改数据。
<?php

namespace App\Http\Livewire;

use Livewire\Component;

class ListFruit extends Component
{

    protected $listeners = ['addFruit'];

    public array $fruits = [];

    public function mount()
    {
        $this->addFruit();
    }

    public function addFruit()
    {
        $this->fruits[] = $this->makeBlankFruit();
    }



    public function makeBlankFruit(): array
    {
        return [
            'type' => '',
            'color' => ''
        ];
    }

      public function render()
    {
        return view('livewire.list-fruit');
    }
}

views/livewire/list-fruit.blade.php文件

<div  class="w-full container mx-auto">

    <h3 class="text-2xl font-sembibold">
          Fruits
    </h3>

    <div id="fruits">
       @foreach($fruits as $fruitIndex=> $fruit)

         <livewire:fruit :fruitIndex="$fruitIndex" :fruit="fruit" :wire:key="$fruitIndex" >

       @endforeach
    </div>

</div>

水果成分

<?php

namespace App\Http\Livewire;

use Livewire\Component;

class Fruit extends Component
{

    public array $fruit = [];

    public string $fruitIndex;

    public function render()
    {
        return view('livewire.fruit');
    }
}

views/livewire/fruit.blade.php 文件



<div x-data="{type: @entangle('fruit.type'), color: @entangle('fruit.color')}"
    x-init="console.log(type)"
>
    <div>
        <div x-on:blur="type = $event.target.innerHTML" contenteditable="true">{{ $fruit['type'] }}</div>
        <div x-on:blur="color = $event.target.innerHTML" contenteditable="true">{{ $fruit['color'] }}</div>
    </div>

    <div>
        $type @ Livewire: {{ $fruit['type'] }}
        $color @ Livewire: {{ $fruit['color']  }}
    </div>
    <div>
        type @ AlpineJS: <span x-text="type"></span>
        color @ AlpineJS: <span x-text="color"></span>
    </div>
</div>

如果我的主要组件中只有一个包含一些属性的数组,假设:

 public array $fruit = [
  'type' => '',
  'color' => '',
 ];

我可以用我的水果访问它们。blade.php

我也尝试过这样的事情。

{type: @entangle('fruits.'. $fruitIndex.'.color'), color: @entangle('fruits.'. $fruitIndex.'.type')}

我得到了什么

在我的 AlpineJs 组件中,我读取了一个 [object Object] 代理,并且在修改我的 fruit.color 或 fruit.type 属性 时,livewire 部分没有更新。

我不知道为什么。 在我最后的尝试中,我尝试将我的水果分成多个子组件,以便我可以在一个数组上工作。

我正在寻找一个死胡同,所以在此先感谢您的帮助。 狸猫

我认为你应该简化你的例子。尽管您的 real-world 代码可能比您的(毫无疑问是人为的)水果示例更复杂,但在我看来没有必要在此处嵌套 Livewire 组件。保留一个 PHP/Livewire class 和一个 Blade 文件要简单得多。然后使用 Alpine 的 x-for,而不是使用 Blade 和 @foreach 循环遍历您的数据。与当前趋势相反,我并不总是认为保持 file-sizes 较小是可取的,如果代价是层层嵌套(越来越难以调试)blade-templates(或 Livewire 组件)。

无论如何,抛开这一点,让我们回到你试图在这里建立的原则,这就是如何将 PHP/Livewire 数组与 Javascript/Alpine 变量纠缠在一起。

答案非常简单:您可以将任意复杂的 PHP data-structure(又名数组)与单个 JavaScript 变量纠缠在一起。正如您所期望的那样,如果您自己构建了框架,PHP 数组将简单地映射到 JSON object.

首先,设置你的复杂 back-end data-structure:

<?php

namespace App\Business\Tbd;

use Livewire\Component;

class StartLw extends Component
{
    public array $fruits = [];

    public function mount() {

        $fruit_template = ['apple', 'banana', 'garlic'];
        $fruit_colours  = ['green', 'yellow', 'pink'];
        $health_benefit = [
            'keeps doctor away',
            'the bend keeps you flexible',
            'None.  It\'s not really a fruit, though there is that vampire thing'
        ];

        foreach ($fruit_template as $id => $fruit) {
            $this->fruits[$fruit] = [
                'colour'    => $fruit_colours[$id],
                'benefit'   => $health_benefit[$id],
                'bought_by' => array_slice(['Jim', 'John', 'Sue'], 0, $id+1),
            ];
        }
    }

    public function render()
    {
        return view('my.livewire.template');
    }
}

然后下面的 Blade 模板将告诉您所有您需要了解的 JavaScript/JSON object(最初创建于 PHP)的内容。

<div class="container-fluid">
    <div class="row" x-data="{ fruits: @entangle('fruits') }">
        <div class="col-11" id="main">

            <p>Type of fruit: <span x-text="typeof fruits"></span></p>

            <template x-for="(fruit_info, fruit) in fruits" :key="fruit">
                <code>
                    Fruit type: <span x-text="fruit"></span><br />
                    Colour: <span x-text="fruit_info.colour"></span><br />
                    Health Superstitions: <span x-text="fruit_info.benefit"></span><br />
                    Bought by:
                    <template x-for="(pers, id) in fruit_info.bought_by" :key="id">
                        <span>
                            <span x-text="id"></span> => <span x-text="pers"></span>,&nbsp;
                        </span>
                    </template>
                    <br />
                    <br />
                </code>
            </template>

        </div>
    </div>
</div>

如您所见,如果您以足够的灵活性构建 Blade 模板,它们可以变得与底层 data-structure 无关,这(在模块化应用程序中)意味着它们可以被使用一遍又一遍。