【问题标题】:ViewModel not binding when using in a new view在新视图中使用时 ViewModel 未绑定
【发布时间】:2015-09-23 15:16:12
【问题描述】:

我最初能够将我的 index.cshtml 页面上的表单上的视图模型传递给编辑器模板页面。在索引页面上,我有一个提交按钮,可将表单结果(编辑器模板中的单选按钮组)发布回控制器,并在 HttpPost 方法中将此模型传递给显示在模式弹出窗口中的局部视图。所有这一切都是显示被选中的表单元素,但它禁用了用户的单选按钮。从这里用户可以返回(关闭窗口)或确认表单结果。当用户单击确认按钮时,它应该将视图模型传递回控制器到另一个 HttpPost 方法,然后该方法将处理表单结果并返回最终确认视图。但是当我尝试将视图模型从模式弹出窗口传递回控制器时,它不会保留绑定。我尝试确保所有内容都通过隐藏输入绑定,但我一定在某处遗漏了一些东西。也许我正在以错误的方式解决这个问题。我只需要基本上保留初始帖子中的视图模型绑定,并能够在用户从模式弹出窗口中确认选择后进行处理。什么是最好的方法来实现这一点而不必在其中放置会话黑客?

索引

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "ballotForm" }))
{
   @Html.AntiForgeryToken()
   @(Html.EditorFor(m => m.BallotViewModel, new ViewDataDictionary(ViewData)
    {
       TemplateInfo = new System.Web.Mvc.TemplateInfo
       {
         HtmlFieldPrefix = "BallotViewModel"
       }
     }))
<table class="col-sm-12">
   <tr>
      <td class="pull-right">
         <button type="submit" class="btn btn-primary" data-target="#modal-container" data-toggle="modal">Vote Management Ballot</button>
      </td>
   </tr>
</table>
}

控制器 - 初始发布到模态弹出窗口

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(HomeViewModel bModel)
{
    if (ModelState.IsValid)
    {
       //set property to identity view
       bModel.BallotViewModel[0].IsVoteConfirmationView = true;
       return PartialView("ViewVoteConfirmation", bModel);
    }         
 }

控制器 - 从模式弹出窗口确认提交后发布

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmVote(HomeViewModel cModel)
{
    //Process form results here but model is null

    //Go to Thank You View
    return View();
}

查看投票确认:

@model Ballot.WebUI.Models.HomeViewModel
<div class="row">
        @(Html.EditorFor(m => m.BallotViewModel, new ViewDataDictionary(ViewData) { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "BallotViewModel" } }))
    </div>

        @using (Html.BeginForm("ConfirmVote", "Home", FormMethod.Post, new { id = "ballotConfirmVoteForm" }))
        {
            @Html.AntiForgeryToken()
            <div class="row">

               @Html.EditorFor(m => m.BallotViewModel[0].Proposals, "Proposals", new ViewDataDictionary(ViewData)
               {
                   TemplateInfo = new TemplateInfo
                   {
                       HtmlFieldPrefix  = "Proposals"
                   }
               })

            </div>
            <div class="row">
                <div class="col-md-4 col-md-offset-4">
                    <button type="button" class="btn btn-default"
                            data-dismiss="modal">
                        Cancel
                    </button>
                    <button type="submit" id="approve-btn"
                            class="btn btn-danger">
                        Confirm
                    </button>
                </div>
            </div>
        }

提案视图模型:

public class ProposalViewModel
    {
        public int ProposalItemID { get; set; }
        public string ProposalItemTitle { get; set; }

        public string Option0_Name { get; set; }
        public string Option1_Name { get; set; }
        public string Option2_Name { get; set; }
        public string Option3_Name { get; set; }
        public string PercOfShare { get { return "% of Share"; }}

        public bool IsHeader { get; set; }
        public int TagOrder { get; set; }
        public int SelectedVoteOption { get; set; }
        public bool IsVoteConfirmationView { get; set; }
        public bool IsCumulative { get; set; }
        public int SharePercentage { get; set; }

        public List<VoteOptionViewModel> lVoteOptions { get; set; }
    }

建议:

@model List<Ballot.WebUI.Models.ProposalViewModel>

@for (int i = 0; i < Model.Count; i++)
{

    @Html.HiddenFor(m => m[i].ProposalItemID)
    @Html.HiddenFor(m => m[i].ProposalItemTitle)
    @Html.HiddenFor(m => m[i].Option0_Name)
    @Html.HiddenFor(m => m[i].Option1_Name)
    @Html.HiddenFor(m => m[i].Option2_Name)
    @Html.HiddenFor(m => m[i].Option3_Name)
    @Html.HiddenFor(m => m[i].PercOfShare)
    @Html.HiddenFor(m => m[i].IsHeader)
    @Html.HiddenFor(m => m[i].TagOrder)
    @Html.HiddenFor(m => m[i].SelectedVoteOption)
    @Html.HiddenFor(m => m[i].IsVoteConfirmationView)
    @Html.HiddenFor(m => m[i].IsCumulative)
    @Html.HiddenFor(m => m[i].lVoteOptions)
    @Html.HiddenFor(m => m[i].SharePercentage)

}

jquery 脚本更改 SharePercentage 标签的值

