LoginSignup
1
0

Onsen UI+Alpine.js を使ってみた

Last updated at Posted at 2023-12-02

はじめに

いわゆるウェブアプリを作るのに自分は Vue.js を使っています。

Onsen UI+Vue.js を使ってみた - Qiita

友人に Alpine.js を紹介されました。よさそうなので使ってみることにします。

Onsen UI とは

Onsen UI は、HTML5 モバイルアプリ開発用のオープンソースの UI フレームワークおよびコンポーネントです。独自の HTML タグを記述してモバイルアプリの画面を作ることができます。

Onsen UI 2: HTML5モバイルアプリを速く、美しく - Onsen UI

Alpine.js とは

Alpine.js は、新しい、軽量の JavaScript フレームワークです。Vue.js と同様のディレクティブが用意されています。コンポーネントは作れませんが、そのために学習しやすくなっています。

Alpine.js

Onsen UI+Alpine.js を使ってみた

  • Onsen UI 2.12
  • Alpine.js 3.13

Onsen UI を使う

CDN を使うことにします。HTML ファイルに以下を追記します。

app.html
    <link rel="stylesheet" href="https://unpkg.com/onsenui@2.12.8/css/onsenui.css">
    <link rel="stylesheet" href="https://unpkg.com/onsenui@2.12.8/css/onsen-css-components.css">
    <script src="https://unpkg.com/onsenui@2.12.8/js/onsenui.min.js"></script>  

Onsen UI は独自タグを使います。

app.html
    <ons-navigator id="main-navi" page="list-page">
    </ons-navigator>    

    <template id="list-page">
      <ons-page id="list-page">
        <ons-toolbar id="list-toolber">
          <div class="center">Try Onsen + Alpine.js</div>
        </ons-toolbar>
        <ons-list id="contact-list">
        </ons-list>
      </ons-page>
    </template>

ツールバーがついたリストビューが本体のページを準備します。

JavaScript のコードは ons.ready() 関数に記述します。

app.js
  ons.ready(function(){
    ....
  });

ドキュメント:Onsen UI 2 Docs - Onsen UI Framework

Alpine.js を使う

CDN を使うことにします。HTML ファイルに以下を追記します。

app.html
    <script defer src="https://unpkg.com/alpinejs@3.13.2/dist/cdn.min.js"></script>

ドキュメント:Docs - Alpine.js

データを準備する

まず、データを準備します。

app.js
    const _contacts = [
      { 
        firstName: "Alice", lastName: "Arten", hasPhone: true, phoneNumber: "555-0184" 
      },{ 
        firstName: "Bob", lastName: "Brigham", hasPhone: true, phoneNumber: "555-0163" 
      },{ 
        firstName: "Charlie", lastName: "Campbell", hasPhone: true, phoneNumber: "555-0129" 
      }
    ];

データをレンダリングする

<ons-page id="list-page"> の内容が Alpine.js で操作されるよう x-data ディレクティブで指定します。
さらに、変数 _contacts を別名 list で扱えるよう指定します。

app.html
      <ons-page id="list-page" x-data="{ list: _contacts }">
        以下略

<ons-list id="content-list"> にデータがリスト表示されるようテンプレートを追記します。
リストを表示するので x-for ディレクティブを使います。

app.html
        <ons-list id="contact-list">
          <template x-for='item in list'>
            <ons-list-item>
              <div class="center"><span x-text='item.firstName'></span>&nbsp;<span x-text='item.lastName'></span></div>
            </ons-list-item>
          </template>
        </ons-list>

ユーザ入力をハンドリングする

リストビューに「編集」と「削除」ボタンを追加します。
x-on ディレクティブで実行したいメソッドを指定します。

app.html
          <template x-for='item in list'>
            <ons-list-item>
              中略
              <div class="right">
                <ons-button modifier="quiet" x-on:click="showDetail(item)"><ons-icon icon="pencil"></ons-icon></ons-button>
                <ons-button modifier="quiet" x-on:click="deleteItem(item)"><ons-icon icon="trash-o"></ons-icon></ons-button>              
              </div>

実行するメソッドを x-data に設定します。

app.atml
      <ons-page id="list-page" x-data="{ 
        list: _contacts,

        showDetail(item) {
          console.log("showDetail:", JSON.stringify(item));
        },
        deleteItem(item) {
          console.log("deleteItem:", JSON.stringify(item));
        }
      }">

このコードはエラーになります。↑
x-data に指定した " で始めた文字列の中で " を使っているからです。
エラーにならないよう "' を注意して使います。↓

app.atml
      <ons-page id="list-page" x-data='{ 
        list: _contacts,

        showDetail(item) {
          console.log("showDetail:", JSON.stringify(item));
        },
        deleteItem(item) {
          console.log("deleteItem:", JSON.stringify(item));
        }
      }'>

x-data に指定する内容を関数にする

HTML タグに JavaScript コードを書けるのが Alpine.js のいいところですが、"' を注意して書かないといけないのは不便です。
x-data に指定する内容を関数にして <script> のエリアに書くことができます。

