2011-12-22 18 views
37

Có đúng là trình kết nối mô hình mặc định trong MVC 3.0 có khả năng xử lý các chỉ mục không tuần tự (cho cả kiểu mô hình đơn giản và phức tạp)? Tôi đã đi qua bài viết cho thấy nó nên, tuy nhiên trong các bài kiểm tra của tôi có vẻ như nó không.MVC3 Chỉ số không tuần tự và DefaultModelBinder

Với bài trở lại giá trị:

items[0].Id = 10 
items[0].Name = "Some Item" 
items[1].Id = 3 
items[1].Name = "Some Item" 
items[4].Id = 6 
items[4].Name = "Some Item" 

Và một phương pháp điều khiển:

public ActionResult(IList<MyItem> items) { ... } 

Các giá trị duy nhất mà được nạp là mục 0 và 1; mục 4 chỉ đơn giản là bỏ qua.

Tôi đã thấy nhiều giải pháp để tạo chỉ mục tùy chỉnh (Model Binding to a List), tuy nhiên tất cả đều xuất hiện để nhắm mục tiêu các phiên bản trước của MVC và hầu hết là IMO 'nặng tay'.

Tôi có thiếu gì đó không?

Trả lời

63

tôi đã làm việc này, bạn cần phải nhớ để thêm một chỉ mục phổ biến đầu vào ẩn như được giải thích trong bài viết tham chiếu của bạn:

Các đầu vào ẩn với name = Items.Index là phần quan trọng

<input type="hidden" name="Items.Index" value="0" /> 
<input type="text" name="Items[0].Name" value="someValue1" /> 

<input type="hidden" name="Items.Index" value="1" /> 
<input type="text" name="Items[1].Name" value="someValue2" /> 

<input type="hidden" name="Items.Index" value="3" /> 
<input type="text" name="Items[3].Name" value="someValue3" /> 

<input type="hidden" name="Items.Index" value="4" /> 
<input type="text" name="Items[4].Name" value="someValue4" /> 

hy vọng điều này giúp

+0

Tôi đã hy vọng để tránh cách tiếp cận này. Tôi đã có những ngón tay của tôi vượt qua rằng các mô hình mặc định chất kết dính sẽ chỉ đơn giản là giải quyết các indice mất tích chính nó. Phải có một lý do (có lẽ cho các tình huống phức tạp hơn?) Vì phải xác định rõ ràng chỉ mục. Dù bằng cách nào, cảm ơn cho trả lời nhanh chóng và mã mẫu. – mindlessgoods

+1

Cảm ơn bạn, hoạt động hoàn hảo! – Levitikon

+4

OH MY NUL! Điều này chỉ làm trở lại danh sách SO dễ dàng hơn nhiều, thay vì làm vô lý cho (i + +) tôi chỉ có thể sử dụng khóa chính hoặc bất kỳ ID khác trên chỉ mục, và danh sách trở lại rất tốt đẹp, và mạnh mẽ đánh máy. Những bí mật đen tối ẩn giấu này. Điều này làm cho ngày của tôi! 1 + bia !!! – ppumkin

4

Bài viết bạn tham chiếu là cũ (MVC2), nhưng theo như tôi biết, đây vẫn là cách defacto để mô hình hóa các bộ sưu tập liên kết bằng cách sử dụng modelbinder mặc định.

Nếu bạn muốn lập chỉ mục không tuần tự, như Bassam nói, bạn sẽ cần chỉ định một người lập chỉ mục. Trình chỉ mục không cần phải là số.

Chúng tôi sử dụng Steve Sanderson's BeginCollectionItem Html Helper cho việc này. Nó tự động tạo chỉ mục làm Hướng dẫn. Tôi nghĩ rằng đây là một cách tiếp cận tốt hơn so với sử dụng các chỉ mục số khi HTML của mục bộ sưu tập của bạn không phải là tuần tự.

+0

