Выполните ненавязчивую проверку формы JQuery без отправки формы

У меня есть форма, которая представляет элемент. Форма содержит кнопку отправки. Если нажата кнопка "Отправить", должна появиться ненавязчивая проверка этих полей.

Если проверка не пройдена, больше ничего не должно происходить.

Если проверка прошла успешно, элемент должен быть добавлен в коллекцию Knockout.js наблюдаемый массив.

В обоих случаях весь процесс должен оставаться на стороне клиента без отправки на сервер. Отправка и проверка на стороне сервера будут проходить на более поздней стадии процесса.

Как мне достичь желаемого эффекта?

Я использую ASP.Net MVC с аннотациями данных. Я предпочитаю не дублировать проверочную логику вручную на стороне клиента.

Я должен также упомянуть, что у меня есть несколько форм на одной странице.

Вот что я сделал так далеко...

Вот мой файл макета ASP.Net MVC:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - JC Guns Online</title>


    @*---------- Stylesheets ----------*@

    @Styles.Render("~/Content/Bootstrap/bootstrap-theme.css")
    @Styles.Render("~/Content/MightyIT/bootstrap_customizations.css")
    @Styles.Render("~/Content/site.css")
    @Styles.Render("~/Content/MightyIT/custom_styles.css")
    @Styles.Render("~/Content/MightyIT/callout.css") 
    @Styles.Render("~/Content/font-awesome-4.0.3/css/font-awesome.min.css")
    @RenderSection("css", required: false)


</head>
<body>

    <div class="navbar navbar-default navbar-fixed-top">
        <div class="container">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
        </div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">

                <li>@Html.ActionLink("Home", "Index", "Home")</li>
                <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                @*<li>
                    @using (Html.BeginForm())
                    {
                        <input id="txtQuickSearch" type="text" class="form-control col-lg-8" placeholder="Search">
                        <img src="~/Content/img/search_32.png" />
                    }
                </li>*@
            </ul>
            @Html.Partial("_LoginPartial")
        </div>
    </div>
    <div class="container body-content">
        <br />
        @RenderBody()

        <br /><br />
        <nav class="navbar navbar-default navbar-fixed-bottom">
            <div style="text-align:center">
                <img src="~/Content/img/logo_small.png" class="img-responsive" />
                <sub style="position:absolute; right:10px; bottom:10px;">&copy; @DateTime.Now.Year </sub>
            </div>
        </nav>
    </div>

    @*---------- Javascripts ----------*@

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/modernizr")
    @Scripts.Render("~/bundles/bootstrap")
    @Scripts.Render("~/Scripts/KnockOut/knockout-3.0.0.js")
    @Scripts.Render("~/Scripts/JQuery/jquery.unobtrusive-ajax.js")
    @Scripts.Render("~/Scripts/JQuery/jquery.validate.js")
    @Scripts.Render("~/Scripts/JQuery/jquery.validate.unobtrusive.js")
    @Scripts.Render("~/Scripts/JQuery/jquery.callout.unobtrusive.js")   
    @Scripts.Render("~/Scripts/MVCFoolProof/mvcfoolproof.unobtrusive.js")    
    @RenderSection("scripts",false)
</body>
</html>

Вот код для соответствующей части, над которой я сейчас работаю (есть несколько похожих частей, которые будут размещены на той же странице):

<form id="AddCrimeForm">
    <div class="panel panel-success">
        <div class="panel-heading">
            <div class="form-horizontal">
                <div class="row">
                    <div class="col-lg-11">Add a crime incident to the list</div>
                    <div class="col-lg-1">
                        <button type="submit" class="btn btn-success btn-xs" onclick="addCrime();"><i class="fa fa-plus"></i> Add</button>
                    </div>
                </div>
            </div>
        </div>

        <div class="panel-body">
            <div class="form-horizontal">
                <div class="row">
                    <div class="col-lg-6">
                        <input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Id" name="Id" type="hidden" value="">
                        <div class="form-group">
                            <label class="control-label col-md-4" for="CaseNumber">Case Number</label>
                            <div class="col-md-8">
                                <input class="form-control text-box single-line" data-val="true" data-val-required="The Case Number field is required." id="CaseNumber" name="CaseNumber" type="text" value="">
                                <span class="field-validation-valid" data-valmsg-for="CaseNumber" data-valmsg-replace="true"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="control-label col-md-4" for="DateOfIncident">Date Of Incident</label>
                            <div class="col-md-8">
                                <input class="form-control text-box single-line valid" data-val="true" data-val-required="The Date of Incident field is required." id="DateOfIncident" name="DateOfIncident" type="date" value="">
                                <span class="field-validation-valid" data-valmsg-for="DateOfIncident" data-valmsg-replace="true"></span>
                            </div>
                        </div>
                    </div>
                    <div class="col-lg-6">
                        <div class="form-group">
                            <label class="control-label col-md-4" for="Description">Description</label>
                            <div class="col-md-8">
                                <textarea class="form-control text-box multi-line" data-val="true" data-val-required="The Description field is required." id="Description" name="Description"></textarea>
                                <span class="field-validation-valid" data-valmsg-for="Description" data-valmsg-replace="true"></span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</form>

<table class="table table-striped table-hover " id="CrimeList">
    <thead>
        <tr>
            <th>Case Number</th>
            <th>Date of Incident</th>
            <th>Description</th>
            <th></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: items">
        <tr>
            <td data-bind="text: $data.CaseNumber">Column content</td>
            <td data-bind="text: $data.DateOfIncident">Column content</td>
            <td data-bind="text: $data.Description" style="text-wrap: normal">Column content</td>
            @*<td></td>
                <td></td>
                <td></td>*@
            <td>...</td>
        </tr>
    </tbody>
</table>

А вот код для client_crime_kjs.js со всем моим кодом модели представления KnouckoutJS:

$(document).ready(
    function ()
    {

        var Crime = function(CaseNumber, DateOfIncident, Description)
        {
            this.CaseNumber = CaseNumber;
            this.DateOfIncident = DateOfIncident;
            this.Description = Description;
        }

        var initialData = new Array();

        var crimes = function (items)
        {
            var self = this;
            //Data
            self.items = ko.observableArray(items)

            //operations
            self.addCrime = function()
            {
                if ($("#AddCrimeForm").valid()) {
                    self.crime = new Crime($("#CaseNumber").val(), $("#DateOfIncident").val(), $("#Description").val());
                    //var JSONObj = { CaseNumber: $("#CaseNumber").val(), DateOfIncident: $("#DateOfIncident").val(), Description: $("#Description").val() };
                    self.items.push(this.crime);
                }

                //$("#CaseNumber").val() = "";
                //$("#DateOfIncident").val() = "";
                //$("#Description").val() = "";

            }

        }

        ko.applyBindings(crimes(initialData), $("#CrimeList")[0])
    }
);

В основном, на этом этапе происходит следующее: когда поля являются недействительными, форма не отправляется (правильно), но когда она действительно проверяется, она отправляет (вопреки моему требованию), и мой KO observablearray впоследствии сбрасывается.

2 ответа

Решение

Вот и я получил ответ на поставленный выше вопрос. Хитрость заключается в том, чтобы установить кнопку type = "button" вместо "submit".

Итак, для всех, кто борется с этим, вот пример того, как заставить его работать...

Ваш нокаут ViewModel:

$(document).ready(
    function () {

        var Crime = function (CaseNumber, DateOfIncident, Description) {
            this.CaseNumber = CaseNumber;
            this.DateOfIncident = DateOfIncident;
            this.Description = Description;
        }

        var crimes = function (items) {
            var self = this;
            //Data
            self.items = ko.observableArray(items)

            //operations
            self.addCrime = function () {
                if ($("#AddCrimeForm").valid()) {
                    self.crime = new Crime($("#CaseNumber").val(), $("#DateOfIncident").val(), $("#Description").val());
                    //var JSONObj = { CaseNumber: $("#CaseNumber").val(), DateOfIncident: $("#DateOfIncident").val(), Description: $("#Description").val() };
                    self.items.push(this.crime);

                    $("#CaseNumber").val("");
                    $("#DateOfIncident").val("");
                    $("#Description").val("");
                }
            }

            self.removeCrime = function (item) {
                self.items().remove(item);
            }

        }

        var initialData = new Array();
        ko.applyBindings(crimes(initialData), $("#CrimeList")[0])
    }
);

И вот соответствующий HTML:

<form id="AddCrimeForm">
    <div class="panel panel-success">
        <div class="panel-heading">
            <div class="form-horizontal">
                <div class="row">
                    <div class="col-lg-11">Add a crime incident to the list</div>
                    <div class="col-lg-1">
                        <button type="button" class="btn btn-success btn-xs" onclick="addCrime();"><i class="fa fa-plus"></i> Add</button>
                    </div>
                </div>
            </div>
        </div>

        <div class="panel-body">
            <div class="form-horizontal">
                <div class="row">
                    <div class="col-lg-6">
                        <input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Id" name="Id" type="hidden" value="">
                        <div class="form-group">
                            <label class="control-label col-md-4" for="CaseNumber">Case Number</label>
                            <div class="col-md-8">
                                <input class="form-control text-box single-line" data-val="true" data-val-required="The Case Number field is required." id="CaseNumber" name="CaseNumber" type="text" value="">
                                <span class="field-validation-valid" data-valmsg-for="CaseNumber" data-valmsg-replace="true"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label class="control-label col-md-4" for="DateOfIncident">Date Of Incident</label>
                            <div class="col-md-8">
                                <input class="form-control text-box single-line valid" data-val="true" data-val-required="The Date of Incident field is required." id="DateOfIncident" name="DateOfIncident" type="date" value="">
                                <span class="field-validation-valid" data-valmsg-for="DateOfIncident" data-valmsg-replace="true"></span>
                            </div>
                        </div>
                    </div>
                    <div class="col-lg-6">
                        <div class="form-group">
                            <label class="control-label col-md-4" for="Description">Description</label>
                            <div class="col-md-8">
                                <textarea class="form-control text-box multi-line" data-val="true" data-val-required="The Description field is required." id="Description" name="Description"></textarea>
                                <span class="field-validation-valid" data-valmsg-for="Description" data-valmsg-replace="true"></span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</form>

<table class="table table-striped table-hover " id="CrimeList">
    <thead>
        <tr>
            <th>Case Number</th>
            <th>Date of Incident</th>
            <th>Description</th>
            <th></th>
        </tr>
    </thead>
    <tbody data-bind="foreach: items">
        <tr>
            <td data-bind="text: $data.CaseNumber">Column content</td>
            <td data-bind="text: $data.DateOfIncident">Column content</td>
            <td data-bind="text: $data.Description" style="text-wrap: normal">Column content</td>
            @*<td></td>
                <td></td>
                <td></td>*@
            <td>...</td>
        </tr>
    </tbody>
</table>

Еще раз - обратите внимание, что тип кнопки "Добавить" был установлен на "кнопку", а НЕ "отправить".

Надеюсь, что это поможет остальной части всего, что вы пишите!

Я в этот момент пытаюсь сделать что-то подобное.

моя идея в начале была такой же, как и у вас, но затем я внес некоторые изменения, потому что сложно выполнить сложную проверку с аннотациями данных. Под сложным я подразумеваю, что запись не повторяется в базе данных, или пользовательский формат на входе.

Так что я пошел на FluentValidation, проблема в том, что беглая проверка не всегда работает в ненавязчивой проверке. Так что я сейчас делаю это.

надеюсь, это поможет вам, и если вы найдете что-то, что может быть лучше, пожалуйста, дайте мне знать:

моя модель с ее проверкой:

    public class BUCashFlow
{
    public int Id { get; set; }
    [Display(Name = "Concepto")]
    public string Text { get; set; }
    [Display(Name = "Valor")]
    public double Value { get; set; }
    public CashFlowType CashFlowType { get; set; }
    [Display(Name = "Cuenta")]
    public int AccountId { get; set; }
    public string User { get; set; }

    public virtual Account Account { get; set; }
}

public class BuCashFlowVal : AbstractValidator<BUCashFlow>
{
    public BuCashFlowVal()
    {

        RuleFor(p => p.Text)
            .NotEmpty().WithMessage(ValHelper.Messages.Required);
        RuleFor(p => p.Value)
            .NotEmpty().WithMessage(ValHelper.Messages.Required);
        RuleFor(p => p.AccountId)
            .NotEmpty().WithMessage(ValHelper.Messages.Required);
    }
}

Я тоже использую web api, так что вот мой контроллер web api, где я проверяю свою новую модель BUCashFlow

        // POST api/BUCashFlows
    [ResponseType(typeof(BUCashFlow))]
    public IHttpActionResult PostBUCashFlow(BUCashFlow bucashflow)
    {
        ValidationResult ValRes = new BuCashFlowVal().Validate(bucashflow);
        if (!ValRes.IsValid)
        {
            return BadRequest(ValRes.Errors[0].ErrorMessage);
        }
        bucashflow.User = User.Identity.GetUserId();
        bucashflow.CashFlowType=CashFlowType.Purchase;
        db.BuCashFlows.Add(bucashflow);
        db.SaveChanges();

        return CreatedAtRoute("DefaultApi", new { id = bucashflow.Id }, bucashflow);
    }

наконец, чтобы отобразить мои ошибки в JS / KO я делаю это:

            self.addExpense = function(selector) {
            $.ajax({
                type: 'POST',
                url: '@ViewBag.ApiBUExpenses',
                data: $(selector).serialize()
            }).done(function(o) {
                self.expenses.push(new ExpenseVM(self, o.Id, o.Text, o.Value, o.AccountId));
            }).fail(function (o) {
                $(selector).find('.val').html(  '<div class="alert alert-warning alert-dismissable">' +
                                                    '<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' +
                                                    '<strong>Warning!</strong> ' + o.responseJSON.Message +
                                                '</div>');
            });
        };

и моя HTML-форма, которая была сериализована в этом вызове ajax:

            <form id="new-expense-form" data-bind="submit: addExpense">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                <h4 class="modal-title" id="myModalLabel">Agregar Nuevo Gasto</h4>
            </div>
            <div class="modal-body">
                <div class="form-group">
                    <label class="control-label">Concepto</label>
                    <input name="Text" type="text" class="form-control" />
                </div>
                <div class="form-group">
                    <label class="control-label">Valor</label>
                    <input name="Value" type="text" class="form-control" />
                </div>
                <div class="form-group">
                    <label class="control-label">Cuenta</label>
                    <i data-bind="visible: isLoadingAccounts" class="fa fa-refresh fa-spin pull-right"></i>
                    <select name="AccountId" class="form-control" data-bind="options: accounts, optionsText: 'name', optionsValue: 'Id', optionsCaption: 'Cuenta'"></select>
                </div>
                <div class="val"></div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-default-pnl btn-circle-m" title="Guardar Gasto">
                    <i class="fa fa-check"></i>
                </button>
            </div>
        </form>

Что вы думаете об этом подходе?

Если FoolProof работает как предполагалось

спасибо, что поделились этой библиотекой, теперь, если это работает, вам просто нужно изменить вызов ajax следующим образом:

self.addExpense = function(selector) {
    $(selector).validate()
    if ($(selector).valid()) {
            $.ajax({
                type: 'POST',
                url: '@ViewBag.ApiBUExpenses',
                data: $(selector).serialize()
            }).done(function(o) {
                self.expenses.push(new ExpenseVM(self, o.Id, o.Text, o.Value, o.AccountId));
            })
    }
};    

Обновить

Я видел вашу библиотеку, и я думаю, что она не будет работать, если это не так, вы всегда можете продублировать свою проверку и использовать KnockoutValidation

Другие вопросы по тегам