$(function () {
    //When 'For' is Selected
    $('[class$=PercOfShareFor]').on('click', function (e) {
        if ($(this).is(':checked')) {
            var forMatches1 = 0;

            $('[class$=PercOfShareFor]').each(function (i, val) {
                if ($(this).is(':checked')) {
                    //check how many 'For' Vote Options are selected
                    forMatches1++;
                    //select the Share Percentage value label in the same row, and change the class to ForSelected (used as selector)
                    $(this).closest('td').next('td').next('td').find('.SharePercentage')
                        .removeClass("SharePercentage")
                        .addClass("SharePercentageForSelected");
                    //if the Share Percentage class (used as selector) was previously WithholdSelected then change to ForSelected
                    $(this).closest('td').next('td').next('td').find('.SharePercentageWithholdSelected')
                        .removeClass("SharePercentageWithholdSelected")
                        .addClass("SharePercentageForSelected");
                }
            });

            //divide total 'For' Selections by number of Director Proposals
            var forPercent1 = 100 / forMatches1;

            //format the percentage to display 2 decimal places if not a whole number
            var forPercent2 = Math.round(forPercent1 * 100) / 100;

            //Update 'For' Percentages
            $('[class$=SharePercentageForSelected]').text(forPercent2);
        }
    });
    //When 'Withhold' is Selected after initially selecting 'For'
    $('[class$=PercOfShareWithhold]').on('click', function (e) {
        if ($(this).is(':checked')) {
            var forMatches = 0;

            $('[class$=PercOfShareFor]').each(function (i, val) {
                if ($(this).is(':checked')) {
                    //check how many 'For' Vote Options are still selected
                    forMatches++;
                }
            });
            var withholdMatches = 0;
            $('[class$=PercOfShareWithhold]').each(function (i, val) {
                if ($(this).is(':checked')) {
                    //check how many 'Withhold' Vote Options are still selected
                    withholdMatches++;
                    //set the class to WithholdSelected
                    $(this).closest('td').next('td').find('.SharePercentageForSelected')
                        .removeClass("SharePercentageForSelected")
                        .addClass("SharePercentageWithholdSelected")
                        .text("0"); //Set 'Withhold' Percentage back to 0
                }
            });

            //divide total 'For' Selections by number of Director Proposals
            var forPercent1 = 100 / forMatches;

            //format the percentage to display 2 decimal places if not a whole number
            var forPercent2 = Math.round(forPercent1 * 100) / 100;

            //Update 'For' Percentages
            $('[class$=SharePercentageForSelected]').text(forPercent2);

        }
    });
});

【问题讨论】:

  • 您需要提供更多信息。如果您不绑定它几乎总是因为您的控件名称不正确。显示我们ViewVoteConfirmation.cshtml 视图的一部分
  • 好的,现在绑定工作正常,除了 SharePercentage 字段。 This field is initially bound from the model as 0. On the page though the value is changed (via jquery) when a radio button is selected.但是当页面发布表单结果时不保存更改的值,它仍然显示为 0。如何保存更改的值?
  • 您确定脚本中的值实际上正在更改(您没有显示它)。您的代码中的一些内容没有意义 - 在 ViewVoteConfirmation.cshtml 中您有 EditorFor(m =&gt; m.BallotViewModel, ...) 但它不在表单标签内。下一个Proposals.cshtmlEditorTemplate,所以它应该是model ProposalViewModel(不是List&lt;ProposalViewModel&gt;)-ViewVoteConfirmation.cshtml 中的EditorFor() 方法将为集合中的每个项目正确生成html。
  • 然后在 Index() POST 方法中使用 bModel.BallotViewModel[0].IsVoteConfirmationView = true; 但这将被忽略 - 如果它是 false 之前它将在视图中呈现为 false 因为该值已添加到ModelState
  • 还要注意你的EditorTemplate要正常工作,它需要命名为ProposalViewModel.cshtml(与类名相同)并将其用作@Html.EditorFor(m =&gt; m.BallotViewModel[0].Proposals, , new ViewDataDictionary ....)(不要指定名称)跨度>

标签: asp.net-mvc binding viewmodel


【解决方案1】:

您无法通过表单帖子做您想做的事情。浏览器将 Post 的返回视为完整的 HTML 页面(即使您说它是部分服务器端)。您将需要使用某种形式的 javascript 来完成它,或者您需要使您的确认页面成为实际页面而不是模式弹出窗口。

基本前提是你需要通过javascript捕获提交(或按钮点击)然后显示模态。您可以使用第一个操作的结果填充模式,但您需要通过 ajax 而不是标准表单帖子提交表单。然后根据他们在模态中的选择,您可以提交或不提交表单。

已经有多种可用资源可能对您有所帮助。这是显示如何display a confirmation for a delete action 的一个。然后,您可以更改该 javascript 以通过 ajax 加载您的第一个操作的结果,如关于加载 MVC partial views using AJAX 的这篇(诚然较早的)文章或关于使用 jQuery dialog for CRUD operations 的这篇文章。

【讨论】:

    【解决方案2】:

    模型绑定修复是向 BallotViewModel 的 EditorTemplate 添加一个隐藏字段,以便在选择单选按钮时,不仅标签会更改值,而且隐藏字段也会更改值。

    编辑器模板

    @Html.HiddenFor(m => Model.Proposals[i].SharePercentage, new { @class= "hdnSharePercentage" }) @Html.LabelFor(m => Model.Proposals[i].lVoteOptions[j].SharePercentage, Model.Proposals[i].lVoteOptions[j].SharePercentage, new { @class= "SharePercentage" })

    jQuery $(this).closest('td').next('td').next('td').find('.hdnSharePercentageWithholdSelected') .removeClass("hdnSharePercentageWithholdSelected") .addClass("hdnSharePercentageForSelected");

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-31
      • 2016-08-15
      • 1970-01-01
      • 2018-12-20
      • 2011-01-31
      • 1970-01-01
      • 2020-12-31
      • 2020-02-06
      相关资源
      最近更新 更多