Alpine.js「───やっと気づいた。livewireは、私の鞘だったのですね」

## 概要
**livewireとAlpine.jsは相性が良い！！**
**livewireにAlpine.jsをトッピング！！**

という文言をチラホラみかけます。
自分も普段livewireにalpine.jsをチラホラ使いますが(``x-show``ぐらい)
キッチリ使った事が無かったのでフロントをAlpine.jsバックをlivewireで分けてみようと思います。

## 投稿機能のようなものを作ってみる

- タイトルと内容を入力して送信する所まで作る
- フロントにlivewireディレクティブを書かない

### コード
> x-adminlteコンポーネントを使用していますがただのinputタグです

```php:php側
<?php

namespace App\Http\Livewire\Board;

use Livewire\Component;

class Create extends Component
{
    public array $board;

    public function mount(): void
    {
        $this->board = ['title' => null, 'content' => null];
    }

    public function render()
    {
        return view('livewire.board.create')
            ->extends('adminlte::page')
            ->section('content');
    }

    public function create(): void
    {
        dd($this->validate()['board']);
    }

    protected function rules(): array
    {
        return [
            'board.title' => 'required',
            'board.content' => 'nullable',
        ];
    }

    protected function validationAttributes(): array
    {
        return [
            'board.title' => 'タイトル',
            'board.content' => '内容',
        ];
    }
}

```


```html
<div class="pt-3">
    <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
    
    <div class="col-6 card card-body" x-data="boardApp">
        <form @submit.prevent="handleSubmit">
            <x-adminlte-input name="board.title" label="タイトル"
                              x-model="board.title"/>

            <div class="form-group form-check">
                <input type="checkbox" class="form-check-input" id="exampleCheck1" x-model="isContentEdit">
                <label class="form-check-label" for="exampleCheck1">内容を入力する</label>
            </div>

            <x-adminlte-textarea name="board.content" label="内容"
                                 x-bind:disabled="!isContentEdit"
                                 x-model="board.content"/>

            <x-adminlte-button label="登録" theme="primary" type="submit"/>
        </form>

    </div>
    <script>
        const boardApp = () => ({
            //livewireコンポーネントのboardと同期
            board: @entangle('board').defer,
            isContentEdit: false,
            init() {
                //isContentEditの変更を検知
                this.$watch('isContentEdit', (isContentEditValue) => {
                    //false時に値をnullに
                    if (!isContentEditValue) {
                        this.board.content = null;
                    }
                });
            },
            handleSubmit() {
                //livewireコンポーネントのcreateメソッド発火
                this.$wire.create();
            }
        })

        document.addEventListener('alpine:init', () => {
            Alpine.data('boardApp', boardApp);
        });
    </script>
</div>
```


## 出来た画面
![画面収録 2022-12-06 19.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/e628536e-5a8a-a78d-ff45-f734783ece11.gif)

### livewireだけで書いてみる
「内容を入力する」が切り替わるたびにリクエストが走りますが、
基本的に同じような感じに動きます。

```diff_php
<?php

namespace App\Http\Livewire\Board;

use Livewire\Component;

class Create extends Component
{
    public array $board;
+    public bool $isContentEdit;

    public function mount(): void
    {
        $this->board = ['title' => null, 'content' => null];
+        $this->isContentEdit = false;
    }

    public function render()
    {
        return view('livewire.board.create')
            ->extends('adminlte::page')
            ->section('content');
    }

    public function create(): void
    {
        dd($this->validate()['board']);
    }

+    public function updatedIsContentEdit()
+    {
+        if (!$this->isContentEdit) {
+            $this->board['content'] = null;
+        }
+    }

    protected function rules(): array
    {
        return [
            'board.title' => 'required',
            'board.content' => 'nullable',
        ];
    }

    protected function validationAttributes(): array
    {
        return [
            'board.title' => 'タイトル',
            'board.content' => '内容',
        ];
    }
}
```

```html
<div class="pt-3">
    <div class="col-6 card card-body">
        <form wire:submit.prevent="create">
            <x-adminlte-input name="board.title" label="タイトル" wire:model.defer="board.title"/>

            <div class="form-group form-check">
                <input type="checkbox" class="form-check-input" id="exampleCheck1" wire:model.lazy="isContentEdit">
                <label class="form-check-label" for="exampleCheck1">内容を入力する</label>
            </div>

            <fieldset @if(!$isContentEdit) disabled @endif>
                <x-adminlte-textarea name="board.content" label="内容"
                                     wire:model.defer="board.content"/>
            </fieldset>
            <x-adminlte-button label="登録" theme="primary" type="submit"/>
        </form>

    </div>
</div>
```

## おわりに
上記は唯のサンプルですが、
Bladeに書く処理を少なくしたり(分岐とか)
可能な限りリクエスト数を少なくする為にはAlpine.jsも使い慣れておく必要がありそうなので、ちらほら勉強しようと思います。