「JavaScript library」の版間の差分
(Templateの節追加。) |
(ToolとFrameworkの節追加。) |
||
1行目: | 1行目: | ||
== Grunt == | [[Category:JavaScript]] | ||
== Tool == | |||
=== Grunt === | |||
=== About === | ==== About ==== | ||
JavaScriptのタスクランナー。ビルドやファイルの整形、変換などを行ってくれる。 | JavaScriptのタスクランナー。ビルドやファイルの整形、変換などを行ってくれる。 | ||
8行目: | 10行目: | ||
[https://gruntjs.com/ Grunt: The JavaScript Task Runner] | [https://gruntjs.com/ Grunt: The JavaScript Task Runner] | ||
==== grunt vs. gulp ==== | ===== grunt vs. gulp ===== | ||
[https://ics.media/entry/3290/ 絶対つまずかないGulp 5入門 - インストールとSassを使うまでの手順 - ICS MEDIA] | [https://ics.media/entry/3290/ 絶対つまずかないGulp 5入門 - インストールとSassを使うまでの手順 - ICS MEDIA] | ||
16行目: | 18行目: | ||
* gulp: JavaScriptで非同期。 | * gulp: JavaScriptで非同期。 | ||
=== Gruntfile === | ==== Gruntfile ==== | ||
Gruntfile.jsかGruntfile.coffeがタスクの定義ファイル。 | Gruntfile.jsかGruntfile.coffeがタスクの定義ファイル。 | ||
以下のコマンドでgruntで実行可能なタスク一覧が表示される。 | 以下のコマンドでgruntで実行可能なタスク一覧が表示される。 | ||
grunt --help | |||
単にgruntとだけ実行すると、defaultのタスクが実行される。基本はサブコマンドを指定する。 | 単にgruntとだけ実行すると、defaultのタスクが実行される。基本はサブコマンドを指定する。 | ||
== Node.js == | === Node.js === | ||
=== Install === | ==== Install ==== | ||
[https://nodejs.org/en/download/package-manager Node.js — Download Node.js®] | [https://nodejs.org/en/download/package-manager Node.js — Download Node.js®] | ||
31行目: | 33行目: | ||
Linuxの場合、以下。 | Linuxの場合、以下。 | ||
# installs nvm (Node Version Manager) | |||
curl -o- <nowiki>https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh</nowiki> | bash | |||
# download and install Node.js (you may need to restart the terminal) | |||
nvm install 22 | |||
# verifies the right Node.js version is in the environment | |||
node -v # should print `v22.12.0` | |||
# verifies the right npm version is in the environment | |||
npm -v # should print `10.9.0` | |||
最初の行を実行してインストールしたら~/.bashrcに以下の内容が追記される。即座にインストールを反映したければ現在の端末でも実行する。 | 最初の行を実行してインストールしたら~/.bashrcに以下の内容が追記される。即座にインストールを反映したければ現在の端末でも実行する。 | ||
export NVM_DIR="$HOME/.nvm" | |||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm | |||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion | |||
== jQuery == | == Framework == | ||
=== jQuery === | |||
[https://api.jquery.com/ jQuery API Documentation] | [https://api.jquery.com/ jQuery API Documentation] | ||
=== レガシー === | ==== レガシー ==== | ||
* [https://yori-york.co.jp/news/190612_jquery jQueryはもう古い!?これから・・・ | YORIYORK|栃木県佐野市] | * [https://yori-york.co.jp/news/190612_jquery jQueryはもう古い!?これから・・・ | YORIYORK|栃木県佐野市] | ||
61行目: | 64行目: | ||
上記2点が大きい。jQueryじゃないとできないことが大幅に減った。標準APIでこなしたほうがいいだろう。 | 上記2点が大きい。jQueryじゃないとできないことが大幅に減った。標準APIでこなしたほうがいいだろう。 | ||
=== ajaxのasync/awaitでの書換 === | ==== ajaxのasync/awaitでの書換 ==== | ||
[https://qiita.com/toromo/items/42195b7a4480fb363478 $.ajax(jQuery.ajax)の非同期処理をasync awaitの入れ子なし同期処理で記述する #JavaScript - Qiita] | [https://qiita.com/toromo/items/42195b7a4480fb363478 $.ajax(jQuery.ajax)の非同期処理をasync awaitの入れ子なし同期処理で記述する #JavaScript - Qiita] | ||
ajaxを使っている関数の定義にasync。ajax関数の使用箇所にawait。これでうまくいく。 | ajaxを使っている関数の定義にasync。ajax関数の使用箇所にawait。これでうまくいく。 | ||
** | |||
* フルーツ名を取得する | |||
* | |||
* @param {string} fruitId | |||
*/ | */ | ||
async function getFruitName(fruitId) { | |||
const fruitRequest = {id: fruitId} | |||
const fruitResult = await ajaxGetFruit(fruitRequest); | |||
return fruitResult.name; | |||
} | |||
/** | |||
* フルーツのajax[GET]を実施する | |||
* | |||
* @param {object} request | |||
* @returns | |||
*/ | */ | ||
function ajaxGetFruit(request) { | |||
return $.ajax({ | |||
url: '/fruit/name', | |||
type: "GET", | |||
async: true, | |||
contentType: "application/json", | |||
data: JSON.stringify(request), | |||
dataType: "json", | |||
}).then( | |||
function (result) { | |||
// 正常終了 | |||
resolve(result); | |||
}, | |||
function () { | |||
// エラー | |||
reject(); | |||
} | |||
) | |||
} | |||
「[https://api.jquery.com/jQuery.ajax/ jQuery.ajax() | jQuery API Documentation]」にあるように、jquery 1.5から$.ajaxの返却値はPromiseの派生クラス。そのまま返却させて良い。 | 「[https://api.jquery.com/jQuery.ajax/ jQuery.ajax() | jQuery API Documentation]」にあるように、jquery 1.5から$.ajaxの返却値はPromiseの派生クラス。そのまま返却させて良い。 | ||
=== DataTables === | ==== DataTables ==== | ||
jqueryのプラグイン。テーブル処理をうまくやってくれる。 | jqueryのプラグイン。テーブル処理をうまくやってくれる。 | ||
==== Option ==== | ===== Option ===== | ||
* [https://legacy.datatables.net/ref.html DataTables - Reference] | * [https://legacy.datatables.net/ref.html DataTables - Reference] | ||
* [https://datatables.net/reference/option/ Options] | * [https://datatables.net/reference/option/ Options] | ||
==== Old ==== | ===== Old ===== | ||
[https://legacy.datatables.net/index-2.html DataTables (table plug-in for jQuery)] | [https://legacy.datatables.net/index-2.html DataTables (table plug-in for jQuery)] | ||
v1.9以下の古いバージョンのサイトはこちらにある。 | v1.9以下の古いバージョンのサイトはこちらにある。 | ||
==== ServerSide ==== | ===== ServerSide ===== | ||
[https://datatables.net/manual/server-side Server-side processing] | [https://datatables.net/manual/server-side Server-side processing] | ||
通常はクライアント画面のみ。全データを検索したい場合、serverSideオプションが必要。ただし、このオプションはdatatablesのv1.10以上じゃない。 | 通常はクライアント画面のみ。全データを検索したい場合、serverSideオプションが必要。ただし、このオプションはdatatablesのv1.10以上じゃない。 | ||
==== Pagination ==== | ===== Pagination ===== | ||
ページ表示件数に関するオプションがいくつかある。 | ページ表示件数に関するオプションがいくつかある。 | ||
127行目: | 130行目: | ||
* lengthMenu/aLengthMenu: リストボックスの項目。デフォルトは[10, 25, 50, 100]。-1 ("All") で全表示。 | * lengthMenu/aLengthMenu: リストボックスの項目。デフォルトは[10, 25, 50, 100]。-1 ("All") で全表示。 | ||
== Nuxt.js == | === Nuxt.js === | ||
=== 終端スラッシュありへのリダイレクト === | ==== 終端スラッシュありへのリダイレクト ==== | ||
[https://qiita.com/too/items/1b0944f5acb2aeb4e9b9 Nuxt.jsにおける末尾スラッシュを統一する方法 #amplify - Qiita] | [https://qiita.com/too/items/1b0944f5acb2aeb4e9b9 Nuxt.jsにおける末尾スラッシュを統一する方法 #amplify - Qiita] | ||
// middleware/trailingSlash.js | |||
/** | |||
* 終端スラッシュありのURLにリダイレクトする。 | |||
* @param {Object} Nuxt.jsのContextオブジェクト。 | |||
*/ | */ | ||
export default ({ route, redirect }) => { | |||
if (route.path.endsWith('/')) { | |||
return | |||
} | |||
let to = route.path + '/' | |||
if (route.query) { | |||
to += | |||
'?' + | |||
Object.entries(route.query) | |||
.map((e) => e.join('=')) | |||
.join('&') | |||
} | |||
to += route.hash | |||
redirect(301, to) | |||
} | |||
=== //へのアクセスエラー === | ==== //へのアクセスエラー ==== | ||
* <nowiki>https://github.com/vuejs/vue-router/issues/2593</nowiki> | * <nowiki>https://github.com/vuejs/vue-router/issues/2593</nowiki> | ||
160行目: | 163行目: | ||
そのため、プラグインにして、router.beforeEachで変化前に書き換え処理を入れた。 | そのため、プラグインにして、router.beforeEachで変化前に書き換え処理を入れた。 | ||
// plugins/redirect.js | |||
export default ({ app }) => { | |||
app.router.beforeEach((to, from, next) => { | |||
// 二重スラッシュを一重スラッシュに変換する。 | |||
// 素直にnextでリダイレクトすると、一度もともとのtoのURLでDOMの更新に進んでエラーが出る。 | |||
// そのためnext(false)でtoは使わずにpushで履歴を差し替える。 | |||
if (/\/\/+/.test(to.path)) { | |||
next(false) | |||
app.router.push(to.path.replace(/\/\/+/g, '/')) | |||
} else { | |||
next() | |||
} | |||
}) | |||
} | |||
== Vue.js == | === Vue.js === | ||
=== Naming === | ==== Naming ==== | ||
[https://qiita.com/ngron/items/ab2a17ae483c95a2f15e 【Vue】単一ファイルコンポーネントの命名規則まとめ【ファイル名から記法まで】 #Vue.js - Qiita] | [https://qiita.com/ngron/items/ab2a17ae483c95a2f15e 【Vue】単一ファイルコンポーネントの命名規則まとめ【ファイル名から記法まで】 #Vue.js - Qiita] | ||
=== radioのbool === | ==== radioのbool ==== | ||
[https://stackoverflow.com/questions/45187048/vue-binding-radio-to-boolean vuejs2 - Vue: Binding radio to boolean - Stack Overflow] | [https://stackoverflow.com/questions/45187048/vue-binding-radio-to-boolean vuejs2 - Vue: Binding radio to boolean - Stack Overflow] | ||
v-bind:か:で型を合わせて代入する。stringにするとうまく認識されない。 | v-bind:か:で型を合わせて代入する。stringにするとうまく認識されない。 | ||
=== IMEの入力制限 === | ==== IMEの入力制限 ==== | ||
IME以外はformatterで処理して、IMEだけcompositionendで処理すればいい。 | IME以外はformatterで処理して、IMEだけcompositionendで処理すればいい。 | ||
<!-- FormGroupText.vue --> | <!-- FormGroupText.vue --> | ||
318行目: | 321行目: | ||
<nowiki></script></nowiki> | <nowiki></script></nowiki> | ||
==== error '<template>' cannot be keyed. Place the key on real elements instead vue/no-template-key ==== | ===== error '<template>' cannot be keyed. Place the key on real elements instead vue/no-template-key ===== | ||
[https://stackoverflow.com/questions/56476413/custom-elements-in-iteration-require-v-bindkey-directives vue.js - Custom elements in iteration require 'v-bind:key' directives - Stack Overflow] | [https://stackoverflow.com/questions/56476413/custom-elements-in-iteration-require-v-bindkey-directives vue.js - Custom elements in iteration require 'v-bind:key' directives - Stack Overflow] | ||
==== ハイライト ==== | ===== ハイライト ===== | ||
<nowiki>https://forum.vuejs.org/t/highlight-in-html-a-new-object-in-javascript-array/38877/9</nowiki> | <nowiki>https://forum.vuejs.org/t/highlight-in-html-a-new-object-in-javascript-array/38877/9</nowiki> | ||
330行目: | 333行目: | ||
検索キーワードで検索対象をsplitして、key/value形式で順番に配列で配置する。 | 検索キーワードで検索対象をsplitして、key/value形式で順番に配列で配置する。 | ||
==== カスタムコンポーネントのv-bind ==== | ===== カスタムコンポーネントのv-bind ===== | ||
[https://stackoverflow.com/questions/42918710/how-to-use-v-bind-in-a-custom-component javascript - How to use v-bind in a custom component? - Stack Overflow] | [https://stackoverflow.com/questions/42918710/how-to-use-v-bind-in-a-custom-component javascript - How to use v-bind in a custom component? - Stack Overflow] | ||
[https://github.com/buefy/buefy/issues/1038 using v-model b-input component · Issue #1038 · buefy/buefy] | [https://github.com/buefy/buefy/issues/1038 using v-model b-input component · Issue #1038 · buefy/buefy] | ||
== Template == | == Template == | ||
506行目: | 512行目: | ||
</script> | </script> | ||
</syntaxhighlight>変数名が、外と衝突する恐れがあるので、thisを明示した方が安全に感じる。 | </syntaxhighlight>変数名が、外と衝突する恐れがあるので、thisを明示した方が安全に感じる。 | ||
2025年3月28日 (金) 13:23時点における版
Tool
Grunt
About
JavaScriptのタスクランナー。ビルドやファイルの整形、変換などを行ってくれる。
gruntの他にgulpというのもある。
Grunt: The JavaScript Task Runner
grunt vs. gulp
絶対つまずかないGulp 5入門 - インストールとSassを使うまでの手順 - ICS MEDIA
設定ファイルが違う。
- grunt: JSONで同期。
- gulp: JavaScriptで非同期。
Gruntfile
Gruntfile.jsかGruntfile.coffeがタスクの定義ファイル。
以下のコマンドでgruntで実行可能なタスク一覧が表示される。 grunt --help 単にgruntとだけ実行すると、defaultのタスクが実行される。基本はサブコマンドを指定する。
Node.js
Install
ここでプラットフォーム別のインストール方法がある。
Linuxの場合、以下。
- installs nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
- download and install Node.js (you may need to restart the terminal)
nvm install 22
- verifies the right Node.js version is in the environment
node -v # should print `v22.12.0`
- verifies the right npm version is in the environment
npm -v # should print `10.9.0` 最初の行を実行してインストールしたら~/.bashrcに以下の内容が追記される。即座にインストールを反映したければ現在の端末でも実行する。 export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
Framework
jQuery
レガシー
- jQueryはもう古い!?これから・・・ | YORIYORK|栃木県佐野市
- jQueryとは何なのか? なぜ使わなくても(あるいは使わないほうが)いいのか? #JavaScript - Qiita
- そろそろなぜjQueryを使うのが難しいのかをちゃんとまとめようと思う。|榊原昌彦
- jQueryを終了する時が来ましたか?
jQueryがもういらなくなってきたという意見がある。
- Webブラウザー間の違いがなくなってきた。
- 標準のJavaScriptでできることが増えた。
上記2点が大きい。jQueryじゃないとできないことが大幅に減った。標準APIでこなしたほうがいいだろう。
ajaxのasync/awaitでの書換
$.ajax(jQuery.ajax)の非同期処理をasync awaitの入れ子なし同期処理で記述する #JavaScript - Qiita
ajaxを使っている関数の定義にasync。ajax関数の使用箇所にawait。これでうまくいく。
- フルーツ名を取得する
- @param {string} fruitId
*/ async function getFruitName(fruitId) { const fruitRequest = {id: fruitId} const fruitResult = await ajaxGetFruit(fruitRequest); return fruitResult.name; }
/**
- フルーツのajax[GET]を実施する
- @param {object} request
- @returns
*/ function ajaxGetFruit(request) { return $.ajax({ url: '/fruit/name', type: "GET", async: true, contentType: "application/json", data: JSON.stringify(request), dataType: "json", }).then( function (result) { // 正常終了 resolve(result); }, function () { // エラー reject(); } ) } 「jQuery.ajax() | jQuery API Documentation」にあるように、jquery 1.5から$.ajaxの返却値はPromiseの派生クラス。そのまま返却させて良い。
DataTables
jqueryのプラグイン。テーブル処理をうまくやってくれる。
Option
Old
DataTables (table plug-in for jQuery)
v1.9以下の古いバージョンのサイトはこちらにある。
ServerSide
通常はクライアント画面のみ。全データを検索したい場合、serverSideオプションが必要。ただし、このオプションはdatatablesのv1.10以上じゃない。
Pagination
ページ表示件数に関するオプションがいくつかある。
- pageLength/iDisplayLength: 1ページあたりの表示件数。デフォルトは10。
- lengthMenu/aLengthMenu: リストボックスの項目。デフォルトは[10, 25, 50, 100]。-1 ("All") で全表示。
Nuxt.js
終端スラッシュありへのリダイレクト
Nuxt.jsにおける末尾スラッシュを統一する方法 #amplify - Qiita // middleware/trailingSlash.js /**
- 終端スラッシュありのURLにリダイレクトする。
- @param {Object} Nuxt.jsのContextオブジェクト。
*/ export default ({ route, redirect }) => { if (route.path.endsWith('/')) { return } let to = route.path + '/' if (route.query) { to += '?' + Object.entries(route.query) .map((e) => e.join('=')) .join('&') } to += route.hash redirect(301, to) }
//へのアクセスエラー
- https://github.com/vuejs/vue-router/issues/2593
- https://github.com/nuxt/nuxt.js/issues/2020
けっきょく、serverMiddlewareは結合環境で機能せず、middlewareもルートの変化後に発動するため対応できなかった。
そのため、プラグインにして、router.beforeEachで変化前に書き換え処理を入れた。 // plugins/redirect.js export default ({ app }) => { app.router.beforeEach((to, from, next) => { // 二重スラッシュを一重スラッシュに変換する。 // 素直にnextでリダイレクトすると、一度もともとのtoのURLでDOMの更新に進んでエラーが出る。 // そのためnext(false)でtoは使わずにpushで履歴を差し替える。 if (/\/\/+/.test(to.path)) { next(false) app.router.push(to.path.replace(/\/\/+/g, '/')) } else { next() } }) }
Vue.js
Naming
【Vue】単一ファイルコンポーネントの命名規則まとめ【ファイル名から記法まで】 #Vue.js - Qiita
radioのbool
vuejs2 - Vue: Binding radio to boolean - Stack Overflow
v-bind:か:で型を合わせて代入する。stringにするとうまく認識されない。
IMEの入力制限
IME以外はformatterで処理して、IMEだけcompositionendで処理すればいい。
<template> <div role="group" class="bv-no-focus-ring"> <div class="form-group-text" :class="{ active: '' + value, 'is-invalid': error }" > <b-form-input :id="'input-text-' + _uid" type="text" :formatter="formatter" :value="value" @input="$emit('input', $event)" @compositionend="$emit('input', normalize($event.target.value))" @paste="$emit('input', paste($event))" /> <label :for="'input-text-' + _uid">{{ /[._]/.test(label) ? $t(label) : label }}</label> </div> <div :id="'input-live-feedback-' + _uid" class="invalid-feedback"> {{ $t(error) }} </div> </div> </template> <script> export default { props: { value: { type: String, default: '', required: true }, label: { type: String, default: '', required: true }, // ファームウェアとスケジュールにエラーのないフォームがある。 error: { type: String, default: '' }, }, methods: { /** * 入力内容の正規化を行う。 * @param {string} input - 入力文字列。 * @return {string} 正規化後文字列。 */ normalize(input) { // ASCII文字列以外は削除する。 return input.replace(/[^ -~]/g, '') }, /** * 入力フォームを整形する。 * @param {string} value - 入力文字列。 * @param {Object} event - イベントオブジェクト。 * @return {string} IME変換時は正規化後の文字列。それ以外は、compositionendで行うため、未変換文字列。 * IME変換時はcompositionendで入力を正規化し、それ以外はformatterで正規化する。 */ formatter(value, event) { return event.isComposing ? value : this.normalize(value) }, /** * クリップボードからの貼付時の文字列を処理する。 * @param {Object} event - イベントオブジェクト。 * @return {string} 処理後の文字列。 * @todo 入力の途中にカーソルを移動させて貼り付けた場合、貼り付け後、カーソルが末尾に移動してしまう。 * event.preventDefault()が原因と思われる。しかし、これの解決が難しい。 * event.target.selectionStartにカーソル位置を指定できるのだが、preventDefaultすると一瞬カーソルが末尾に飛ぶ。 */ paste(event) { event.preventDefault() const cb = (event.clipboardData || window.clipboardData).getData('text') const start = event.target.selectionStart return this.normalize( this.value.slice(0, start) + cb + this.value.slice(start) ) }, }, } </script>
<template> <div role="group" class="bv-no-focus-ring mb-3"> <div class="form-group-text" :class="{ active: '' + value, 'is-invalid': error }" > <b-form-input :id="'input-number-' + _uid" class="form-control" type="tel" autocomplete="off" maxlength="10" :formatter="formatter" :value="value" @input="$emit('input', $event)" @compositionend="$emit('input', normalize($event.target.value))" /> <label :for="'input-number-' + _uid">{{ $t(label) }}</label> </div> <div :id="'input-live-feedback-' + _uid" class="invalid-feedback"> {{ $t(error) }} </div> </div> </template> <script> export default { props: { value: { type: [String, Number], default: '', required: true }, error: { type: String, default: '', required: true }, label: { type: String, default: '', required: true }, }, methods: { /** * 入力内容の正規化を行う。 * @param {string} value - 入力文字列。 * @return {string} result - 正規化後文字列。 * @warn v-model.numberの修飾子を指定するとcompositionendでの変更が反映されない。 * そのため、.numberを使わず、この関数で型変換する。 */ normalize(value) { const result = parseFloat(value.replace(/[^0-9]/g, '')) return isNaN(result) ? '' : result }, /** * 入力フォームを整形する。 * @param {string} value - 入力文字列。 * @param {Object} event - イベントオブジェクト。 * @return {string} IME変換時は正規化後の文字列。それ以外は、compositionendで行うため、未変換文字列。 * IME変換時はcompositionendで入力を正規化し、それ以外はformatterで正規化する。 */ formatter(value, event) { return event.isComposing ? value : this.normalize(value) }, }, } </script>
error '<template>' cannot be keyed. Place the key on real elements instead vue/no-template-key
vue.js - Custom elements in iteration require 'v-bind:key' directives - Stack Overflow
ハイライト
https://forum.vuejs.org/t/highlight-in-html-a-new-object-in-javascript-array/38877/9
nextTickでやればいいか。
【Vue.js】検索文字などの特定文字を、マーカーでハイライト表示するコンポーネントを作った | SAGA.TXT
検索キーワードで検索対象をsplitして、key/value形式で順番に配列で配置する。
カスタムコンポーネントのv-bind
javascript - How to use v-bind in a custom component? - Stack Overflow
using v-model b-input component · Issue #1038 · buefy/buefy
Template
Handlebars
テンプレートエンジン。.hbsという拡張子のファイルがHandlebarsのテンプレートファイル。JavaScriptの変数値を参照してHTMLを生成できる。
{{}}のマスタッシュ記法でJSの変数をHTML内で参照する。
例えば、PHPの配列などのデータを、jsonに変換して、HTMLに埋め込み表示したりするのに使うことがある。
他に有名なテンプレートエンジンに、Mustacheというのがあるが、Handlebars.jsはそれより、高速で上位互換とのこと。
Introduction
What is Handlebars?
以下のような式で、オブジェクトからHTMLを生成するテンプレートエンジン。 <p>{{firstname}} {{lastname}}</p> なお、この二重波括弧はマスタッシュ記法と読んだりする。これは、ヒゲに似ているからで、他にMustache.jsというテンプレートエンジンの名前から。
Language features
Simple expressions
<p>{{firstname}} {{lastname}}</p>
{ firstname: "Yehuda", lastname: "Katz", }
<p>Yehuda Katz</p> 上記のように、マスタッシュ記法がJavaScriptのオブジェクトに変換される。
Nested input objects
オブジェクトや配列もJavaScriptで参照できる。 <p>{{person.firstname}} {{person.lastname}}</p>
{ person: { firstname: "Yehuda", lastname: "Katz", }, }
Evaluation context
組み込みのブロックヘルパーのeachとwithは、コンテキストを評価できる。
withヘルパーはオブジェクトプロパティーのオブジェクトの前置を省略できる。 {{#with person}} {{firstname}} {{lastname}} {{/with}}
{ person: { firstname: "Yehuda", lastname: "Katz", }, } eachヘルパーは配列。 <ul class="people_list"> {{#each people}} <li>{{this}}</li> {{/each}} </ul>
{ people: [ "Yehuda Katz", "Alan Johnson", "Charles Jolley", ], }
<ul class="people_list"> <li>Yehuda Katz</li> <li>Alan Johnson</li> <li>Charles Jolley</li> </ul>
Template comments
handlebarsコードのコメントは、以下の2種類が可能。通常のHTMLコメントも問題ないが、HTMLをソースコードとして表示すると、コード上には表示される。 {{! }} {{!-- }} --}} コメント内に、}}を含めたい場合は、{{!-- --}}の記法を使う必要がある。
Block Helpers
Simple Iterators
ブロックヘルパーの主な用途は、カスタムイテレーターの定義時の使用だ。eachヘルパーの使用例は以下だ。
<div class="entry">
<h1>{{title}}</h1>
{{#with story}}
<div class="intro">{{{intro}}}</div>
<div class="body">{{{body}}}</div>
{{/with}}
</div>
<div class="comments">
{{#each comments}}
<div class="comment">
<h2>{{subject}}</h2>
{{{body}}}
</div>
{{/each}}
</div>
このケースでは、eatchにコメントの配列を渡している。
Handlebars.registerHelper("each", function(context, options) { var ret = "";
for (var i = 0, j = context.length; i < j; i++) { ret = ret + options.fn(context[i]); }
return ret; });
Built-in Helpers
#each
eachを使うことで、リストを反復できる。ブロック内で、thisを要素の参照に使える。
{ people: [ "Yehuda Katz", "Alan Johnson", "Charles Jolley", ], }
<ul class="people_list"> <li>Yehuda Katz</li> <li>Alan Johnson</li> <li>Charles Jolley</li> </ul> { {else}} を配列が空の場合の表示に使える。
オブジェクトの反復では、{ {@key}} で現在のキー名を参照できる。
なお、公式マニュアルに明記されていないが、each内で要素がオブジェクトの場合、this.を省略して、いきなりプロパティーにアクセスできる (https://chatgpt.com/c/67e50098-cbd4-800b-89f5-501b03f4d6d2)。
変数名が、外と衝突する恐れがあるので、thisを明示した方が安全に感じる。