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>