Tôi đã xem qua cùng một bài viết và nó chắc chắn đã giải quyết được vấn đề tôi đang mô tả. Như đã đề cập ở trên, tôi đã hy vọng rằng trình mô hình hóa mặc định sẽ xử lý tình huống này trong nội bộ và trình trợ giúp BeginCollectionItem không thực sự cần thiết. Cảm ơn vi đa trả lơi! – mindlessgoods

5

Phương pháp trợ giúp này, bắt nguồn từ cách tiếp cận của Steve Sanderson, đơn giản hơn nhiều và có thể được sử dụng để neo bất kỳ mục nào trong bộ sưu tập và dường như làm việc với mô hình MVC ràng buộc.

public static IHtmlString AnchorIndex(this HtmlHelper html) 
{ 
    var htmlFieldPrefix = html.ViewData.TemplateInfo.HtmlFieldPrefix; 
    var m = Regex.Match(htmlFieldPrefix, @"([\w]+)\[([\w]*)\]"); 
    if (m.Success && m.Groups.Count == 3) 
     return 
      MvcHtmlString.Create(
       string.Format(
        "<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", 
        m.Groups[1].Value, m.Groups[2].Value)); 
    return null; 
} 

Ví dụ: Đơn giản chỉ cần gọi nó trong một EditorTemplate, hoặc bất cứ nơi nào khác bạn sẽ tạo ra các yếu tố đầu vào, như sau để tạo ra các chỉ số neo đậu ẩn biến nếu một là áp dụng.

@model SomeViewModel 
@Html.AnchorIndex() 
@Html.TextBoxFor(m => m.Name) 
... etc. 

Tôi nghĩ rằng nó có một số lợi thế so với cách tiếp cận của Steve Sanderson.

  1. Nó hoạt động với EditorFor và các cơ chế sẵn có khác để xử lý số liệu. Vì vậy, nếu Items là một tài sản trên một mô hình xem IEnumerable<T>, các công việc sau như mong đợi:

    <ul id="editorRows" class="list-unstyled"> @Html.EditorFor(m => m.Items) @* Each item will correctly anchor allowing for dynamic add/deletion via Javascript *@ </ul>

  2. Nó là đơn giản và không đòi hỏi bất kỳ dây ma thuật hơn.

  3. Bạn có thể có một EditorTemplate/DisplayTemplate duy nhất cho một kiểu dữ liệu và nó sẽ chỉ đơn giản là không có op nếu không được sử dụng trên một mục trong danh sách.

Nhược điểm duy nhất là nếu mô hình gốc bị ràng buộc là đếm được (tức là tham số cho phương thức hành động riêng của mình và không chỉ đơn giản là một tài sản ở đâu đó sâu trong đồ thị đối tượng tham số), các ràng buộc sẽ thất bại tại chỉ số không tuần tự đầu tiên. Thật không may, chức năng .Index của DefaultModelBinder chỉ hoạt động cho các đối tượng không phải root. Trong trường hợp này, lựa chọn duy nhất của bạn vẫn là sử dụng các phương pháp trên.

+0

Rất cám ơn Phil này. Tôi đã cam kết sử dụng EditorFor với các bảng kê và điều này làm việc như một sự quyến rũ! – Jono

1

Hoặc sử dụng chức năng javascript này để sửa chữa việc lập chỉ mục: (Thay thế Tên pháp nhân và FieldName rõ ràng)

