2013-09-21 17 views
9

Giả sử tôi có chế độ xem được nhập vào bộ sưu tập, ví dụ: một List<ItemViewModel>:NameĐể tạo tên không chính xác khi lặp qua bộ sưu tập trong mẫu trình soạn thảo

@model List<ItemViewModel> 

@for(int i = 0; i < Model.Count; i++) 
{ 
    @Html.EditorFor(m => m[i].Foo) 
    @Html.EditorFor(m => m[i].Bar) 
} 

FooBar chỉ đơn giản là thuộc tính chuỗi.

Điều này tạo ra các thuộc tính tên HTML của biểu mẫu [i].Foo[i].Bar, tất nhiên, đúng và liên kết chính xác khi được đăng trong biểu mẫu.

Bây giờ giả sử, thay vào đó, rằng quan điểm trên là một mẫu biên tập, được trả lại như vậy (nơi Model.Items là một List<ItemViewModel>):

@model WrappingViewModel 

@Html.EditorFor(m => m.Items) 

Đột nhiên, tên được tạo ra bên trong mẫu biên tập viên có dạng - ví dụ - Items.[i].Foo. Trình kết nối mô hình mặc định không thể ràng buộc điều này vì nó mong đợi biểu mẫu Items[i].Foo.

này hoạt động tốt trong kịch bản đầu tiên - nơi quan điểm không phải là một mẫu biên tập - và cũng hoạt động tốt mà bộ sưu tập là một tài sản, chứ không phải là toàn bộ mô hình:

@Html.EditorFor(m => m.Items[i].Foo) 

Nó chỉ thất bại khi chính mô hình là bộ sưu tập chế độ xem là mẫu trình chỉnh sửa.

Có một vài cách để làm việc xung quanh này, không ai trong số đó là lý tưởng:

  • Loại biên tập mẫu để một thể hiện cá nhân của ItemViewModel - đây là không tốt như các mẫu thực tế trong câu hỏi có chứa đánh dấu để thêm/xóa khỏi bộ sưu tập. Tôi cần để có thể làm việc với toàn bộ bộ sưu tập bên trong mẫu.
  • Bọc List<ItemViewModel> vào thuộc tính khác (ví dụ: bằng cách triển khai ItemListViewModel) và chuyển mẫu đó tới mẫu - đây không phải là lý tưởng vì đây là ứng dụng doanh nghiệp mà tôi không muốn lộn xộn với các mô hình xem không cần thiết.
  • Tạo đánh dấu cho mẫu trình chỉnh sửa bên trong theo cách thủ công để tạo tên chính xác - đây là những gì tôi hiện đang làm nhưng tôi muốn tránh nó vì tôi mất tính linh hoạt của HtmlHelpers.

Vì vậy, câu hỏi: Tại sao NameFor (và do đó EditorFor) hiện hành vi này trong kịch bản đặc biệt này khi nó hoạt động tốt cho các biến thể nhẹ (ví dụ là nó cố ý, và nếu vậy, tại sao)? Có cách nào đơn giản để làm việc xung quanh hành vi này mà không có bất kỳ thiếu sót nào ở trên không?

Theo yêu cầu, mã đầy đủ để tái sản xuất:

Models:

public class WrappingViewModel 
{ 
    [UIHint("_ItemView")] 
    public List<ItemViewModel> Items { get; set; } 

    public WrappingViewModel() 
    { 
     Items = new List<ItemViewModel>(); 
    } 
} 

public class ItemViewModel 
{ 
    public string Foo { get; set; } 
    public string Bar { get; set; } 
} 

điều khiển hành động:

public ActionResult Index() 
{ 
    var model = new WrappingViewModel(); 
    model.Items.Add(new ItemViewModel { Foo = "Foo1", Bar = "Bar1" }); 
    model.Items.Add(new ItemViewModel { Foo = "Foo2", Bar = "Bar2" }); 
    return View(model); 
} 

Index.cshtml:

@model WrappingViewModel 

