我有一个ViewModel,其中包含两个条件列表 - BuyConditions
和SellConditions
- 加上一些属性 - Name
等。列表使用视图中的表格显示。 jQuery允许对表进行操作 - 向表添加条件(从购买条件/销售条件DDL')并从表中删除条件。
我的问题是,当模型发布回控制器时,条件列表不会在模型中更新。
可能指向该问题的一点是,当模型返回到视图时(由于ModelStat.IsValid
失败),Markets DDL中的数据仍然存在,但是买入和卖出条件中的数据DDL& #39; s没有。
我无法在网上找到一个例子来弄清楚我出错的地方。有人这么做过吗?
视图模型:
public class StrategyViewModel
{
public string ID { get; set; }
[Display(Name = "Strategy Name")]
public string StrategyName { get; set; }
[Display(Name = "Market")]
public string SelectedMarketID { get; set; }
[Display(Name = "Asset Type")]
public string SelectedShareTypeID { get; set; }
[Display(Name = "Share")]
public string SelectedShareID { get; set; }
public bool Active { get; set; }
public IEnumerable<Market> Markets { get; set; } // top level of three cascading DDL's - the other two populated via JQuery
public IEnumerable<Condition> BuyConditionList { get; set; } // used to populate DDL
public IEnumerable<Condition> SellConditionList { get; set; } // used to populate DDL
public List<BuyCondition> BuyConditions { get; set; } // list of Buy Conditions
public List<SellCondition> SellConditions { get; set; } // list of Sell Conditions
AppRepository repository = new AppRepository();
public StrategyViewModel()
{
// populate lists
Markets = ListUtils.AddDefaultOptionToMarketList( repository.GetMarkets(), -1, "Select a Market" ).AsEnumerable();
BuyConditionList = repository.GetConditions();
SellConditionList = repository.GetConditions();
BuyConditions = new List<BuyCondition>();
SellConditions = new List<SellCondition>();
}
}
控制器:
public ActionResult Create()
{
StrategyViewModel model = new StrategyViewModel();
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
[Authorize(Roles = "User, Admin")]
public ActionResult Create(StrategyViewModel model)
{
// for debugging
var errorList = ModelState.Values.SelectMany(v => v.Errors);
if (ModelState.IsValid)
{
var user = repository.GetCurrentUser(User.Identity.GetUserId());
var strategy = new Strategy();
strategy.StrategyName = model.StrategyName;
strategy.ShareID = Convert.ToInt32(model.SelectedShareID);
strategy.BuyConditions = model.BuyConditions.ToList();
strategy.SellConditions = model.SellConditions.ToList();
strategy.Active = model.Active;
int strategyID = repository.AddStrategy(user, strategy);
return RedirectToAction("Index");
}
return View(model);
}
查看:
@model ShareTrigger.Models.StrategyViewModel
@{
ViewBag.Title = "Create";
}
@{Html.EnableUnobtrusiveJavaScript(true);}
<script type="text/javascript">
$(document).ready(function () {
$(function () {
$('#SelectedMarketID').change(function () {
var selectedMarketID = $(this).val();
$.getJSON('@Url.Action("ShareTypes")', { marketId: selectedMarketID }, function (shareTypes) {
var shareTypesSelect = $('#SelectedShareTypeID');
shareTypesSelect.empty();
$.each(shareTypes, function (index, shareType) {
shareTypesSelect.append($('<option/>').attr('value', shareType.Value).text(shareType.Text));
});
$('#SelectedShareTypeID').trigger("change");
});
});
$('#SelectedShareTypeID').change(function () {
var selectedShareTypeId = $(this).val();
var selectedMarketID = $('#SelectedMarketID').val();
$.getJSON('@Url.Action("Shares")', { shareTypeId: selectedShareTypeId, marketID: selectedMarketID }, function (shares) {
var sharesSelect = $('#SelectedShareID');
sharesSelect.empty();
$.each(shares, function (index, share) {
sharesSelect.append($('<option/>').attr('value', share.Value).text(share.Text));
});
});
});
});
$('#AddBuyCondition').click(function () {
var conditionID = +$("#BuyConditionList").val();
if (conditionID != -2) {
conditionID = +$("#BuyConditionList").val();
var conditionText = $("#BuyConditionList").find('option:selected').text();
$("tr:contains('Add')").remove();
$('#BCDropDownRow').before('<tr class="bcrow"><td class="BuyConditionCell" data-conditionID="' + conditionID + '"><input data-val="true" data-val-number="The field ConditionID must be a number." data-val-required="The ConditionID field is required." id="item_ConditionID" name="item.ConditionID" type="hidden" value="' + conditionID + '">' + conditionText + '</td><td><button class="RemoveBuyCondition type="button" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-minus"></span></button></td></tr>');
$("#BuyConditionList").find('option:selected').remove();
var rows = $('#BuyConditionList option').size();
if (rows == 0) { $("#BuyConditionList").append('<option value="-2">No more conditions to select</option>'); }
}
});
$('#BuyConditionsTable').on('click', '.RemoveBuyCondition', function () {
var conditionID = +$(this).parent().parent().find('.BuyConditionCell').data('conditionid');
var conditionText = $(this).parent().parent().find('.BuyConditionCell').text();
$(this).closest('.bcrow').remove();
$('#BuyConditionList option[value="-2"]').remove();
$("#BuyConditionList").append('<option value="' + conditionID + '">' + conditionText + '</option>');
var options = $('#BuyConditionList option');
var arr = options.map(function (_, o) { return { t: $(o).text(), v: o.value }; }).get();
arr.sort(function (o1, o2) { return o1.t > o2.t ? 1 : o1.t < o2.t ? -1 : 0; });
options.each(function (i, o) {
o.value = arr[i].v;
$(o).text(arr[i].t);
});
});
$('#AddSellCondition').click(function () {
var conditionID = +$("#SellConditionList").val();
if (conditionID != -2) {
conditionID = +$("#SellConditionList").val();
var conditionText = $("#SellConditionList").find('option:selected').text();
$("tr:contains('Add')").remove();
$('#SCDropDownRow').before('<tr class="scrow"><td class="SellConditionCell" data-conditionID="' + conditionID + '"><input id="item_ConditionID" name="item.ConditionID" type="hidden" value="' + conditionID + '">' + conditionText + '</td><td><button class="RemoveSellCondition type="button" class="btn btn-default btn-xs"><span class="glyphicon glyphicon-minus"></span></button></td></tr>');
$("#SellConditionList").find('option:selected').remove();
var rows = $('#SellConditionList option').size();
if (rows == 0) { $("#SellConditionList").append('<option value="-2">No more conditions to select</option>'); }
}
});
$('#SellConditionsTable').on('click', '.RemoveSellCondition', function () {
var conditionID = +$(this).parent().parent().find('.SellConditionCell').data('conditionid');
var conditionText = $(this).parent().parent().find('.SellConditionCell').text();
$(this).closest('.scrow').remove();
$('#SellConditionList option[value="-2"]').remove();
$("#SellConditionList").append('<option value="' + conditionID + '">' + conditionText + '</option>');
var options = $('#SellConditionList option');
var arr = options.map(function (_, o) { return { t: $(o).text(), v: o.value }; }).get();
arr.sort(function (o1, o2) { return o1.t > o2.t ? 1 : o1.t < o2.t ? -1 : 0; });
options.each(function (i, o) {
o.value = arr[i].v;
$(o).text(arr[i].t);
});
});
});
</script>
<h2>Create Strategy</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
@Html.ValidationSummary(true)
<div class="form-group">
@Html.LabelFor(model => model.StrategyName, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.StrategyName)
@Html.ValidationMessageFor(model => model.StrategyName)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SelectedMarketID, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(x => x.SelectedMarketID, new SelectList(Model.Markets, "MarketId", "MarketCode"))
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SelectedShareTypeID, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(x => x.SelectedShareTypeID, Enumerable.Empty<SelectListItem>(), "Select an Asset Type")
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SelectedMarketID, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(x => x.SelectedShareID, Enumerable.Empty<SelectListItem>(), "Select a Share")
</div>
</div>
<div>
</div>
<div class="col-md-10">
<table class="table" id="BuyConditionsTable">
<tr>
<th>
@Html.DisplayName("Buy Conditions")
</th>
<th></th>
</tr>
@foreach (var item in Model.BuyConditions)
{
<tr>
<td>
@Html.HiddenFor(modelItem => item.ConditionID)
@Html.DisplayFor(modelItem => item.ConditionName)
</td>
<td></td>
</tr>
}
<tr id="BCDropDownRow">
<td>
<button id="AddBuyCondition" type="button" class="btn btn-default btn-xs">
<span class="glyphicon glyphicon-plus"></span>
</button>
@Html.DropDownListFor(x => x.BuyConditionList, new SelectList(Model.BuyConditionList, "ConditionID", "ConditionName"))
</td>
<td></td>
</tr>
</table>
</div>
<div>
</div>
<div class="col-md-10">
<table class="table" id="SellConditionsTable">
<tr>
<th>
@Html.DisplayName("Sell Conditions")
</th>
<th></th>
</tr>
@foreach (var item in Model.SellConditions)
{
<tr>
<td>
@Html.HiddenFor(modelItem => item.ConditionID)
@Html.DisplayFor(modelItem => item.ConditionName)
</td>
<td></td>
</tr>
}
<tr id="SCDropDownRow">
<td>
<button id="AddSellCondition" type="button" class="btn btn-default btn-xs">
<span class="glyphicon glyphicon-plus"></span>
</button>
@Html.DropDownListFor(x => x.SellConditionList, new SelectList(Model.SellConditionList, "ConditionID", "ConditionName"))
</td>
<td></td>
</tr>
</table>
</div>
<div class="form-group">
<div class="col-md-offset-9 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryval")
}
答案 0 :(得分:1)
由于以下代码,您BuyConditions
和SellConditions
为空的原因
@Html.HiddenFor(modelItem => item.ConditionID)
这会将ALL渲染为
<input type="hidden" id="item_ConditionID" name="item.ConditionID" />
由于命名错误,无法在控制器操作Create
中解析。
相反,它应该是
@Html.HiddenFor(x => x.SellConditions[index].ConditionID)
这将使控件呈现为(如果index
为0)
<input type="hidden" id="SellConditions_0__ConditionID" name="SellConditions[0].ConditionID" />
因此,foreach
块应如下所示
@foreach (int index = 0; index < Model.SellConditions.Count; index++)
{
<tr>
<td>
@Html.HiddenFor(x => x.SellConditions[index].ConditionID)
@Html.DisplayFor(x => x.SellConditions[index].ConditionName)
</td>
<td></td>
</tr>
}
对于BuyConditions也是一样的规则。