简体   繁体   English

如何手动将数据从视图传递到控制器?

[英]How do I manually pass data from view to controller?

I have a view with a form that can be edited. 我有一个可以编辑的表单视图。 The view is rather complicated. 该视图相当复杂。 The form is for a contract. 该表格是一份合同。 The contract name can be edited at the top. 合同名称可以在顶部进行编辑。 A contract has many ads, and the details for each of those ads can be edited. 合同中有很多广告,并且每个广告的详细信息都可以修改。 The length of the form depends on how many ads a contract has. 表格的长度取决于合同中有多少个广告。 I have a for loop in the view that displays all the form fields for each ad. 我在视图中有一个for循环,可显示每个广告的所有表单字段。

Furthermore, there are some fields in the model that are used to determine some of the ad details, but themselves shouldn't be saved to the database (yet they must appear as, say, dropdowns in the view). 此外,模型中的某些字段用于确定某些广告详细信息,但不应将其保存到数据库中(但它们必须在视图中显示为下拉列表)。

Also, there are some model fields that don't need to appear in the view but are needed in the POST controller method in order to save the database properly (I'm saving it manually), so they must go through the view somehow and be passed back to the controller. 另外,有些模型字段不需要显示在视图中,但在POST控制器方法中是必需的,以便正确保存数据库(我是手动保存),因此它们必须以某种方式遍历视图并被传递回控制器。 Here's my model: 这是我的模型:

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
}

The fields that have a comment of "save" after them should be saved to the database. 注释为“保存”的字段应保存到数据库中。 The ones with "appear" must be in the view but are not needed in the POST controller method. 带有“出现”的内容必须在视图中,但POST控制器方法中不需要。 The ones with "needed" are needed in the POST controller method but not the view. POST控制器方法中需要“需要”的内容,而视图则不需要。

By the way, these model classes don't map one-to-one with database tables, they're made from several different tables. 顺便说一下,这些模型类不会与数据库表一对一地映射,它们是由几个不同的表组成的。

As far as I know I don't see how I can get model binding to work with this. 据我所知,我还看不到如何获得模型绑定来使用它。 The model that's passed back to the controller has a null value for the ads field of ModContract, for example. 例如,传递回控制器的模型的ModContract的ads字段的值为空。 I would like to know how I can manually pass what I want from the view back to the controller without relying on model binding. 我想知道如何在不依赖模型绑定的情况下,将视图中所需的内容手动传递回控制器。

I know I might use @Html.HiddenFor for the ids needed in the POST method but not the view, but for some reason when I tried it this with the contract_id and _name, it didn't work. 我知道我可以使用@ 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>

The contract_id that was returned in the POST method was 0. 在POST方法中返回的contract_id为0。

Let me know if my question sounds stupid because this is impossible or because I'm overcomplicating things, I'm pretty new to all this. 让我知道如果我的问题听起来很愚蠢,是因为这是不可能的,或者是因为我过于复杂,那么我对这一切还是很陌生的。

Thanks in advance! 提前致谢!

EDIT 编辑

Here's the view. 这是视图。 I can't get it to indent right but hopefully it's readable. 我无法使其缩进正确,但希望它是可读的。

@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
    });
}

You have quite a few issues there and understandably if you have a complex form. 您那里有很多问题,如果表单很复杂,这是可以理解的。 So let me try and summarize your problem to give the best possible solution. 因此,让我尝试总结您的问题,以提供最佳解决方案。

The Breakdown 细目分类

there are some fields in the model...(that) themselves shouldn't be saved to the database (yet they must appear as, say, dropdowns in the view) 模型中有一些字段...本身不应该保存到数据库中(但是它们必须在视图中显示为下拉列表)

You put these fields in the model and treat them as read-only . 您将这些字段放入模型中,并将其视为只读 I'll show later how. 稍后再说明。

there are some model fields that don't need to appear in the view but are needed in the POST controller method in order to save the database properly (I'm saving it manually) 有一些模型字段不需要显示在视图中,但在POST控制器方法中是必需的,以便正确保存数据库(我正在手动保存)

You don't need to do that. 您不需要这样做。 You are passing unnecessary data around and you are introducing over posting. 您正在传递不必要的数据,并且正在引入过帐。 In addition, you are not allowing your users to edit them and yet you pass it to the client, receive it, and save it back to the database. 此外,您不允许用户对其进行编辑,但是您将其传递给客户端,接收它并将其保存回数据库。

