はじめに
いわゆるウェブアプリを作るのに自分は 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 と同様のディレクティブが用意されています。コンポーネントは作れませんが、そのために学習しやすくなっています。
Onsen UI+Alpine.js を使ってみた
- Onsen UI 2.12
- Alpine.js 3.13
Onsen UI を使う
CDN を使うことにします。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 は独自タグを使います。
<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()
関数に記述します。
ons.ready(function(){
....
});
ドキュメント:Onsen UI 2 Docs - Onsen UI Framework
Alpine.js を使う
CDN を使うことにします。HTML ファイルに以下を追記します。
<script defer src="https://unpkg.com/alpinejs@3.13.2/dist/cdn.min.js"></script>
ドキュメント:Docs - Alpine.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
で扱えるよう指定します。
<ons-page id="list-page" x-data="{ list: _contacts }">
以下略
<ons-list id="content-list">
にデータがリスト表示されるようテンプレートを追記します。
リストを表示するので x-for
ディレクティブを使います。
<ons-list id="contact-list">
<template x-for='item in list'>
<ons-list-item>
<div class="center"><span x-text='item.firstName'></span> <span x-text='item.lastName'></span></div>
</ons-list-item>
</template>
</ons-list>
ユーザ入力をハンドリングする
リストビューに「編集」と「削除」ボタンを追加します。
x-on
ディレクティブで実行したいメソッドを指定します。
<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
に設定します。
<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
に指定した "
で始めた文字列の中で "
を使っているからです。
エラーにならないよう "
と '
を注意して使います。↓
<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>
のエリアに書くことができます。
<ons-page id="list-page" x-data="list_data()">
const list_data = () => ({
list: _contacts,
showDetail(item) {
console.log("showDetail:", JSON.stringify(item));
},
deleteItem(item) {
console.log("deleteItem:", JSON.stringify(item));
}
});
データを削除できるようにする
const list_data = () => ({
中略
deleteItem(item) {
中略
const index = this.list.indexOf(item);
this.list.splice(index, 1);
}
});
データを操作しただけだが、画面も変わります。
詳細ページを表示する
<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>
const list_data = () => ({
中略
showDetail(item) {
中略
document.querySelector('#main-navi').pushPage('detail-page');
},
Onsen UI の機能でページ遷移します。
詳細ページに内容を表示する
ポインタ _selected
を用意して、一覧ページで選択した内容をセットします。
let _selected = null;
const list_data = () => ({
中略
showDetail(item) {
中略
_selected = item;
document.querySelector('#main-navi').pushPage('detail-page');
},
これを詳細ページで参照できるようにします。
const detail_data = () => ({
item: _selected
});
<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 のおかげで、双方向データバインディングが簡単にできます。
データを追加できるようにする
ツールバーに「+」ボタンを追加して、データを追加できるようにします。
<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>
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
と別に用意して使用します。詳細ページを開く前に、この変数に内容をセットします。↓
let _current = null;
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,
詳細ページに「保存」ボタンを用意して、内容を保存するようにします。↓
<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>
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();
}
});
内容を保存した時点で、詳細ページから一覧ページに戻るようにします。↑