Alpine.js 素振り

例1: カウンター

現在のカウント:

<div x-data="{ count: 0 }">
  <!-- @click は x-on:click の短縮記法 -->
  <button @click="count++">増加</button>
  <button @click="count--">減少</button>
  <button @click="count = 0">リセット</button>
  <p>現在のカウント: <span x-text="count"></span></p>
</div>

例2: 表示/非表示の切り替え

この文章は表示/非表示が切り替わります。

<div x-data="{ open: false }">
  <button @click="open = !open" x-text="open ? '非表示にする' : '表示する'"></button>
  <p x-show="open">この文章は表示/非表示が切り替わります。</p>
</div>

例3: 入力フォームとバインディング

入力された内容:

<div x-data="{ message: '' }">
  <input type="text" x-model="message" placeholder="何か入力してください">
  <p>入力された内容: <span x-text="message"></span></p>
</div>

例4: ループ

<div x-data="{ items: ['りんご', 'バナナ', 'オレンジ'] }">
  <ul>
    <template x-for="item in items" :key="item">
      <li x-text="item"></li>
    </template>
  </ul>
</div>

例5: x-show による条件分岐

ようこそ!ログイン中です。

ログインしてください。

※ x-show は display:none で表示/非表示を切り替えます(要素は常に DOM に存在)

<div x-data="{ loggedIn: false }">
  <button @click="loggedIn = !loggedIn">ログイン状態を切り替え</button>
  <p x-show="loggedIn">ようこそ!ログイン中です。</p>
  <p x-show="!loggedIn">ログインしてください。</p>
</div>

例6: x-if による条件分岐

※ x-if は要素を DOM から追加/削除します(条件が false の時は DOM に存在しない)

<div x-data="{ showContent: false }">
  <button @click="showContent = !showContent">コンテンツを切り替え</button>
  <template x-if="showContent">
    <div>
      <p>このコンテンツは x-if で制御されています。</p>
      <p>x-show と異なり、DOM から完全に追加/削除されます。</p>
    </div>
  </template>
  <template x-if="!showContent">
    <p>コンテンツが非表示の状態です。</p>
  </template>
</div>

例7: 検索フィルター

該当するレシピが見つかりません。

<div x-data="{
  query: '',
  items: [
    { title: 'カレーライス', description: 'スパイスと野菜を煮込んだ定番料理...' },
    { title: '親子丼', description: '鶏肉と卵のとじ丼...' },
    { title: '肉じゃが', description: '牛肉とじゃがいもの煮物...' },
    // ... 残り17件省略
    { title: 'だし巻き卵', description: 'だし入り卵を巻いて焼く一品...' }
  ],
  get filteredItems() {
    const normalizedQuery = this.query.trim().toLowerCase();

    if (!normalizedQuery) {
      return this.items;
    }

    return this.items.filter((item) => {
      const title = item.title.toLowerCase();
      const description = item.description.toLowerCase();

      return title.includes(normalizedQuery) || description.includes(normalizedQuery);
    });
  }
}">
  <label for="search-query" class="sr-only">料理名や説明文で検索</label>
  <input id="search-query" type="search" x-model="query" class="search-input" placeholder="料理名や説明文で検索">
  <div class="recipe-list">
    <template x-for="item in filteredItems" :key="item.title">
      <article class="recipe-card">
        <h3 x-text="item.title"></h3>
        <p x-text="item.description"></p>
      </article>
    </template>
  </div>
  <p x-cloak x-show="filteredItems.length === 0" class="empty-state">該当するレシピが見つかりません。</p>
</div>