app.html
      <ons-page id="list-page" x-data="list_data()">
app.js
    const list_data = () => ({
      list: _contacts,

      showDetail(item) {
        console.log("showDetail:", JSON.stringify(item));
      },
      deleteItem(item) {
        console.log("deleteItem:", JSON.stringify(item));
      }
    });

データを削除できるようにする

app.js
    const list_data = () => ({
      中略
      deleteItem(item) {
        中略
        const index = this.list.indexOf(item);
        this.list.splice(index, 1);
      }
    });

データを操作しただけだが、画面も変わります。

詳細ページを表示する

app.html
    <template id="detail-page">
      <ons-page id="detail-page">
        <ons-toolbar>
          <div class="left"><ons-back-button>Back</ons-back-button></div>
          <div class="center">Detail</div>
        </ons-toolbar>
        <ons-list id="detail">
          <ons-list-item>
            <ons-input placeholder="First Name"></ons-input>
          </ons-list-item>
          <ons-list-item>
            <ons-input placeholder="Last Name"></ons-input>
          </ons-list-item>
        </ons-list>
      </ons-page>
    </template>
app.js
    const list_data = () => ({
      中略
      showDetail(item) {
        中略
        document.querySelector('#main-navi').pushPage('detail-page');
      },

Onsen UI の機能でページ遷移します。

詳細ページに内容を表示する

ポインタ _selected を用意して、一覧ページで選択した内容をセットします。

app.js
    let _selected = null;

    const list_data = () => ({
      中略
      showDetail(item) {
        中略
        _selected = item;
        document.querySelector('#main-navi').pushPage('detail-page');
      },

これを詳細ページで参照できるようにします。

app.js
    const detail_data = () => ({
      item: _selected
    });
app.html
      <ons-page id="detail-page" x-data="detail_data()">
        中略
        <ons-list id="detail">
          <ons-list-item>
            <ons-input x-model="item.firstName" placeholder="First Name"></ons-input>
          </ons-list-item>
          <ons-list-item>
            <ons-input x-model="item.lastName" placeholder="Last Name"></ons-input>
          </ons-list-item>

データバインディングのため x-model ディレクティブを追記します。↑

双方向データバインディングする

_selected の内容が画面に表示されるだけでなく、
画面の要素の内容で _selected の内容が更新されるようになります。

_selected はポインタです。値がコピーされているわけではありません。
詳細画面の要素の内容を変えると、_contents の内容も変わっています。

さらに、一覧ページの画面の内容も変わっています。

Alpine.js のおかげで、双方向データバインディングが簡単にできます。

データを追加できるようにする

ツールバーに「+」ボタンを追加して、データを追加できるようにします。

app.html
      <ons-page id="list-page" x-data="list_data()">
        <ons-toolbar id="list-toolber">
          中略
          <div class="right">
            <ons-toolbar-button x-on:click="addItem()"><ons-icon icon="fa-plus"></ons-icon></ons-toolbar-button>
          </div>
app.js
    const list_data = () => ({
      中略
      addItem() {
        console.log("addItem:");
        const contact = {
          firstName: "", lastName: "", hasPhone: false, phoneNumber: "" 
        };
        this.list.push(contact);
        _selected = contact;
        document.querySelector('#main-navi').pushPage('detail-page');
      }

詳細ページで保存するまでデータを変更しないようにする

詳細ページで内容を変更すると否応なしに一覧ページの内容も変わります。また、一覧ページで「+」実行した時点で一覧に追加されます。
詳細ページに「保存」ボタンを用意して、そこで初めて内容を変更するようにしたいものです。

詳細ページのデータを扱う変数 _current を、_selected と別に用意して使用します。詳細ページを開く前に、この変数に内容をセットします。↓

app.js
    let _current = null;
app.js
      showDetail: function(item){
        中略
        _selected = item;
        _current = { ...item };
        document.querySelector('#main-navi').pushPage('detail-page');
      },
      中略
      addItem() {
        中略
        _selected = null;
        _current = {
          firstName: "", lastName: "", hasPhone: false, phoneNumber: "" 
        };
        document.querySelector('#main-navi').pushPage('detail-page');
      }

    中略
    const detail_data = () => ({
      中略
      item: _current,

詳細ページに「保存」ボタンを用意して、内容を保存するようにします。↓

app.html
      <ons-page id="detail-page" x-data="detail_data()">
        <ons-toolbar>
          中略
          <div class="right">
             <ons-toolbar-button x-on:click="saveItem()">Save</ons-toolbar-button>
          </div>

app.js
    const detail_data = () => ({
      中略
      list: _contacts,
      中略
      saveItem: function(){
        console.log("saveItem:");

        if (_selected) {
          Object.assign(_selected, this.item);
        }
        else {
          this.list.push({ ...this.item });
        }

        document.querySelector('#main-navi').popPage();
      }
    });

内容を保存した時点で、詳細ページから一覧ページに戻るようにします。↑

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0