function fixIndexing() { 
     var tableRows = $('#tblMyEntities tbody tr'); 

     for (x = 0; x < tableRows.length; x++) { 
      tableRows.eq(x).attr('data-index', x); 

      tableRows.eq(x).children('td:nth-child(1)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName1"); 

      tableRows.eq(x).children('td:nth-child(2)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName2"); 

      tableRows.eq(x).children('td:nth-child(3)').children('input:first').attr('name', 'EntityName[' + x + "].FieldName3"); 
     } 

     return true; //- Submit Form - 
    } 
2

Tôi đã đấu tranh với điều này trong tuần này và câu trả lời Bassam là chìa khóa để nhận được tôi đi đúng hướng. Tôi có danh sách động các mục khoảng không quảng cáo có thể có trường số lượng. Tôi cần biết có bao nhiêu mục trong số họ đã chọn, ngoại trừ danh sách các mục có thể thay đổi từ 1 đến n.

Giải pháp của tôi khá đơn giản cuối cùng. Tôi tạo ra một ViewModel được gọi là ItemVM với hai thuộc tính. ItemID và số lượng. Trong bài viết, tôi chấp nhận một danh sách những điều này. Với tính năng Lập chỉ mục, tất cả các mục sẽ được chuyển đi .. ngay cả với số lượng không. Bạn phải xác nhận và xử lý nó phía máy chủ, nhưng với iteration nó tầm thường để xử lý danh sách động này.

Trong Xem của tôi, tôi đang sử dụng một cái gì đó như thế này:

@foreach (Item item in Items) 
{ 
<input type="hidden" name="OrderItems.Index" value="@item.ItemID" /> 
<input type="hidden" name="OrderItems[@item.ItemID].ItemID" value="@item.ItemID" /> 
<input type="number" name="OrderItems[@item.ItemID].Quantity" /> 
} 

này mang lại cho tôi một danh sách với chỉ số 0-based, nhưng lặp đi lặp lại trong bộ điều khiển trích xuất toàn bộ dữ liệu cần thiết từ một mô hình mạnh mẽ, đánh máy mới .

public ActionResult Marketing(List<ItemVM> OrderItems) 
... 
     foreach (ItemVM itemVM in OrderItems) 
      { 
       OrderItem item = new OrderItem(); 
       item.ItemID = Convert.ToInt16(itemVM.ItemID); 
       item.Quantity = Convert.ToInt16(itemVM.Quantity); 
       if (item.Quantity > 0) 
       { 
        order.Items.Add(item); 
       } 
      } 

Sau đó, bạn sẽ kết thúc với tập hợp các mục có số lượng lớn hơn 0 và ID mặt hàng.

Kỹ thuật này đang hoạt động ở MVC 5 sử dụng EF 6 trong Visual Studio 2015. Có thể điều này sẽ giúp ai đó tìm kiếm giải pháp này giống như tôi.

+0

Bạn có thể cung cấp ví dụ đầy đủ không? Tôi có hoàn cảnh tương tự –

1

tôi đã kết thúc thực hiện một Helper HTML chung chung hơn: -

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Text; 
using System.Text.RegularExpressions; 
using System.Web; 
using System.Web.Mvc; 

namespace Wallboards.Web.Helpers 
{ 
    /// <summary> 
    /// Hidden Index Html Helper 
    /// </summary> 
    public static class HiddenIndexHtmlHelper 
    { 
     /// <summary> 
     /// Hiddens the index for. 
     /// </summary> 
     /// <typeparam name="TModel">The type of the model.</typeparam> 
     /// <typeparam name="TProperty">The type of the property.</typeparam> 
     /// <param name="htmlHelper">The HTML helper.</param> 
     /// <param name="expression">The expression.</param> 
     /// <param name="index">The Index</param> 
     /// <returns>Returns Hidden Index For</returns> 
     public static MvcHtmlString HiddenIndexFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, int index) 
     { 
      var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 
      var propName = metadata.PropertyName; 

      StringBuilder sb = new StringBuilder(); 
      sb.AppendFormat("<input type=\"hidden\" name=\"{0}.Index\" autocomplete=\"off\" value=\"{1}\" />", propName, index); 

      return MvcHtmlString.Create(sb.ToString()); 
     } 
    } 
} 

Và sau đó bao gồm nó trong mỗi lần lặp của phần tử danh sách theo quan điểm của Razor của bạn: -

@Html.HiddenIndexFor(m => m.ExistingWallboardMessages, i) 
Các vấn đề liên quan