简体   繁体   English

将项目从动态加载的视图中添加到列表中,并将其传递给 asp.net 内核中的 Controller

[英]Add item into List from View Loaded Dynamically and pass it to Controller in asp.net core

I am working on Mobile Store Management System's Order page.我正在处理移动商店管理系统的订单页面。 I want to allow users to select a company through a select list, and then select multiple models of that company from another select list which is loaded dynamically through AJAX. I want to allow users to select a company through a select list, and then select multiple models of that company from another select list which is loaded dynamically through AJAX.

The code for the cascading models is working, but I am unable to send the selected models to the server because it is adding them in the DOM through JavaScript.级联模型的代码正在运行,但我无法将所选模型发送到服务器,因为它通过 JavaScript 将它们添加到 DOM 中。

The following is the code for the cascading selection:以下是级联选择的代码:

<div class="form-group row">
    <label  class="control-label col-6">Company Name</label>
    <div class="col-12">
        <select id="CompanyId"  class="custom-select mr-sm-2"
                asp-items="@(new SelectList( 
                @ViewBag.Companies,"Phoneid","Com_name"))">
            <option value="">Please Select</option>
        </select>
    </div>
    <span class="text-danger"></span>
</div>
<div class="form-group row">
    <label  class="control-label col-6"></label>
    <div class="col-12">
        <select id="modelId" multiple class="custom-select mr-sm-2"
                asp-items="@(new SelectList(string.Empty,"modelId","model_name","--Select--"))">
            <option value="">Please Select</option>
        </select>
    </div>
    <span class="text-danger"></span>
</div> 
<div>
     <input type="button" id="saveBtn" value="Save" />
</div>

Cascading Code:级联代码:

$("#CompanyId").change(async function() 
{
  await $.getJSON("/Order/GetAllModels",{ PhoneId: $("#CompanyId").val()}, 
  function(data) 
  {
    $("#modelId").empty();
    $.each(data, function (index, row) {
        $("#modelId").append("<option value='" + row.modelId + "'>" + 
          row.model_name + '</option>')
    });
  });
}

Once the Save button is clicked, I am displaying the product for the currently selected models using a partial view:单击“保存”按钮后,我将使用局部视图显示当前所选模型的产品:

$('#saveBtn').click(function () {   
  $.ajax({
    url: '/Order/GetProduct?Phoneid=' + $("#CompanyId").val() + "&modelId=" + $('#modelId').val(),
    type: 'Post',
    success: function (data) {
      $('#products').append(data);
    },
  })
})

Problem 1问题 1

When the user selects the first company and their two models, and then clicks the Save button, the partial view loads with indexes i=0,i=1 .当用户选择第一家公司及其两个模型,然后单击“保存”按钮时,部分视图会加载索引i=0,i=1 Then, the user selects another company and selects their models.然后,用户选择另一家公司并选择他们的模型。 Again, the partial view renders with same indexes.同样,局部视图使用相同的索引呈现。 How can I make the indexes unique?如何使索引唯一? This partial view is rendered when the user clicks the Save button, which renders only the current company's selected models.当用户单击“保存”按钮时会呈现此局部视图,该按钮仅呈现当前公司的选定模型。

@model List<Mobile_Store_MS.ViewModel.Orders.Products>
<table class="table">
    <tbody>
        @for (int i = 0; i < Model.Count; i++)
        {
            <tr class="card d-flex">
                <td>
                    <input asp-for="@Model[i].isSelected" />
                </td>
                <td>
                    <input hidden asp-for="@Model[i].Phoneid" />  <input hidden asp-for="@Model[i].modelId" />
                    @Html.DisplayFor(modelItem => Model[i].com_Name)   @Html.DisplayFor(modelItem => Model[i].model_name)
                </td>
                <td>
                    <input asp-for="@Model[i].Quantity" />
                </td>
                <td>
                    <input class="disabled" readonly asp-for="@Model[i].price" />
                </td>
            </tr>
        }
    </tbody>
</table>

Problem 2问题 2

How can I send all of the items rendered through the partial view to the server?如何将通过局部视图呈现的所有项目发送到服务器? I just want to send these selected products along with the quantity and price for each model to the server.我只想将这些选定的产品连同每个 model 的数量和价格一起发送到服务器。 This means binding these items in the product list of the OrderViewModel .这意味着将这些项目绑定到OrderViewModel的产品列表中。

You can find my OrderViewModel and Products model in the following diagram:您可以在下图中找到我的OrderViewModelProducts model:

在此处输入图像描述

Can you tell me how to bind Razor items into a list to post to the controller?你能告诉我如何将 Razor 项目绑定到列表中以发布到 controller 吗? I would be very grateful if you give me some suggestions.如果您能给我一些建议,我将不胜感激。

Related有关的

TL;DR: Instead of relying on the asp-for tag helper, you can set your own name attribute. TL;DR:您可以设置自己的name属性,而不是依赖asp-for标签助手。 This gives you the flexibility to start the index at whatever number you want.这使您可以灵活地以您想要的任何数字开始索引。 Ideally, you will pass the number of existing products to GetProduct() and start indexing off of that.理想情况下,您会将现有产品的数量传递给GetProduct()并开始对其进行索引。 In addition, you also need to prefix your name attribute with Products , thus ensuring those form elements are properly bound to your OrderViewModel.Products collection on post back.此外,您还需要在name属性前加上Products ,从而确保这些表单元素在回发时正确绑定到您的OrderViewModel.Products集合。

<input name="Products[@(startIndex+i)].Quantity" value="@Model[i].Quantity" />

You can then filter the OrderViewModel.Products collection on the server-side using LINQ to limit the results to selected products:然后,您可以使用 LINQ 在服务器端过滤OrderViewModel.Products集合,以将结果限制为选定的产品:

var selectedProducts = c.Products.Where(p => p.IsSelected);

For a more in-depth explanation of how this approach works, as well as some of the variations in the implementation, read my full answer below.要更深入地解释这种方法的工作原理以及实现中的一些变化,请阅读下面的完整答案。


Detailed Answer详细解答

There's a lot going on here, so this is going to be a lengthy answer.这里发生了很多事情,所以这将是一个冗长的答案。 I'm going to start by providing some critical background on how ASP.NET Core MVC connects the dots between your view model, your view, and your binding model, as that will better understand how to adapt this behavior to your needs.我将首先提供一些关于 ASP.NET Core MVC 如何连接您的视图 model、您的视图和您的绑定 model 之间的点的关键背景,因为这将更好地理解如何使这种行为适应您的需求。 Then I'm going to provide a strategy for solving each of your problems.然后我将提供一个策略来解决你的每一个问题。

Note: I'm not going to write all of the code, as that would result in me reinventing a lot of code you've already written—and would make for an even longer answer.注意:我不会编写所有代码,因为这会导致我重新发明你已经编写的许多代码——并且会产生更长的答案。 Instead, I'm going to provide a checklist of steps needed to apply the solution to your existing code.相反,我将提供将解决方案应用于现有代码所需的步骤清单。

Background背景

It's important to note that while ASP.NET Core MVC attempts to standardize and simplify the workflow from view model to view to binding model through conventions (such as the asp-for tag helper) these are each independent of one another.需要注意的是,虽然 ASP.NET Core MVC 试图标准化和简化从视图 model 到视图绑定asp-for的工作流程(例如,这些都是彼此独立的标签,例如)

So when you call asp-for on a collection using eg,因此,当您使用例如在集合上调用asp-for时,

<input asp-for="@Model[i].Quantity" />

It then outputs the something like the following HTML:然后它输出类似于以下 HTML 的内容:

<input id="0__Quantity" name="[0].Quantity" value="1" />

And then, when you submit that, the server looks at your form data, and uses a set of conventions to map that data back to your binding model.然后,当您提交时,服务器查看您的表单数据,并使用一组约定map 将该数据返回到您的绑定 model。 So this might map to:所以这可能 map 到:

public async Task<IActionResult> ProductsAsync(List<Product> products) { … }

When you call asp-for on a collection, it will always start the index at 0 .当您在集合上调用asp-for时,它将始终从0开始索引。 And when it bind the form data to a binding model, it will always start at [0] and count up.并且当它将表单数据绑定到绑定 model 时,它将始终从[0]开始并向上计数。

But there's no reason you need to use asp-for if you need to change this behavior.但是,如果您需要更改此行为,则没有理由需要使用asp-for You can instead set the id and/or name attributes yourself if you need flexibility over how they are generated.如果您需要灵活地生成它们,您可以自己设置id和/或name属性。

Note: When you do this, you'll want to make sure you're still sticking to one of the conventions that ASP.NET Core MVC is already familiar with to ensure data binding occurs.注意:当你这样做时,你需要确保你仍然坚持ASP.NET Core MVC 已经熟悉的约定之一,以确保发生数据绑定。 Though, if you really want to, you can instead create your own binding conventions .不过,如果您真的愿意,您可以改为创建自己的绑定约定

Problem 1: Setting the index问题一:设置索引

Given the above background, if you want to customize the starting index returned from your call to GetProducts() for your second model, you'll want to do something like the following:鉴于上述背景,如果您想为第二个 model 自定义调用GetProducts()返回的起始索引,您需要执行以下操作:

  1. Before calling GetProduct() , determine how many products you already have by eg counting the number of elements with the card class assigned (ie, $(".card").length ).在调用GetProduct()之前,通过例如计算分配有card class 的元素数量(即$(".card").length )来确定您已经拥有多少产品。

    Note: If the card class is not exclusively used for products, you can instead assign a unique class like product to each tr element in your _DisplayOrder view and count that.注意:如果card class 并非专门用于产品,您可以改为将唯一的 class 类似product分配给您的_DisplayOrder视图中的每个tr元素并计算它。

  2. Include this count in your call to GetProduct() as eg, a &startingIndex= parameter:在您对GetProduct()的调用中包含此计数,例如&startingIndex=参数:

$('#saveBtn').click(function () {   
  $.ajax({
    url: '/Order/GetProduct?Phoneid=' + $("#CompanyId").val() + "&modelId=" + $('#modelId').val() + "&startingIndex=" + $("#card").length,
    type: 'Post',
    success: function (data) {
      $('#products').append(data);
    },
  })
})
[HttpPost]
public IActionResult GetProduct(int Phoneid, string[] modelId, int startingIndex = 0) { … }
  1. Relay this startingIndex to your "partial" view via a view model;通过视图 model 将此startingIndex索引中继到您的“部分”视图; eg,例如,
public class ProductListViewModel {
  public int StartingIndex { get; set; }
  public List<Product> Products { get; set; }
}
  1. Use that value to offset the index written to the output:使用该值偏移写入 output 的索引:
<input id="@(Model.StartingIndex+i)__Quantity" name="[@(Model.StartingIndex+i)].Quantity" value="@Model.Products[i].Quantity" />

That's not as tidy as asp-for since you're needing to wire up a lot of similar information, but it offers you the flexibility to ensure that your name values are unique on the page, no matter how many times you call GetProduct() .这不像asp-for那样整洁,因为您需要连接很多类似的信息,但它为您提供了确保您的name值在页面上唯一的灵活性,无论您调用多少次GetProduct() .

Notes笔记

  • If you don't want to create a new view model, you could relay the startingIndex via your ViewData dictionary instead.如果您不想创建新视图 model,则可以改为通过ViewData字典传递startingIndex I prefer having a view model that includes all of the data I need, though.不过,我更喜欢包含我需要的所有数据的视图 model。
  • When using the asp-for tag helper, it automatically generates the id attribute, but if you're not ever referencing it via eg JavaScript you can omit it.使用asp-for标签助手时,它会自动生成id属性,但如果您从未通过例如 JavaScript 引用它,则可以省略它。
  • Browsers will only submit values to the server for form elements that have a name attribute.浏览器只会将具有name属性的表单元素的值提交给服务器。 So if you have input elements that are needed on the client-side but aren't needed in the binding model, for some reason, you can omit the name attribute.因此,如果您有客户端需要但在绑定 model 中不需要的输入元素,出于某种原因,您可以省略name属性。
  • There are other conventions besides {Index}__{Property} that you can follow.除了{Index}__{Property}之外,您还可以遵循其他约定。 But unless you really want to get into the weeds of model binding, you're best off sticking to one of the existing collection conventions .但是,除非您真的想深入了解 model 绑定的杂草,否则最好坚持使用现有的集合约定之一。

Be careful of your indexing!小心你的索引!

In the Model Binding conventions for collections , you'll notice a warning:在 collections 的Model 绑定约定中,您会注意到一个警告:

Data formats that use subscript numbers (... [0]... [1]...) must ensure that they are numbered sequentially starting at zero.使用下标数字 (... [0]... [1]...) 的数据格式必须确保它们从零开始按顺序编号。 If there are any gaps in subscript numbering, all items after the gap are ignored.如果下标编号有任何间隙,则忽略该间隙之后的所有项目。 For example, if the subscripts are 0 and 2 instead of 0 and 1, the second item is ignored.例如,如果下标是 0 和 2 而不是 0 和 1,则忽略第二项。

As such, when assigning these, you need to make sure that they're sequential without any gaps.因此,在分配这些时,您需要确保它们是连续的,没有任何间隙。 If you're using the count ( .length ) of existing eg $(".card") or $(".product") elements on your page to seed the startingIndex value, however, then that shouldn't be a problem.但是,如果您使用页面上现有的例如$(".card")$(".product")元素的计数 ( .length ) 来设置startingIndex值,那么这应该不是问题。

Problem 2: Sending these values to the server问题 2:将这些值发送到服务器

As mentioned above, any form element with a name attribute will have its data submitted to the server.如上所述,任何具有name属性的表单元素都会将其数据提交给服务器。 So it doesn't really matter if you're using asp-for , writing out your form manually using HTML, or constructing it dynamically using JavaScript.因此,如果您使用asp-for ,使用 HTML 手动写出您的表单,或者使用 JavaScript 动态构建它,这并不重要。 If there's a form element with a name attribute, and it's within the form being submitted, it will get included in the payload.如果有一个带有name属性的表单元素,并且它在提交的form中,它将被包含在有效负载中。

Debugging your form data调试表单数据

You're likely already familiar with this, but if not: If you use your browser's developer console, you'll be able to see this information as part of the page metadata when you submit your form.您可能已经对此很熟悉,但如果不熟悉:如果您使用浏览器的开发者控制台,您将能够在提交表单时将此信息作为页面元数据的一部分查看。 For instance, in Google Chrome:例如,在谷歌浏览器中:

  • Go to Developer Tools ( Ctrl + Shift + I ) Go 到开发人员工具Ctrl + Shift + I
  • Go to the Network tab Go 到网络选项卡
  • Submit your form提交您的表格
  • Click on the name of your page (normally the first entry)点击您的页面名称(通常是第一个条目)
  • Go to the Headers tab Go 到标题选项卡
  • Scroll down to the Form Data section (or Query String Parameters for a GET request)向下滚动到表单数据部分(或GET请求的查询字符串参数

You should see something like:您应该会看到如下内容:

  • [0].isSelected true [0].isSelected
  • [0].Phoneid 4 [0].Phoneid 4
  • [0].modelId 10 [0].modelId 10
  • [0].Quantity 5 [0].数量5
  • [0].price 10.50 [0].价格10.50
  • [1].isSelected true [1].isSelected
  • […]… […]…

If you're seeing these in Chrome, but not seeing these data reflected in your ASP.NET Core MVC controller action, then there's a disconnect between the naming conventions of these fields and your binding model—and, thus, ASP.NET Core MVC doesn't know how to map the two.如果您在 Chrome 中看到这些,但在您的 ASP.NET Core MVC controller 操作中没有看到这些数据,那么这些字段的命名约定与您的绑定模型之间存在脱节 - 因此,Z9E0DA8438E1E38A1C30 Core MVC'tCE38A1CZ30 不知道如何map这两个。

Binding Problems绑定问题

There are two likely issues here, both of which might be interfering with your data binding.这里有两个可能的问题,它们都可能会干扰您的数据绑定。

Duplicate Indexes重复索引

Since you are currently submitting duplicate indexes, that could be causing collisions with the data.由于您当前正在提交重复的索引,这可能会导致与数据发生冲突。 Eg, if there are two values for [0].Quantity , then it will retrieve those as an array—and may fail to bind either value to eg the int Quantity property on your Products binding model.例如,如果[0].Quantity有两个值,那么它将以数组的形式检索这些值,并且可能无法将任一值绑定到例如绑定 model 的Products上的int Quantity属性。 I haven't tested this, though, so I'm not entirely sure how ASP.NET Core MVC deals with this situation.不过,我还没有对此进行测试,所以我不完全确定 ASP.NET Core MVC 是如何处理这种情况的。

Collection Name集合名称

When you bind to a List<Products> with the asp-for tag helper, I believe it will use the [i].Property naming convention.当您使用asp-for标签助手绑定到List<Products>时,我相信它将使用[i].Property命名约定。 That's a problem because your OrderViewModel is not a collection.这是一个问题,因为您的OrderViewModel不是一个集合。 Instead, these needs to be associated with the Products property on your binding model.相反,这些需要与绑定 model 上的Products属性相关联。 That can be done by prefixing the name with Products .这可以通过在name前加上Products来完成。 This will be done automatically if you use asp-for to bind to a Products property on your view model—as proposed on the ProductListViewModel above.如果您使用asp-for绑定到视图模型上的Products属性,这将自动完成 - 正如上面ProductListViewModel中所建议的那样。 But since you need to dynamically generate the name 's based on the IndexOffset anyway, you can just hard-code these as part of your template:但是由于无论如何您都需要根据IndexOffset动态生成name ,因此您可以将这些硬编码为模板的一部分:

<input id="Products_@(Model.StartingIndex+i)__Quantity" name="Products[@(Model.StartingIndex+i)].Quantity" value="@Model.Products[i].Quantity" />

Selected Products精选产品

There's still a problem, though!不过还是有问题! This is going to include all of your products—even if they weren't otherwise selected by the user.这将包括您的所有产品——即使它们不是由用户选择的。 There are a number of strategies for dealing with this, from dynamically filtering them on the client, to creating a custom model binder that first validates the Products_[i]__isSelected attribute.有很多策略可以解决这个问题,从在客户端动态过滤它们,到创建一个自定义 model 绑定器,首先验证Products_[i]__isSelected属性。 The easiest, though, is to simply allow all of them to be bound to your binding model, and then filter them prior to any processing using eg LINQ:不过,最简单的方法是简单地将它们全部绑定到您的绑定 model,然后在任何处理之前使用例如 LINQ 过滤它们:

var selectedProducts = c.Products.Where(p => p.IsSelected).ToList();
…
repo.SetProducts(selectedProducts);

For the 1st question, you can try different things.对于第一个问题,您可以尝试不同的事情。 When you do the ajax call, you get a list of models.当您调用 ajax 时,您会得到一个模型列表。 For each of these models, add the selected company ID as a property.对于这些模型中的每一个,将选定的公司 ID 添加为属性。 So you don't have to worry about the index being something unique.所以你不必担心索引是独一无二的。

As for the 2nd question, should be a relatively easy thing to do.至于第二个问题,应该是比较容易做的事情。 However, more information is needed.但是,需要更多信息。 1. When the save button is hit, are you doing a full postback? 1.当保存按钮被点击时,你是在做一个完整的回发吗? or it is also an AJAX call?或者它也是一个 AJAX 电话? 2. Why do you not want to opt for a AJAX call to do the update as well? 2. 为什么您不想同时选择 AJAX 调用来进行更新? So you can based upon the response, redirect the user to a results page, etc.因此,您可以根据响应,将用户重定向到结果页面等。

If you can create a small sample in a new project, and upload to github, and post the information here.如果你可以在新项目中创建一个小样本,并上传到github,并在此处发布信息。 I should be able to take a look and understand better.我应该可以看一下并更好地理解。 I will definitely be able to help.我一定能提供帮助。

Also try reading this thread, it might help也尝试阅读这个线程,它可能会有所帮助

how to persist partial view model data during postback in asp.net mvc 如何在 asp.net mvc 回发期间保留部分视图 model 数据

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

相关问题 ASP.NET 内核如何通过列表<int>从视图到 Controller</int> - ASP.NET Core how to pass List<int> from view to Controller 将对象列表从视图传递到asp.net中的控制器 - pass list of objects from view to controller in asp.net 显示列表并在列表中添加新项目 asp.net 内核 - Display a list and add new item in on list asp.net core ASP.NET Core MVC - 数据没有从视图传递到 controller 在 post 请求中 - ASP.NET Core MVC - Data does not pass from the view to the controller in a post request ASP.NET MVC Core TextBoxFor event pass model from View to Controller - ASP.NET MVC Core TextBoxFor event pass model from View to Controller ASP.NET 内核 - 如何将 Javascript 变量从视图传递到 Controller - ASP.NET Core - How to pass a Javascript variable from View to Controller 在 C# ASP.NET Core MVC 中使用 AJAX 将数据从视图传递到控制器 - Pass data from view to controller using AJAX in C# ASP.NET Core MVC 如何将任何选定的选项值从视图传递到 controller ASP.NET Core - How to pass any selected option value from a view to a controller ASP.NET Core asp.net核心-如何将变量从控制器传递到过滤器 - asp.net core - How to pass variable from controller to filter 如何使用带有VS RC 2017的ASP.NET Core从控制器添加自动视图 - How can i add an automatic View from controller using ASP.NET Core with VS RC 2017
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM