【问题标题】:Ajax post in MVC 3 with multiple-form ViewMVC 3 中的 Ajax 帖子与多表单视图
【发布时间】:2012-04-03 19:05:35
【问题描述】:

我删除了我之前处理该主题的问题,因为上下文发生了一些变化并且问题是新的。

我现在只有一个视图,只有一个模型。该视图有 2 种形式。每个表单提交自己的操作,每个操作使用模型数据的子集,使用数据执行搜索。然后将搜索结果添加到模型的属性中,并需要将其呈现回视图。我可以通过一个标准的 html 帖子来完成这一切,但现在这需要是一个 ajax 帖子。

这是我的 ViewModel:

public sealed class SearchUsersViewModel {

    [Display(Name = "Last Name")]
    public string LastName { get; set; }

    [Display(Name = "Username")]
    public string Username { get; set; }

    [Display(Name = "Email Address")]
    public string EmailAddress { get; set; }

    [Display(Name = "Entity Type")]
    public byte EntityTypeID { get; set; }

    [Display(Name = "Search Name")]
    public string SearchField { get; set; }

    public IEnumerable<SelectListDTO> EntityTypes { get; set; }

    public IEnumerable<UserEditViewModel> Users { get; set; }

    [Display(Name = "Total Rows")]
    public int TotalRowCount { get; internal set; }

    public SearchUsersViewModel() {
        EntityTypes = LookupEntityTypeService.Instance.SelectList;
        Users = new List<UserEditViewModel>();
    }
}

这是我的观点:

@model SearchUsersViewModel

<div>
    <div class="SearchByUserDataTable">
        @using (Html.BeginForm("FilterByUserData", "Admin", FormMethod.Post, new {model = Model})) {
            @Html.ValidationSummary(true)
            <table cellpadding="0" cellspacing="0" border="0">
                <tr style="height: 30px;">
                    <td class="Header01">
                        User Search:
                    </td>
                </tr>
            </table>
            <table cellpadding="0" cellspacing="0" border="0">
                <tr style="height: 20px;">
                    <td class="Header02">
                        Search By User Information:
                    </td>
                </tr>
            </table>
            <table cellpadding="0" cellspacing="0" border="0">
                <tr style="height: 1px;">
                    <td class="ContentDividerHoriz_425"></td>
                </tr>
            </table>
            <table id="searchByUserDataTable" cellpadding="0" cellspacing="0" border="0">
                <tr style="height: 26px;">
                    <td class="leftColumn">
                        @Html.LabelFor(model => model.LastName):
                    </td>
                    <td class="rightColumn">
                        @Html.TextBoxFor(model => model.LastName, new { id = "lastNameSearchField", @class = "TextField_220" })
                    </td>
                </tr>
                <tr style="height: 26px;">
                    <td class="leftColumn">
                        @Html.LabelFor(model => model.Username):
                    </td>
                    <td class="rightColumn">
                        @Html.TextBoxFor(model => model.Username, new { id = "userNameSearchField", @class = "TextField_220" })
                    </td>
                </tr>
                <tr style="height: 26px;">
                    <td class="leftColumn">
                        @Html.LabelFor(model => model.EmailAddress):
                    </td>
                    <td class="rightColumn">
                        @Html.TextBoxFor(model => model.EmailAddress, new { id = "emailAddressSearchField", @class = "TextField_220" })
                    </td>
                </tr>
            </table>
            <table cellpadding="0" cellspacing="0" border="0">
                <tr>
                    <td id="filterByUserError" style="width: 375px;"></td>
                    <td align="right" style="width: 50px; padding-right: 75px;">
                        <div>
                            <input id="filterByUserButton" type="submit" value="Search" />
                        </div>
                    </td>
                </tr>
            </table>
        }
    </div>

    <div class="SearchByEntityDataTable">
        @using (Html.BeginForm("FilterByEntityData", "Admin", FormMethod.Post, new { model = Model })) { 
            <table cellpadding="0" cellspacing="0" border="0">
                <tr style="height: 28px;">
                    <td style="width: 425px;"></td>
                </tr>
            </table>
            <table cellpadding="0" cellspacing="0" border="0">
                <tr style="height: 20px;">
                    <td class="Header02">
                        Search By Entity Information:
                    </td>
                </tr>
            </table>
            <table cellpadding="0" cellspacing="0" border="0">
                <tr style="height: 1px;">
                    <td class="ContentDividerHoriz_425"></td>
                </tr>
            </table>
            <table id="searchByEntityDataTable" cellpadding="0" cellspacing="0" border="0">
                <tr style="height: 26px;">
                    <td class="leftColumn">
                        @Html.LabelFor(model => model.EntityTypeID):
                    </td>
                    <td class="rightColumn">
                        @Html.DropDownListFor(model => model.EntityTypeID, new SelectList(Model.EntityTypes, "ID", "Name"), new { id = "entityTypeDropDown", @class = "DropDown_220" })
                    </td>
                </tr>
                <tr style="height: 26px;">
                    <td class="leftColumn">
                        @Html.LabelFor(model => model.SearchField, new { id = "entityTypeSearchLabel"}):
                    </td>
                    <td class="rightColumn">
                        @Html.TextBoxFor(model => model.SearchField, new { id = "entityTypeSearchField", @class = "ui-widget TextField_220" })
                    </td>
                </tr>
                <tr style="height: 26px;">
                    <td class="leftColumn"></td>
                    <td class="rightColumn"></td>
                </tr>
            </table>
            <table cellpadding="0" cellspacing="0" border="0">
                <tr>
                    <td id="filterByEntityError" style="width: 375px;"></td>
                    <td align="right" style="width: 50px; padding-right: 75px;">
                        <div>
                            <input type="submit" value="Search" />
                        </div>
                    </td>
                </tr>
            </table>
        }
    </div>

    <div id="searchResults">
        @(Html.Telerik().Grid(Model.Users)
            .Name("Users").TableHtmlAttributes(new { style = "width: 870px;"})
            .Columns(columns => {
                columns.Bound(o => o.EntityTypeName).Title("Entity Type");
                columns.Bound(o => o.FirstName).Title("First Name");
                columns.Bound(o => o.LastName).Title("Last Name");
                columns.Bound(o => o.Username).Title("Username");
                columns.Template(
                    @<text>
                        <a href="mailto:@item.EmailAddress" target="blank">@item.EmailAddress</a>
                    </text>).Title("Email").HtmlAttributes(new { style = "text-align: center" }).HeaderHtmlAttributes(new { style = "text-align: center" });
                columns.Template(
                    @<text>
                        <div class="ActionIcon_ResendInvitationOn" title="Resend Invitation" onclick="location.href='@Url.Action("ResendInvitation", "Admin", new { entityTypeID = item.EntityTypeID, emailAddress = item.EmailAddress })'"></div> 
                        @{
                            if (item.IsApproved) {
                                <div class="ActionIcon_AccountStatusOn" title="Disable Account" onclick="setApprovalStatus('@item.Username', false);"></div> 
                            }
                            else {
                                <div class="ActionIcon_AccountStatusOn" title="Enable Account" onclick="setApprovalStatus('@item.Username', true);"></div> 
                            }
                        }
                        @{
                            if (item.IsLockedOut) {
                                <div class="ActionIcon_ResetPasswordOn" title="Unlock Account" onclick="unlockAccount('@item.Username')"></div> 
                            }
                            <div class="ActionIcon_ResetPasswordOn" title="Reset Password"  onclick="resetPassword('@item.Username')"></div> 
                        }
                        <div class="ActionIcon_EditOn" title="Edit User" onclick="location.href='@Url.Action("Edit", "Admin", new { id = item.MembershipID, username = item.Username })'"></div>
                    </text>).Title("Actions");
                columns.Bound(o => o.RowNumber).Hidden(true);
                columns.Bound(o => o.MembershipID).Hidden(true);
                columns.Bound(o => o.EntityTypeID).Hidden(true);
            })
            .Pageable(paging => paging.PageSize(10))
            .Sortable()
        )
        <span>Total Rows: </span> @Html.DisplayFor(model => Model.TotalRowCount)

        <p>
            @Html.ActionLink("Create New User", "Create", "Invitation")
        </p>
    </div>
</div>

我已将此视图与这一行一起用作 BeginForm:

@using (Html.BeginForm("FilterByUserData", "Admin", FormMethod.Post, new {model = Model})) 

然后使用这个:

 @using (Html.BeginForm())

使用这个 Ajax 脚本

$('#filterByUserButton').click(function () {
    $.ajax({
        url: '/Admin/FilterByUserData',
        type: 'POST',
        success: function (result) {
            alert(result);
        }
    });
});

第一种方法使用 HTML 后效果很好,但我需要使用 ajax 功能,所以第二种方法是我的麻烦

1) 使用 ajax 调用,发布到操作的模型包含文本字段中的所有 Null 值,而不是我在表单中输入的值。该模型使用 HTML 方法可以正常发布

2) 即使我可以让模型正确发布以便我可以搜索值,我如何将数据返回到网格以及如何在验证失败时显示模型错误而不是将结果发送到更新目标 ID?

3) 能够让两个表单都提交给控制器,并将相同的结果返回给视图,即搜索结果

似乎我只能有 1 个 UpdateTarget - 如果您从来没有错误,这很好......但行为很糟糕,我需要在字段上显示验证错误,就像这是一个同步调用一样。所以来自动作的错误结果需要有它自己的目标。

我想出了这个 Ajax 调用,它可以很好地处理不需要返回数据的操作,只需返回成功或错误消息。每个都加载到自己的 div 中。

$(function () {
    $('form').submit(function () {
        if ($(this).valid()) {
            $.ajax({
                url: this.action,
                type: this.method,
                data: $(this).serialize(),
                success: function (result) {
                    if (result.toString().indexOf("Success") == -1) {
                        $('#successDiv').hide();
                        $('#errorDiv').html('');
                        $('#errorDiv').fadeIn(100).append($('<ul />').append(result));
                    }
                    else {
                        $('#errorDiv').hide();
                        $('#successDiv').fadeIn(1000).html(result).fadeOut(6000);
                    }
                }
            });
        }
        return false;
    });
});

这些是处理搜索的操作(FilterByUserData 操作不同,因为我试图弄清楚如何返回正确的结果,但我还没有让它正确返回数据。

    public ActionResult Search() {
        var model = new SearchUsersViewModel();
        return View(model);
    }

    [HttpPost]
    public ActionResult FilterByUserData(SearchUsersViewModel model) {
        var result = string.Empty;
        if (model.LastName != null || model.Username != null || model.EmailAddress != null) {
            var listOfMatchingUsers = SearchUserService.SearchByUserData(model.LastName, model.Username, model.EmailAddress);
            model = PrepareResultsModel(listOfMatchingUsers, model);
        }
        else {
            ModelState.AddModelError("", "Last Name, Username or Email Address must be entered for search");
        }
        if (ModelState.IsValid)
            result = "Success: Thanks for your submission: " + model.Username;
        else {
            result = ModelState.SelectMany(item => item.Value.Errors).Aggregate(result, (current, error) => current + error.ErrorMessage);
        }
        return Content(result, "text/html");
    }

    [HttpPost]
    public ActionResult FilterByEntityData(SearchUsersViewModel model) {
        if(model.EntityTypeID > 0 &&  model.SearchField != null) {
            var listOfMatchingUsers = SearchUserService.SearchByEntityData(model.EntityTypeID, model.SearchField);
            model = PrepareResultsModel(listOfMatchingUsers, model);
        }
        else {
            var entityType = string.Empty;
            if(model.EntityTypeID == 2) entityType = "Lender";
            if (model.EntityTypeID == 3) entityType = "School";
            ModelState.AddModelError("", entityType + " name must be entered for search");
        }
        return View("Search", model);
    }

    private SearchUsersViewModel PrepareResultsModel(ICollection<SearchUserResultsDTO> listOfMatchingUsers, SearchUsersViewModel model) {
        if (listOfMatchingUsers.Count != 0) {
            model.Users = listOfMatchingUsers.Select(item => new UserEditViewModel(item)).ToList();
            model.TotalRowCount = model.Users.Count();
        }
        return model;
    }

如何获取 ajax 帖子的模型以执行我的操作?以及如何获得正确的模型,以便我的网格有结果?如果出现错误,如何以与验证摘要类似的方式显示?

更新:@Shyju 我尝试了您建议的尝试将您的示例合并到我的代码中,但模型属性的值仍然为空并且没有被设置。这是我在我的 ajax 函数中所做的,注意它与你的 form.post 方法不同:

$('#filterByUserButton').click(function () {
    $.ajax({
        url: '/Admin/FilterByUserData',
        type: 'POST',
        data: {LastName : $('#LastName').val(), Username : $('#Username').val(), EmailAddress : $('#EmailAddress').val()},
        success: function (result) {
            processResult(result);
        }
    });
});

我也试过这个:

$('form').submit(function() {
    alert("submitting");
    $.post('@Url.Action("FilterByUserData","Admin")', { LastName: $("#LastName").val(), UserName: $("#Username").val(), EmailAddress: $("#EmailAddress").val() },
        function(data) {
            alert(data);
    });
});

页面闪烁并且第一个警报是它,但它从未命中第二个警报并且从未命中控制器,因此它看起来甚至没有真正发布。 我也试过这个,但它也没有击中控制器动作:

$('#filterByUserButton').click(function () {
    alert("submitting");
    $.ajax({
        url: '/Admin/FilterByUserData',
        type: 'POST',
        data: {LastName : $('#LastName').val(), Username : $('#Username').val(), EmailAddress : $('#EmailAddress').val()},
        success: function (result) {
            alert(result);
            processResult(result);
        }
    });
});

我也尝试了上面的数据:省略行,它会影响我的控制器操作,但同样,正在发送的模型在属性中具有空值。模型不是空的,只是属性值。我在模型的下拉列表属性中有值,EntityTypes 仍然显示 4 个值。所以有一些关于发布该表单的事情,但表单值没有进入模型属性。

更新 2

@Shyju 我正在使用你的 ajax 代码:

$(function () {
    $('#SearchByUserForm').submit(function () {
        $.post('@Url.Action("FilterByUserData", "Admin")', { LastName: $("#LastName").val(), UserName: $("#UserName").val(), EmailAddress: $("#EmailAddress").val() },
        function (data) {
            alert(data);
        });
        return false;
    });
});

我可以在点击提交按钮后逐步执行此操作,但它无法点击我的控制器的发布操作。我查看了为表单呈现的 HTML,这就是我所看到的......这是不正确的:

<form action="/Admin/Search/SearchByUserForm" method="post">         

如您所见,此处的 POST 操作是错误的。控制器是正确的,但其余的都是错误的。它应该发布到 /Admin/FilterByUserData。我确定这只是我所处的语法混乱,我只是不确定我做错了什么。在标准形式中,我会这样做:

@using (Html.BeginForm("FilterByUserData", "Admin", FormMethod.Post, new { model = Model })) { 

哪个工作正常。但是我不能将此表单签名与您的 Ajax 代码中的 URL 操作帖子结合起来。我尝试只用 ID 留下表格,但我遇到了上面所说的问题:

@using (Html.BeginForm(new {@id = "SearchByUserForm"})) {

我认为这个问题是由于我的视图是通过点击控制器的搜索操作来呈现的,所以 URL 是 /Admin/Search 但视图上的表单需要发布到 /Admin/FilterByUserData 和 /管理员/FilterByEntityData。就像我上面说的,如果我用正确的签名明确地创建 Html.BeginFrom ,那么它就可以工作,但它不是 Ajax

更新 3

@Shyju

我搞定了,问题肯定是表单签名,我修改了它,我可以成功地点击我的控制器操作,并且数据正在传入!感谢您的所有帮助!

这就是它的工作原理:

@using (Html.BeginForm(new {id = "SearchByUserForm", @controller = "Admin", @action = "FilterByUserData"})) {

【问题讨论】:

    标签: jquery ajax asp.net-mvc-3 c#-4.0 razor


    【解决方案1】:

    当您在 jquery 帖子中传递数据时,使用与您的属性名称相同的名称(视图模型是您的操作方法的参数)。您将在那里获得充满数据的对象。

    ('form').submit(function () {
        $.post('@Url.Action("Logon","Account")', { LastName : $("#username").val(), 
                         UserName:   $("#password").val() },
                         EmailAddress:   $("#password").val() },  function (data) {
    
        //process the result and update the grid
    
    });
    

    编辑: 由于 OP 说它不工作,我从头开始创建了一个示例项目来验证这一点,它工作正常。 这是我的视图看起来像

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
    
    <h2>Index</h2>
    <form id="frm1" action="Search">
    LastName <input type="text" id="LastName" /><br />
    UserName <input type="text" id="UserName" /><br />
    Email Address <input type="text" id="EmailAddress" />
    <input type="submit" value="go" />
    </form>
    <script type="text/javascript">
        $(function () {
            $('#frm1').submit(function () {
                $.post('@Url.Action("Search", "Home")', { LastName: $("#LastName").val(),
                    UserName: $("#UserName").val(),
                    EmailAddress: $("#EmailAddress").val()
                }, function (data) {
    
                    alert(data)
                });
                return false;
            });
        });
    </script>
    

    而我收到 post 调用的操作方法是

    public ActionResult Search(SearchUsersViewModel objVM)
    {
       return View();
    }
    

    这是结果的截图

    客户

    控制器动作方法

    您甚至可以使用 jquery serialize() 方法将表单发送到控制器操作。

    http://api.jquery.com/serialize/

    【讨论】:

    • 我尝试采纳您的建议,但它不起作用,请参阅问题末尾的更新
    • 感谢您抽出宝贵时间提供帮助并扩展您的答案。不幸的是,它仍然不适合我。我的观点、表格和发布路线比您的示例要复杂一些。我最后更新了我的问题,并结合了你的代码,并解释了为什么它对我来说失败了。
    • 我唯一不明白的是,整个页面看起来都在刷新,整个屏幕在提交期间闪烁/闪烁,并随着更新的屏幕重新渲染。这并不让人感到 ajaxy。但那是因为操作的返回是返回 View() 而不是 Content 或某种 JSON 结果。所以看起来得到一个干净的异步帖子和响应还没有完成
    • @CDSmith:您可以返回一个带有表格的视图,您可以在其中找到搜索结果。如果页面正在重新加载,这意味着您不会停止页面以进行进一步的表单提交。你做了 return false 吗?
    • @CDSmith:我认为您的页面中还有其他问题。没有看到它,我不知道它是什么。或许,您应该尝试找出问题并加以解决。
    猜你喜欢
    • 1970-01-01
    • 2013-01-18
    • 2013-01-08
    • 1970-01-01
    • 2012-10-02
    • 1970-01-01
    • 1970-01-01
    • 2012-05-10
    • 2015-09-08
    相关资源
    最近更新 更多