0

我有一个可以编辑的表单视图。观点比较复杂。该表格用于合同。合同名称可以在顶部进行编辑。一份合同有很多广告,每个广告的详细信息都可以编辑。表格的长度取决于合同有多少广告。我在视图中有一个 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
    });
}

4

1 回答 1

3

如果你有一个复杂的表格,你会遇到很多问题,这是可以理解的。因此,让我尝试总结您的问题,以提供最佳解决方案。

故障

模型中有一些字段......(它们)本身不应该保存到数据库中(但它们必须显示为视图中的下拉列表)

您将这些字段放入模型中并将它们视为只读。我稍后会展示如何。

有一些模型字段不需要出现在视图中,但在 POST 控制器方法中需要才能正确保存数据库(我手动保存)

你不需要这样做。您正在传递不必要的数据,并且您正在引入过度发布。此外,您不允许您的用户编辑它们,但您将其传递给客户端,接收它并将其保存回数据库。

它甚至会暴露系统中的安全漏洞。例如,您正在传递product_name到视图,不希望对其进行编辑,但随后将其保存到另一个表中。有人可以轻松破解该值,而您最终可能会得到product_name您从未预料到的结果。

后面带有“保存”注释的字段应保存到数据库中。

接受这些作为表单上的输入。代码将在稍后显示。

带有“出现”的必须在视图中,但在 POST 控制器方法中不需要。

正如我所提到的,这些应该是视图模型上的只读字段,代码稍后显示。

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 的 ads 字段有一个空值

永远不要在集合上使用 a foreach,因为 razor 引擎无法以您可以将其保存回来的方式正确写入它。原因是编写的元素具有相同的名称并且它们无法正确绑定(或绑定,我认为是英语中的正确单词 - 在编程中绑定:)。始终使用 a 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
}

结论

这是我可以根据您的模型和您正在尝试做的事情为您提供的最接近的示例。如果您仔细按照该示例进行操作,我相信它会解决您的问题。

于 2013-04-18T03:14:36.897 回答