フォームの送信の前後にカスタム処理を追加する方法です。追加のバリデーション、確認ダイアログ、送信データの加工、外部 API との非同期通信などに使います。CS-Cart 4.19.1 で確認した内容です。
フォームのイベント
| イベント名 | タイミング | 用途 |
|---|---|---|
ce.formpre_[FORM_NAME] |
標準のバリデーションの前 | 追加のチェック、確認、データの加工 |
ce.formpost_[FORM_NAME] |
標準のバリデーションを通過した後 | バリデーション通過後の処理 |
ce.formajaxpost_[FORM_NAME] |
AJAX 送信の後 | AJAX 送信後の処理 |
ハンドラーが false を返すと、送信はキャンセルされます。true を返すか、何も返さなければ、送信を続けます。同じイベントに複数のハンドラーを登録でき、すべて実行されます。1つでも false を返すと、送信はキャンセルされます。
[FORM_NAME] には、フォームの name 属性を入れます。name 属性のないフォームは、フックできません。
<form action="" method="post" name="product_update_form">
^^^^^^^^^^^^^^^^^^^
これがフォーム名。イベント名は ce.formpre_product_update_form になる基本の書き方
(function (_, $) {
'use strict';
// フォーム送信前のフック
$.ceEvent('on', 'ce.formpre_my_form', function (form, clicked_elm) {
// form: 送信されるフォーム(jQuery オブジェクト)
// clicked_elm: クリックされた要素(jQuery オブジェクト)
// false を返すと送信をキャンセルする
});
})(Tygh, Tygh.$);ハンドラーの引数から、次のような情報を取り出せます。
$.ceEvent('on', 'ce.formpre_product_update_form', function (form, clicked_elm) {
var formName = form.attr('name'); // フォーム名
var formAction = form.attr('action'); // 送信先の URL
var formData = form.serialize(); // フォームのデータ
var buttonName = clicked_elm.attr('name'); // ボタンの name
var buttonValue = clicked_elm.val(); // ボタンの値
var dispatch = clicked_elm.data('ca-dispatch'); // dispatch の値
});使用例
送信前に確認ダイアログを出す
$.ceEvent('on', 'ce.formpre_order_form', function (form, clicked_elm) {
// 特定のボタンのときだけ確認する
if (clicked_elm.attr('name') === 'dispatch[orders.delete]') {
if (!confirm('本当に削除しますか?')) {
return false; // キャンセル
}
}
});追加のバリデーションをする
$.ceEvent('on', 'ce.formpre_product_update_form', function (form, clicked_elm) {
var codeA = form.find('#elm_code_a').val();
var codeB = form.find('#elm_code_b').val();
if (codeA && codeB && !validateRelation(codeA, codeB)) {
$.ceNotification('show', {
type: 'E',
title: 'エラー',
message: '2つのコードの関係が不正です'
});
return false; // 送信をキャンセル
}
});送信前にデータを加工する
$.ceEvent('on', 'ce.formpre_my_form', function (form, clicked_elm) {
// カンマを取り除く
var $priceField = form.find('#elm_price');
$priceField.val($priceField.val().replace(/,/g, ''));
// hidden フィールドを動的に追加する
form.append('<input type="hidden" name="processed" value="1" />');
});複数のフォームに同じ処理を適用する
['form_a', 'form_b', 'form_c'].forEach(function (formName) {
$.ceEvent('on', 'ce.formpre_' + formName, function (form, clicked_elm) {
return commonValidation(form);
});
});ce.formpre_ と ce.formpost_ の使い分け
フォームを送信する前に、外部の API と非同期で通信し、取得したトークンをフォームに入れて再送信する場合を考えます。たとえば、決済サービスのカードトークンを取得する場合です。
ce.formpre_ を使うと、次の問題が起きました。
- 標準のバリデーションでエラーがあっても、外部の API が呼ばれる。
$.ceFormValidator('check', $form)がundefinedを返し、cm-check-expiryのような独自のバリデーターのエラーを検知できない。
ce.formpre_ はバリデーションの前に発火するため、この時点では検証が終わっていません。バリデーションの結果が必要な処理は、ce.formpost_ を使います。
悪い例:ce.formpre_ を使う
// バリデーションの前に発火するため、エラーがあっても API が呼ばれる
$.ceEvent('on', 'ce.formpre_' + formName, function (form, elm) {
// $.ceFormValidator('check', $form) は undefined を返す
});良い例:ce.formpost_ を使う
// バリデーションを通過した後に発火する
$.ceEvent('on', 'ce.formpost_' + formName, function (form) {
// トークンがあれば、そのまま送信する
if ($('#token').val()) {
return true;
}
// バリデーションを再確認する(form オブジェクトを直接使う)
var isFormValid = form.ceFormValidator('check');
if (isFormValid) {
// 非同期で API を呼び出す(externalApi は、外部のサービスの SDK を想定した例)
var data = form.serialize();
externalApi.getToken(data, function (token) {
$('#token').val(token);
form[0].submit(); // フォームを再送信する
});
}
return false; // 最初の送信はキャンセルする
});実装のポイントは、次の3つです。
formパラメーターを直接使います。$(form)にはしません。form.ceFormValidator('check')で、バリデーションを再確認します。- トークンを取得したあとは、
form[0].submit()でネイティブの送信をします。
任意のタイミングでバリデーションを実行して送信する
通常は、フォームに cm-ajax などの適切なクラスを付けるだけで、バリデーションが自動で実行されます。独自のボタンから、バリデーションを実行して送信したい場合は、$.ceFormValidator('check', $form) を使います。
$(document).on('click', '#my_submit_button', function (e) {
e.preventDefault();
var $form = $('#my_form');
// true: すべてのチェックが OK、false: エラーあり
if ($.ceFormValidator('check', $form)) {
$form.submit();
}
// エラーがあれば、自動でエラーが表示される
});AJAX で送信する場合は、$.ceAjax('request', ...) を使います。
if ($.ceFormValidator('check', $form)) {
$.ceAjax('request', $form.attr('action'), {
method: 'post',
data: $form.serialize(),
callback: function (data) {
if (data.success) {
$.ceNotification('show', {
type: 'N',
message: '保存しました'
});
}
}
});
}ceFormValidator のメソッド
| メソッド | 説明 |
|---|---|
$.ceFormValidator('check', $form) |
バリデーションを実行し、結果を真偽値で返します。 |
$.ceFormValidator('showErrors', $form, errors) |
エラーを手動で表示します。 |
$.ceFormValidator('hideErrors', $form) |
エラーの表示を消します。 |
$.ceFormValidator('registerValidator', {...}) |
独自のバリデーターを登録します。 |
JavaScript で言語変数(翻訳)を使う
1. テンプレートで翻訳キーを登録する
hooks/index/scripts.post.tpl
<script>
(function(_, $) {
_.tr({
"my_addon.message_key":
"{__("my_addon.message_key")|escape:"javascript"}",
"my_addon.validation_error":
"{__("my_addon.validation_error")|escape:"javascript"}"
});
}(Tygh, Tygh.$));
</script>
{script src="js/addons/my_addon/func.js"}{__("キー")}で、Smarty から翻訳を取得します。|escape:"javascript"で、JavaScript 用にエスケープします。必須です。_.tr({...})で、Tyghオブジェクトに登録します。
2. JavaScript で翻訳を取得する
(function (_, $) {
'use strict';
var message = _.tr('my_addon.message_key');
$.ceNotification('show', {
type: 'W',
message: _.tr('my_addon.warning_message')
});
})(Tygh, Tygh.$);3. 翻訳ファイル
# var/langs/ja/addons/my_addon.po
msgctxt "Languages::my_addon.message_key"
msgid "English message"
msgstr "日本語のメッセージ"CS-Cart の JavaScript イベントの一覧
| イベント | 説明 |
|---|---|
ce.formpre_[FORM_NAME] |
フォームの標準のバリデーションの前 |
ce.formpost_[FORM_NAME] |
フォームの標準のバリデーションの通過後 |
ce.formajaxpost_[FORM_NAME] |
AJAX 送信の後 |
ce.form_confirm |
確認ダイアログの表示前 |
ce.commoninit |
共通の初期化 |
ce.ajaxdone |
AJAX の完了後 |
ce.ajax_callback_[NAME] |
特定の AJAX コールバック |
ce.dialogshow |
ダイアログの表示後 |
ce.notificationshow |
通知の表示後 |
ce.loadershow |
ローダーの表示時 |
ce.scrolltoelm |
要素へのスクロール時 |
ce.switch_[ID] |
スイッチの切り替え時 |
ce.picker_js_action_[TARGET_ID] |
ピッカーのアクション時 |
ce.colorpicker.show |
カラーピッカーの表示時 |
ce.colorpicker.hide |
カラーピッカーの非表示時 |
参考
- CS-Cart 公式ドキュメント:Microformats(英語)
- Javascript hooks used in CS-Cart 4.x(CS-Cart 公式フォーラム、英語)