在新视图中使用时 ViewModel 未绑定
ViewModel not binding when using in a new view
我能够将 index.cshtml 页面上表单上的视图模型最初传递到编辑器模板页面。在索引页面上,我有一个提交按钮,该按钮 post 将表单结果(编辑器模板中的单选按钮组)返回到控制器,并在 HttpPost 方法中将此模型传递给局部视图显示在模式弹出窗口中。所有这一切只是显示被选中的表单元素,但它禁用了单选按钮给用户。用户可以从这里返回(关闭 window)或确认表单结果。当用户单击确认按钮时,它应该将视图模型传递回控制器到另一个 HttpPost 方法,该方法将处理表单结果和 return 最终确认视图。但是当我尝试将视图模型从模式弹出窗口传递回控制器时,它不会保持绑定。我尝试确保所有内容都通过隐藏输入进行绑定,但我一定是在某处遗漏了一些东西。也许我正在以错误的方式解决这个问题。我只需要基本上保持视图模型与初始 post 的绑定,并能够在用户从模式弹出窗口中确认选择后进行处理。无需在其中放置会话 hack 即可实现此目的的最佳方法是什么?
索引
@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>
}
控制器 - 初始 Post 到模态弹出
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(HomeViewModel bModel)
{
if (ModelState.IsValid)
{
//set property to identity view
bModel.BallotViewModel[0].IsVoteConfirmationView = true;
return PartialView("ViewVoteConfirmation", bModel);
}
}
控制器 - Post 从模态弹出窗口确认提交后
[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);
}
});
});
您无法通过表单 Post 来完成您想做的事情。浏览器将 Post 中的 return 视为完整的 HTML 页面(即使您说它是部分服务器端)。您将需要使用某种形式的 javascript 来完成它,或者您需要将确认页面设为实际页面而不是模态弹出窗口。
基本前提是您需要通过javascript 捕获提交(或按钮单击)然后显示模态。您可以使用第一个操作的结果填充模式,但您需要通过 ajax 而不是标准表单 post 提交表单。然后根据他们在模式中的选择,您可以提交或不提交表单。
已有多种资源可以帮助您。这是一个展示如何 display a confirmation for a delete action. You could then alter that javascript to load the result of your first action via ajax as in this (admittedly older) article about loading MVC partial views using AJAX or maybe this one about using jQuery dialog for CRUD operations.
模型绑定修复是向 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");
我能够将 index.cshtml 页面上表单上的视图模型最初传递到编辑器模板页面。在索引页面上,我有一个提交按钮,该按钮 post 将表单结果(编辑器模板中的单选按钮组)返回到控制器,并在 HttpPost 方法中将此模型传递给局部视图显示在模式弹出窗口中。所有这一切只是显示被选中的表单元素,但它禁用了单选按钮给用户。用户可以从这里返回(关闭 window)或确认表单结果。当用户单击确认按钮时,它应该将视图模型传递回控制器到另一个 HttpPost 方法,该方法将处理表单结果和 return 最终确认视图。但是当我尝试将视图模型从模式弹出窗口传递回控制器时,它不会保持绑定。我尝试确保所有内容都通过隐藏输入进行绑定,但我一定是在某处遗漏了一些东西。也许我正在以错误的方式解决这个问题。我只需要基本上保持视图模型与初始 post 的绑定,并能够在用户从模式弹出窗口中确认选择后进行处理。无需在其中放置会话 hack 即可实现此目的的最佳方法是什么?
索引
@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>
}
控制器 - 初始 Post 到模态弹出
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(HomeViewModel bModel)
{
if (ModelState.IsValid)
{
//set property to identity view
bModel.BallotViewModel[0].IsVoteConfirmationView = true;
return PartialView("ViewVoteConfirmation", bModel);
}
}
控制器 - Post 从模态弹出窗口确认提交后
[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);
}
});
});
您无法通过表单 Post 来完成您想做的事情。浏览器将 Post 中的 return 视为完整的 HTML 页面(即使您说它是部分服务器端)。您将需要使用某种形式的 javascript 来完成它,或者您需要将确认页面设为实际页面而不是模态弹出窗口。
基本前提是您需要通过javascript 捕获提交(或按钮单击)然后显示模态。您可以使用第一个操作的结果填充模式,但您需要通过 ajax 而不是标准表单 post 提交表单。然后根据他们在模式中的选择,您可以提交或不提交表单。
已有多种资源可以帮助您。这是一个展示如何 display a confirmation for a delete action. You could then alter that javascript to load the result of your first action via ajax as in this (admittedly older) article about loading MVC partial views using AJAX or maybe this one about using jQuery dialog for CRUD operations.
模型绑定修复是向 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");