@using (Html.BeginForm()) 
{ 
    @Html.EditorFor(m => m.Items) 
    <input type="submit" value="Submit" /> 
} 

_ItemView.cshtml (biên tập mẫu):

@model List<ItemViewModel> 

@for(int i = 0; i < Model.Count; i++) 
{ 
    @Html.EditorFor(m => m[i].Foo) 
    @Html.EditorFor(m => m[i].Bar) 
} 

Tên thuộc tính cho FooBar đầu vào sẽ có dạng Model.[i].Property và sẽ không ràng buộc trở lại khi được đưa lên một phương pháp hành động với chữ ký ActionResult Index(WrappingViewModel). Lưu ý rằng, như đã đề cập ở trên, điều này hoạt động tốt nếu bạn lặp lại trên Items trong chế độ xem chính hoặc nếu bạn loại bỏ WrappingViewModel, làm cho mô hình cấp cao nhất là List<ItemViewModel> và lặp lại trực tiếp Model. Nó chỉ thất bại cho kịch bản cụ thể này.

+0

Ông có thể đề xuất một mã nguồn của hành động cho kịch bản thứ hai?Bạn mong đợi những tham số nào trong hành động? –

+0

@AlexanderSimonov Tôi sẽ đăng một số mã nguồn để tái tạo vấn đề sau này một chút. –

+0

@AlexanderSimonov Mã có thể tái sản xuất đầy đủ được đăng, theo yêu cầu. –

Trả lời

5

Tại sao NameFor (và do đó EditorFor) hiện hành vi này trong kịch bản đặc biệt này khi nó hoạt động tốt cho các biến thể nhẹ (ví dụ là nó cố ý, và nếu vậy, tại sao)?

Đây là một lỗi (link) và nó sẽ được cố định với việc phát hành của ASP.NET MVC 5.

Có một cách đơn giản để làm việc xung quanh hành vi này mà không cần bất kỳ của những thiếu sót của ở trên?

đơn giản:

  1. Thêm ItemViewModel.cshtml biên tập mẫu với đoạn mã sau:

    @model ItemViewModel 
    @Html.EditorFor(m => m.Foo) 
    @Html.EditorFor(m => m.Bar) 
    
  2. Di _ItemView.cshtml biên tập mẫu.

  3. Xóa [UIHint("_ItemView")] thuộc tính từ WrappingViewModel.

Một chút khó khăn hơn:

  1. Thêm ItemViewModel.cshtml biên tập mẫu (tương tự như trên).

  2. Sửa _ItemView.cshtml:

    @model List<ItemViewModel> 
    
    @{ 
        string oldPrefix = ViewData.TemplateInfo.HtmlFieldPrefix; 
    
        try 
        { 
         ViewData.TemplateInfo.HtmlFieldPrefix = string.Empty; 
    
         for (int i = 0; i < Model.Count; i++) 
         { 
          var item = Model[i]; 
          string itemPrefix = string.Format("{0}[{1}]", oldPrefix, i.ToString(CultureInfo.InvariantCulture)); 
          @Html.EditorFor(m => item, null, itemPrefix) 
         } 
        } 
        finally 
        { 
         ViewData.TemplateInfo.HtmlFieldPrefix = oldPrefix; 
        } 
    } 
    

CẬP NHẬT

Trong trường hợp nếu bạn không muốn thêm ItemViewModel.cshtml biên tập mẫu cho các tùy chọn thứ hai sau đó thay vì @Html.EditorFor(m => item, null, itemPrefix) bạn có để viết một cái gì đó như thế:

@Html.EditorFor(m => item.Foo, null, Html.NameFor(m => item.Foo).ToString().Replace("item", itemPrefix)) 

@Html.EditorFor(m => item.Bar, null, Html.NameFor(m => item.Bar).ToString().Replace("item", itemPrefix)) 

LƯU Ý: Đó là tốt hơn để quấn rằng đoạn mã như phương pháp khuyến nông

Các vấn đề liên quan