ビューヘルパーを作成する
次のように、アプリケーションの中に、「 helpers/viewhelper.js 」というディレクトリとファイルを作成します。
- example
- ...
- helpers
- viewhelper.js
- ...
- app.js
ファイルの内容
helpers/viewhelper.js
module.exports = {
hello: () => {
return "hello, World";
}
}
「 var app = express(); 」より後に、以下を追加します。
app.js
app.locals.viewhelper = require('./helpers/viewhelper');
ビューヘルパーの呼び出し
テンプレート上で次のように記述すると、ビューヘルパーが呼び出せます。
<%= viewhelper.hello() %>
自作クラス
以下は、フォームタグの自作のビューヘルパーのクラスです。
helpers/FormHelpers.js
class FormHelpers {
_buildAttributes(attributes) {
return Object.entries(attributes)
.filter(([_, value]) => value !== false && value != null)
.map(([key, value]) => {
if (value === true) return key;
return `${key}="${this._escapeHtml(value)}"`; // ← ここでもエスケープすべき
})
.join(' ');
}
_normalizeClass(attr = '') {
return attr.split(' ').filter(Boolean).join(' ');
}
_escapeHtml(str) {
return String(str ?? '').replace(/[&<>"']/g, s => ({
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
}[s]));
}
textInput(name, attributes = {}) {
const {
value = '',
class: classAttr = '',
...restAttributes
} = attributes;
restAttributes.class = this._normalizeClass(classAttr);
return `<input type="text" name="${this._escapeHtml(name)}" value="${this._escapeHtml(value)}" ${this._buildAttributes(restAttributes)}>`;
}
passwordInput(name, attributes = {}) {
const {
value = '',
class: classAttr = '',
...restAttributes
} = attributes;
restAttributes.class = this._normalizeClass(classAttr);
return `<input type="password" name="${this._escapeHtml(name)}" value="${this._escapeHtml(value)}" ${this._buildAttributes(attributes)}>`;
}
textArea(name, attributes = {}) {
const {
value = '',
class: classAttr = '',
...restAttributes
} = attributes;
restAttributes.class = this._normalizeClass(classAttr);
return `<textarea name="${this._escapeHtml(name)}" ${this._buildAttributes(restAttributes)}>${this._escapeHtml(value)}</textarea>`;
}
hiddenInput(name, attributes = {}) {
const {
value = '',
...restAttributes
} = attributes;
return `<input type="hidden" name="${this._escapeHtml(name)}" value="${this._escapeHtml(value)}" ${this._buildAttributes(restAttributes)}>`;
}
checkBox(name, attributes = {}) {
const {
value = '',
class: classAttr = '',
checked = false,
...restAttributes
} = attributes;
restAttributes.class = this._normalizeClass(classAttr);
const checkedAttr = checked === true ? 'checked' : '';
return `<input type="checkbox" name="${this._escapeHtml(name)}" value="${this._escapeHtml(value)}" ${this._buildAttributes(restAttributes)} ${checkedAttr}>`;
}
radioButton(name, attributes = {}) {
const {
value = '',
class: classAttr = '',
checked = false,
...restAttributes
} = attributes;
restAttributes.class = this._normalizeClass(classAttr);
const checkedAttr = checked === true ? 'checked' : '';
return `<input type="radio" name="${this._escapeHtml(name)}" value="${this._escapeHtml(value)}" ${this._buildAttributes(restAttributes)} ${checkedAttr}>`;
}
selectBox(name, optionsArray = [], attributes = {}) {
const {
class: classAttr = '',
value: selectedValue,
placeholder = '--選択してください--',
includePlaceholder = true, // ← 明示的に出すかどうか切り替えられる
...restAttributes
} = attributes;
restAttributes.class = this._normalizeClass(classAttr);
if (!Array.isArray(optionsArray)) {
throw new TypeError('optionsArray must be an array');
}
const optionsHtml = [
// プレースホルダーの option(先頭に追加)
includePlaceholder ? `<option value="">${this._escapeHtml(placeholder)}</option>` : null,
// その他の option
...optionsArray.map(option => {
const isSelected = selectedValue !== undefined
? String(option.value) === String(selectedValue)
: option.selected === true;
const selectedAttr = isSelected ? 'selected' : '';
return `<option value="${this._escapeHtml(option.value)}" ${selectedAttr}>${this._escapeHtml(option.text)}</option>`;
})
].filter(Boolean).join('');
return `<select name="${this._escapeHtml(name)}" ${this._buildAttributes(restAttributes)}>${optionsHtml}</select>`;
}
label(forId, text, attributes = {}) {
return `<label for="${this._escapeHtml(forId)}" ${this._buildAttributes(attributes)}>${this._escapeHtml(text)}</label>`;
}
}
module.exports = FormHelpers;
すべてのルーターで利用するために、app.jsに以下の設定を追加します。
app.js
const FormHelpers = require('./helpers/FormHelpers');
app.use((req, res, next) => {
res.locals.form = new FormHelpers();
next()
});
ejs ファイル上で、ヘルパー関数を使うには、以下のようにします。
<div>
<p><%- form.textInput('username', { class: 'form-control' }) %></p>
<p><%- form.passwordInput('password', { id: 'password', class: 'form-control' }) %></p>
<p><%- form.textArea('textarea', { class: 'form-control textare', value: "hoge" }) %></p>
<p><%- form.hiddenInput('hidden_input', { id: 'hidden_input', rows: 5, class: 'form-control' }) %></p>
<p><%- form.checkBox('role', { id: 'role', class: 'form-check-input', checked: true, value: "admin" }) %></p>
<p><%- form.radioButton('gender', { id: 'gender_male', class: 'form-check-input', value: 'maile', checked: true }) %></p>
<p><%- form.radioButton('gender', { id: 'gender_female', class: 'form-check-input', value: 'female' }) %></p>
<p>
<%- form.selectBox('country', [
{ value: '', text: '選択してください' },
{ value: 'JP', text: '日本' },
{ value: 'US', text: 'アメリカ', selected: true },
{ value: 'CA', text: 'カナダ' }
], { id: 'country', class: 'form-control' }) %>
</p>
</div>