[英]How do I manually pass data from view to controller?
我有一个可以编辑的表单视图。 该视图相当复杂。 该表格是一份合同。 合同名称可以在顶部进行编辑。 合同中有很多广告,并且每个广告的详细信息都可以修改。 表格的长度取决于合同中有多少个广告。 我在视图中有一个for循环,可显示每个广告的所有表单字段。
此外,模型中的某些字段用于确定某些广告详细信息,但不应将其保存到数据库中(但它们必须在视图中显示为下拉列表)。
另外,有些模型字段不需要显示在视图中,但在POST控制器方法中是必需的,以便正确保存数据库(我是手动保存),因此它们必须以某种方式遍历视图并被传递回控制器。 这是我的模型:
public class ModContract
{
public int contract_id; // pk; needed
public string contract_name { get; set; } // save
public List<ModAds> ads { get; set; }
}
public class ModAds
{
public int contr_ad_id; // pk; needed
public string name { get; set; } // save
public string print_product_id { get; set; } // used for product_name
public string product_name { get; set; } // appear
public string print_ad_option_id { get; set; } // save
public string adv_product { get; set; } // appear
public List<string> editions { get; set; } // save
public double freq_disc { get; set; } // save
public double other_dis_dol { get; set; } // save
public double? other_dis_per { get; set; } // save
public string non_cash_note { get; set; } // save
public double non_cash_cons { get; set; } // save
}
注释为“保存”的字段应保存到数据库中。 带有“出现”的内容必须在视图中,但POST控制器方法中不需要。 POST控制器方法中需要“需要”的内容,而视图则不需要。
顺便说一下,这些模型类不会与数据库表一对一地映射,它们是由几个不同的表组成的。
据我所知,我还看不到如何获得模型绑定来使用它。 例如,传递回控制器的模型的ModContract的ads字段的值为空。 我想知道如何在不依赖模型绑定的情况下,将视图中所需的内容手动传递回控制器。
我知道我可以使用@ Html.HiddenFor作为POST方法中所需的ID,但不能使用视图,但是由于某种原因,当我使用contract_id和_name尝试此操作时,它没有用。
<li data-role="fieldcontain">
<label>Contract Name</label>
@Html.HiddenFor(m => m.contract_id)
@Html.EditorFor(m => m.contract_name)
</li>
在POST方法中返回的contract_id为0。
让我知道如果我的问题听起来很愚蠢,是因为这是不可能的,或者是因为我过于复杂,那么我对这一切还是很陌生的。
提前致谢!
编辑
这是视图。 我无法使其缩进正确,但希望它是可读的。
@model oulookmediaweb.Models.ModContract
@{
ViewBag.Title = "ModifyContract";
}
@{
var num = 1;
}
<h2>Modify Contract</h2>
@using (Html.BeginForm("ModifyContract", "Contract", FormMethod.Post, htmlAttributes: new { data_ajax = "false" }))
{
@Html.ValidationSummary();
<ul data-role="listview" data-inset="true">
<li data-role="fieldcontain">
<label>Contract Name</label>
@Html.HiddenFor(m => m.contract_id)
@Html.EditorFor(m => m.contract_name)
</li>
</ul>
<div>
@foreach (var ad in Model.ads)
{
<div id="@num">
<ul data-role="listview" data-inset="true">
<li data-role="list-divider">@ad.name</li>
<li data-role="fieldcontain">
<label><strong>Product Name</strong></label>
<div>@ad.product_name</div>
<div id="@num-drdn0" hidden="true">@Html.DropDownListFor(m => ad.print_product_id, ViewData["products_list"] as SelectList)</div>
<input id="@num-editbutton" type="button" onclick="edit(@num)" value="Edit" />
</li>
<li data-role="fieldcontain">
<label><strong>Advertising Product</strong></label>
<div>@ad.adv_product</div>
<div id="@num-drdn1" class="hid">
<select></select>
</div>
</li>
<li data-role="fieldcontain">
<label><strong>Editions to run in:</strong></label>
@foreach (var ed in ad.editions)
{
<div>@ed</div>
}
<div id="@num-drdn2" class="hid">
<select multiple="multiple" data-native-menu="false"></select>
</div>
</li>
<li data-role="fieldcontain">
<label><strong>Frequency Discount (%)</strong></label>
<div>@Html.EditorFor(m => ad.freq_disc)</div>
</li>
<li data-role="fieldcontain">
<label><strong>Other Discount ($)</strong></label>
<div>@Html.EditorFor(m => ad.other_dis_dol)</div>
</li>
<li data-role="fieldcontain">
<label><strong>Other Discount (%)</strong></label>
<div>@Html.EditorFor(m => ad.other_dis_per)</div>
</li>
<li data-role="fieldcontain">
<label><strong>Non Cash Note</strong></label>
<div>@Html.EditorFor(m => ad.non_cash_note)</div>
</li>
<li data-role="fieldcontain">
<label><strong>Non Cash Consideration</strong></label>
<div>@Html.EditorFor(m => ad.non_cash_cons)</div>
</li>
</ul>
@{num++;}
</div>
}
</div>
<ul data-role="listview" data-inset="true">
<li data-role="fieldcontain">
<input type="submit" data-theme="e" value="Submit" />
</li>
</ul>
}
<script type="text/javascript">
var nu;
window.onload = function () {
// hide adv prods and editions select for now
$(".hid select").closest('.ui-select').hide();
}
// called when the edit button for product is clicked
function edit(num) {
nu = num;
$("#" + nu + "-drdn0").show(); // show dropdown
$("#" + nu + "-editbutton").closest('.ui-btn').hide(); // hide edit button; '.ui-btn'? WTF?
// remove current product selection div
// remove adv product selection div
// remove editions div
$("#" + nu + "-drdn0 select").change(prodChange); // on select change
$("#" + nu + "-drdn0 select").trigger("change"); // trigger for default; happens twice; WHY?
}
// called when a magazine is selected
function prodChange() {
// ajax
var url = '@Url.Action("GetAdvProdList", "Contract")' + '?prod_id=' + this.value;
$.getJSON(url, null, function (list) {
// for adv list dropdown
$("#" + nu + "-drdn1 select").empty(); // remove old stuff
$("#" + nu + "-drdn1 select").closest('.ui-select').show(); // show dropdown
var arr = list.advlist; // get the array from object
$.each(arr, function (ind, val) {
// add each item in list as an option of select
$("#" + nu + "-drdn1 select").append('<option value=' + val.Value + '>' + val.Text + '</option>');
});
$("#" + nu + "-drdn1 select").selectmenu('refresh', true); // refresh menu
// for ed list
$("#" + nu + "-drdn2 select").empty(); // remove old stuff
$("#" + nu + "-drdn2 select").closest('.ui-select').show(); // show list
var lis = list.edlist; // get the array from object
$.each(lis, function (ind, val) {
// add each item to list
$("#" + nu + "-drdn2 select").append('<option value=' + val.Value + '>' + val.Text + '</option>');
});
$("#" + nu + "-drdn2 select").selectmenu('refresh', true); // refresh menu
});
}
您那里有很多问题,如果表单很复杂,这是可以理解的。 因此,让我尝试总结您的问题,以提供最佳解决方案。
模型中有一些字段...本身不应该保存到数据库中(但是它们必须在视图中显示为下拉列表)
您将这些字段放入模型中,并将其视为只读 。 稍后再说明。
有一些模型字段不需要显示在视图中,但在POST控制器方法中是必需的,以便正确保存数据库(我正在手动保存)
您不需要这样做。 您正在传递不必要的数据,并且正在引入过帐。 此外,您不允许用户对其进行编辑,但是您将其传递给客户端,接收它并将其保存回数据库。
它甚至暴露了系统中的安全漏洞。 例如,您要将product_name
传递给视图,不希望对其进行编辑,但是将其保存到另一个表中。 有人可以轻松破解该价值,而您最终可能会获得一个您从未想到的product_name
。
注释为“保存”的字段应保存到数据库中。
接受这些作为您表单上的输入。 代码将在稍后显示。
带有“出现”的内容必须在视图中,但POST控制器方法中不需要。
正如我已经提到的,这些应该是您的viewmodel上的只读字段,稍后将显示代码。
POST控制器方法中需要“需要”的内容,而视图则不需要。
您无需传递此信息,也无需立即对其进行查询。 节省时间以后,可以从数据库中获取它。 伪代码稍后显示。
在这一点上,我认为您已经通过回答问题的某些点来理解了我上面提到的内容。 以下是与这些说明相关的代码。
public class ContractViewModel {
// start:read-only fields.
// These are the fields that will be used as read-only
public string product_name { get; set; }
public string adv_product { get; set; }
public IEnumerable<SelectListItem> PrintAdOptions { get; set; }
// end:read-only fields.
public InputModel Contract {get;set;}
}
// you use this class to capture "all your inputs"
public class InputModel {
public int contract_id {get;set;}
public string contract_name { get; set; }
public IEnumerable<InputSubModel> ads {get;set;}
}
public class InputSubModel {
public int contr_ad_id {get;set;}
public string print_ad_option_id {get;set;} // is this really a string?
public string name {get;set;}
}
您将创建一个名为ContractViewModel
的“主”视图模型,并在视图上使用它(当然,您可以使用任何想要的名称)。 它包含用于显示信息的字段和用于捕获所有输入的“输入”字段。 它是所有实体的扁平表示。 称为InputModel
的“输入”字段是所有实体的另一个扁平表示。 该视图模型的输入将被分散并正确保留。
// method for creating a contract
public ActionResult Create() {
// implement the ff methods, whatever method you're using ORM, plain ADO, etc.
var product = getEntityFromYourDb();
// you may eagerly load this (ORM) or query it separately, it's up to you
// AdOption is a simple entity with structure: Id, Name
IEnumerable<AdOption> adOptions = getAdOptionsFromDb();
// map entity to model
// I am guessing here that "product" has the following fields.
// Just replace this accordingly based on you entities
var model = new ContractViewModel{
product_name = product.Name,
adv_product = product.AdvProduct,
PrintAdOptions = aOptions.Select(x=>
new SelectListItem{ Value = x.Id, Text = x.Name })
Contract = getEntitiesFromDbAndMapToModel();
};
// at this point you have everything you need
// (1) a field that you can just show (and not save)
// (2) a list of fields that you can use on a drop-down
// (3) input fields, the ONLY ones that you will POST
return View(model);
}
此处给出的示例也解决了此问题:
据我所知,我还看不到如何获得模型绑定来使用它。 传回控制器的模型的ModContract广告字段的值为空
切勿在集合上使用foreach
,因为剃刀引擎无法以可以将其保存回去的方式正确写入它。 这样做的原因是所写的元素具有相同的名称,并且不能正确地绑定(或绑定,我认为这是英语中的正确单词-在编程中绑定:)。 始终使用for-loop
,请在以下代码中查看。
@model ContractViewModel
<h2>Modify Contract</h2>
<p>@Model.product_name</p>
<p>@Model.adv_product</p>
@Html.HiddenFor(m=>m.Contract.contract_id) // your reference Id for saving
@Html.TextBoxFor(m=>m.Contract.contract_name)
@for(var i=0;i<Model.Contract.ads.Count();i++){
@Html.HiddenFor(m=>m.Contract.ads[i].contr_ad_id) // your reference Id for saving
@Html.TextBoxFor(m=>m.Contract.ads[i].name)
@Html.DropdownListFor(m=>m.Contract.ads[i].print_ad_option_id, m.PrintAdOptions)
}
在下面给出的示例中,您将注意到我们使用了Bind
属性。 这告诉控制器仅绑定并获取我们的“输入”模型,即InputModel
。 无需回传保存或查询等不需要的所有其他值。因此请注意,如果您需要一个字段,请说出一个id
(例如产品ID),以备日后使用从产品表中查询产品, 因为您需要将其名称保存到ModAds
表中,然后将其包含在InputModel
。 这样您将拥有InputModel.ProductId
。
[HttpPost]
public ActionResult Create([Bind(Prefix = "Contract")]InputModel model) {
// map back the values from the flatten model into your entities
// it could be a single entity or a complex one
// but I'm sure you can easily determine how to do it
// as an example:
var modContract = getContractFromDb(model.contract_id);
modContract.contract_name = model.contract_name;
// do the rest of the mapping here
}
这是我可以根据您的模型和您要执行的操作提供的最接近的示例。 如果您仔细遵循该示例,我相信它将解决您的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.