It even exposes a security hole in your system. 它甚至暴露了系统中的安全漏洞。 For example, you are passing product_name to the view, don't want it to be edited, but then save it to another table. 例如,您要将product_name传递给视图,不希望对其进行编辑,但是将其保存到另一个表中。 Someone can easily hack that value and you could end up with a product_name that you never expected. 有人可以轻松破解该价值,而您最终可能会获得一个您从未想到的product_name

The fields that have a comment of "save" after them should be saved to the database. 注释为“保存”的字段应保存到数据库中。

Accept these as inputs on your form. 接受这些作为您表单上的输入。 Code will be shown later. 代码将在稍后显示。

The ones with "appear" must be in the view but are not needed in the POST controller method. 带有“出现”的内容必须在视图中,但POST控制器方法中不需要。

As I've mentioned these should be read-only fields on your viewmodel, code shown later. 正如我已经提到的,这些应该是您的viewmodel上的只读字段,稍后将显示代码。

The ones with "needed" are needed in the POST controller method but not the view. POST控制器方法中需要“需要”的内容,而视图则不需要。

You don't need to pass this around nor query it right away. 您无需传递此信息,也无需立即对其进行查询。 Get it later from your database when saving-time comes. 节省时间以后,可以从数据库中获取它。 Pseudo-code shown later. 伪代码稍后显示。

The Proposed Solution 拟议的解决方案

At this point I assume you understand what I mentioned above by answering some points of your question. 在这一点上,我认为您已经通过回答问题的某些点来理解了我上面提到的内容。 Here are the relevant codes to those explanation. 以下是与这些说明相关的代码。

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;}
}

You will create a "master" viewmodel called ContractViewModel and use it on your view (of course you can have any name you want). 您将创建一个名为ContractViewModel的“主”视图模型,并在视图上使用它(当然,您可以使用任何想要的名称)。 It contains the fields to show information and an "input" field to capture all your inputs. 它包含用于显示信息的字段和用于捕获所有输入的“输入”字段。 It is a flatten representation of all your entities. 它是所有实体的扁平表示。 The "input" field, called InputModel is another flatten representation of all your entities. 称为InputModel的“输入”字段是所有实体的另一个扁平表示。 The inputs from that viewmodel will be dispersed and persisted properly. 该视图模型的输入将被分散并正确保留。

Building The Model 建立模型

// 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);
}

Consuming the Model on your View 在视图上使用模型

The example given here also solve this problem: 此处给出的示例也解决了此问题:

As far as I know I don't see how I can get model binding to work with this. 据我所知,我还看不到如何获得模型绑定来使用它。 The model that's passed back to the controller has a null value for the ads field of ModContract 传回控制器的模型的ModContract广告字段的值为空

Never use a foreach on a collection as the razor engine cannot properly write it in a way that you can save it back. 切勿在集合上使用foreach ,因为剃刀引擎无法以可以将其保存回去的方式正确写入它。 The reason for this is that the elements written have the same name and they cannot be properly binded (or bound, I think is the right word in English - binded in programming :). 这样做的原因是所写的元素具有相同的名称,并且不能正确地绑定(或绑定,我认为这是英语中的正确单词-在编程中绑定:)。 Always use a for-loop , see it in the following code. 始终使用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)
}

Finally, Saving Just What You Need 最后,节省您所需的一切

In the example given below you will notice we used the Bind attribute. 在下面给出的示例中,您将注意到我们使用了Bind属性。 That tells the controller to only bind and get our "input" model and that is the InputModel . 这告诉控制器仅绑定并获取我们的“输入”模型,即InputModel There is no need to post back all the other values that you won't need for saving or for querying, etc. So take note that if you need a field, say an id (eg product id) that you will later on use to query a product from the product table, because you need to save its name to the ModAds table then include that in the InputModel . 无需回传保存或查询等不需要的所有其他值。因此请注意,如果您需要一个字段,请说出一个id (例如产品ID),以备日后使用从产品表中查询产品, 因为您需要将其名称保存到ModAds表中,然后将其包含在InputModel So that you will have, InputModel.ProductId . 这样您将拥有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
}

Conclusion 结论

That is the closest example that I can give you based on your model and what you are trying to do. 这是我可以根据您的模型和您要执行的操作提供的最接近的示例。 If you follow along carefully with that example, I'm sure it will solve your problem. 如果您仔细遵循该示例,我相信它将解决您的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM