2013-09-21 9 views
9

Załóżmy, że mam widok wpisany do kolekcji, np. List<ItemViewModel>:NameDo generowania niepoprawnej nazwy podczas iteracji nad kolekcją w szablonie edytora

@model List<ItemViewModel> 

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

Foo i Bar są po prostu właściwości smyczkowych.

Generuje to atrybuty nazwy HTML formularza [i].Foo i [i].Bar, które oczywiście są poprawne i wiążą się poprawnie po opublikowaniu w formularzu.

Załóżmy teraz, zamiast tego, że powyższe zdanie jest szablon edytor, który jest renderowany podobnie jak (gdzie Model.Items jest List<ItemViewModel>):

@model WrappingViewModel 

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

Nagle, nazwy generowane wewnątrz szablonu edytora mają postać - na przykład - Items.[i].Foo. Domyślny segregator modelu nie może tego powiązać, ponieważ oczekuje formy Items[i].Foo.

Działa to dobrze w pierwszym scenariuszu - gdzie widok nie jest szablon redaktor - a także działa dobrze, gdzie zbiór jest nieruchomość, a nie całego modelu

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

zawiedzie, gdy tylko sam model to kolekcja i widok jest szablonem edytora.

Istnieje kilka sposobów pracy wokół tego, z których żadna nie jest idealna:

  • Wpisz szablon redaktor indywidualnej instancji ItemViewModel - to nie jest dobre, jak rzeczywista szablon w pytaniu zawiera dodatkowe znaczniki do dodawania do/usuwania z kolekcji. Muszę umieć pracować z całą kolekcją wewnątrz szablonu.
  • Owiń w innej usłudze (np. Implementując ItemListViewModel) i przekaż ją do szablonu - to też nie jest idealne, ponieważ jest to aplikacja dla przedsiębiorstw, której nie chciałbym zaśmiecać zbytecznymi modelami widoku zwijania.
  • Generowanie znaczników dla wewnętrznych szablonów edytora ręcznie w celu uzyskania poprawnej nazwy - właśnie to robię, ale wolałbym tego uniknąć, ponieważ tracę elastyczność HtmlHelpers.

Więc pytanie: Dlaczego NameFor (a więc EditorFor) wykazują tego zachowania w tej konkretnej sytuacji, kiedy to działa dobrze dla drobnymi zmianami (to jest to zamierzone, a jeśli tak, to dlaczego)? Czy istnieje prosty sposób obejścia tego zachowania bez żadnych niedociągnięć powyższych?

Zgodnie z wnioskiem, aby odtworzyć pełny kod:

modele:

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

Controller działania:

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 (szablon redaktor):

@model List<ItemViewModel> 

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

Nazwa atrybuty Foo i Bar wejść będzie formie Model.[i].Property i nie będą wiązać z powrotem, gdy pisał do sposobu działania z podpis ActionResult Index(WrappingViewModel). Zauważ, że, jak wspomniano powyżej, działa to dobrze, jeśli wykonasz iterację ponad Items w widoku głównym lub, jeśli pozbędziesz się WrappingViewModel, ustaw model najwyższego poziomu na List<ItemViewModel> i wykonaj bezpośrednio iterację ponad . Nie udaje się tylko w tym konkretnym scenariuszu.

+0

mógłbyś zaproponować kod źródłowy działania dla drugiego scenariusza?Jakie parametry oczekujesz w działaniu? –

+0

@AlexanderSimonov Wezwę trochę kodu źródłowego do odtworzenia problemu nieco później. –

+0

@AlexanderSimonov Pełny, powtarzalny kod, zgodnie z życzeniem. –

Odpowiedz

5

Dlaczego NameFor (a więc EditorFor) wykazują takie zachowanie w tej konkretnej sytuacji, kiedy to działa dobrze dla drobnymi zmianami (czyli jest to zamierzone, a jeśli tak, to dlaczego)?

Jest to bug (link) i zostanie ona ustalona z wydaniem ASP.NET MVC 5.

Czy istnieje prosty sposób pracy wokół tego problemu bez żadnych niedociągnięć powyżej?

Proste:

  1. Dodaj ItemViewModel.cshtml edytora szablonu z następującego kodu:

    @model ItemViewModel 
    @Html.EditorFor(m => m.Foo) 
    @Html.EditorFor(m => m.Bar) 
    
  2. Usuń _ItemView.cshtml edytora szablonu.

  3. Usuń atrybut [UIHint("_ItemView")] z WrappingViewModel.

Trochę trudniej:

  1. Dodaj ItemViewModel.cshtml edytora szablonu (jak wyżej).

  2. Zmienić _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; 
        } 
    } 
    

UPDATE

W przypadku, gdy nie chcesz, aby dodać ItemViewModel.cshtml edytora szablonu dla opcji sekund wtedy zamiast @Html.EditorFor(m => item, null, itemPrefix) masz napisać coś takiego:

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

UWAGA: Lepiej owinąć ten kawałek kodu jako metodę rozszerzenia

Powiązane problemy