💻
knockout.js の observableArray でクライアントサイド検証を有効にする
ko.observableArray
でバインドしたフォーム要素にバリデーションを入れたかったのですが、普通にやっただけではうまく動いてくれないようです。knockout.js のプラグインには Knockout Validation というのもあるようなのですが、以下の理由で断念してしまいました。
- そもそも
ko.observableArray
に対応してない (それぞれの要素にko.observable
する必要がある) - jquery.validate.unobtrusive.js に対応してない (致命的)
「誰も作らないなら自分で作るしかない」ということで、knockout.js のカスタムバインディングを作ってみました。
$(function () {
ko.bindingHandlers.validate = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (valueAccessor()) {
$.validator.unobtrusive.parse(element);
}
}
};
});
$.validator.unobtrusive.parse
メソッドに要素を指定すると、動的に追加したフォームの検証を有効にしてくれるらしいです。あとは、バインドしてあげれば完成です。
@{
ViewBag.Title = "ホーム";
Html.EnableClientValidation();
Html.EnableUnobtrusiveJavaScript();
}
<form action="/" method="post">
<input type="button" value="追加" data-bind="click: add" />
<input type="button" value="送信" data-bind="click: submit" />
</form>
<fieldset id="Items" data-bind="foreach: items">
@using (Html.BeginForm("Index", null, FormMethod.Post,
new Dictionary<string, object>() { { "data-bind", "validate: true" } })) {
@Html.ValidationSummary()
<table>
<tbody>
<tr>
<td class="editor-label">
<span>名前:</span>
</td>
<td class="editor-field">
<input id="Name" name="Name" type="text" data-bind="value: name" data-val="true" data-val-required="名前は必須です。" />
</td>
<td class="editor-label">
<span>年齢:</span>
</td>
<td class="editor-field">
<input id="Age" name="Age" type="text" data-bind="value: age" data-val="true" data-val-number="年齢は数値です。" />
</td>
</tr>
</tbody>
</table>
}
</fieldset>
<script type="text/javascript">
function ViewModel() {
this.items = ko.observableArray([]);
this.add = function () {
this.items.push({ name: null, age: null });
};
this.submit = function () {
var valid = Enumerable
.From($("#Items").find("form"))
.Select(function (form) { return $(form).valid() })
.All(function (result) { return result });
};
};
ko.applyBindings(new ViewModel());
$(function () {
ko.bindingHandlers.validate = {
update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (valueAccessor()) {
$.validator.unobtrusive.parse(element);
}
}
};
});
</script>
Discussion