読者です 読者をやめる 読者になる 読者になる

からめもぶろぐ。

ワタシ SharePoint チョット デキル

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>

実行してみます。

f:id:karamem0:20160624200956p:plain

できました!