2012-02-09 11 views
5

W moim viewmodel, mam listę elementów I pobrania z bazy danych, a następnie wysłać do widoku. Chciałbym wiedzieć, czy można uniknąć konieczności uzupełniania właściwości opcji za każdym razem, gdy kliknę akcję Post i trzeba zwrócić model (w przypadku błędów sprawdzania poprawności, a co nie)?modelowe Reuse dane w działaniu postu

W webforms, to nie będzie konieczne.

Edit: I nie było jasne. Mój problem dotyczy opcji SelectList, których używam dla moich DropDownLists. Wszystko zostaje opublikowane, ale jeśli muszę wrócić do widoku (model jest nieprawidłowy), muszę przeładować opcje z bazy danych! Chcę wiedzieć, czy można tego uniknąć.

Moje ViewModel:

public class TestModel 
{ 
    public TestModel() 
    { 
     Departments = new List<SelectListItem>(); 
    } 

    public string Name { get; set; } 
    public int Department { get; set; } 
    public IEnumerable<SelectListItem> Departments { get; set; } 
} 

Moim zdaniem:

@model MvcApplication1.Models.TestModel  
@using (Html.BeginForm()) 
{ 
    @Html.TextBoxFor(m => m.Name) 

    @Html.DropDownListFor(m => m.Department, Model.Departments) 

    <input type=submit value=Submit /> 
} 

Mój kontroler (nie zauważyć komentarz na HttpPost):

public ActionResult Index() 
{ 
    TestModel model = new TestModel 
    { 
     Name = "Rafael", 
     Department = 1, 
     Departments = new List<SelectListItem> 
     { 
      new SelectListItem { Text = "Sales", Value = "1" }, 
      new SelectListItem { Text = "Marketing", Value = "2", Selected = true }, 
      new SelectListItem { Text = "Development", Value = "3" } 
     } 
    }; 

    // Departments gets filled from a database. 

    return View(model); 
} 

[HttpPost] 
public ActionResult Index(TestModel model) 
{ 
if (!ModelState.IsValid) 
{ 
    //Do I have to fill model.Departments again!?!?!? 

    return View(model); 
} 
else { ... } 
} 

góry dzięki.

Edytuj: FYI, moim rozwiązaniem było użycie zmiennej Session.

Odpowiedz

1

wystarczy wpisać swój pogląd silnie i zmienić metodę kontrolera mieć parametr tego typu klasy.

Oznacza to, że widok

@model MyNamesspace.Models.MyModel 
... 
@using (Html.BeginForm()) 
{ 
    .... 
} 

I metoda kontroler, który jest wysyłany do.

[HttpPost] 
public ActionResult MyAction(MyModel model) 
{ 
    ... 
} 

EDYCJA: Upewnij się również, że masz pola formularza dla każdej właściwości modelu, który chcesz wysłać do kontrolera. Moim przykładem jest używanie Razor zbyt BTW.

+0

Chcesz zobaczyć moją edycję? – rebelliard

+0

OK Rozumiem, co masz teraz na myśli. Ten sam problem pojawił się przy moim ostatnim projekcie. Niestety, użycie wpisu HTTP z natury rzeczy ograniczy model do odpowiednika par klucz-wartość. Oznacza to, że nie można mieć złożonego modelu z większą liczbą obiektów biznesowych jako właściwości zwróconych do kontrolera. Więc będziesz musiał przechowywać listę oddziałów gdzieś uporczywie. Sesja będzie odpowiednia do tego, o ile oczyścisz ją, gdy skończysz. Jeśli naprawdę, naprawdę musisz wysłać obiekty przez przewód, musisz użyć JSON lub innej techniki serializacji. – jhsowter

+0

Rozumiem. Czy poleciłbyś przekazanie danych z JSON wewnątrz ukrytego wejścia lub obiektu Session []? Dzięki. – rebelliard

0

Napotkano podobny problem podczas próby utworzenia kreatora Order w MVC (gdzie każda strona kreatora jest zaimplementowana jako widok częściowy załadowany przez AJAX). Szczerze wątpię, że jest zasugerował metody, ale mój sposób rozwiązywania tego było nazwać zwyczaj MergeChanges metody w każdej akcji wzywano mojego kreatora:

public Order MergeChanges(Order newOrder) 
{ 
    var sessionHistory = (List<string>)Session["sessionHistory"]; 

    if (sessionHistory == null || sessionHistory.Count == 0) 
    return MergeChanges(newOrder, -1); 

    return MergeChanges(newOrder, MasterViewController.GetStepNumberByName(sessionHistory.Last())); 
} 

public Order MergeChanges(Order newOrder, int step) 
{ 
    PreMerge(newOrder); 

    Order result = null; 
    try 
    { 
     ApplyLookups(ref newOrder); 
     Order oldOrder = (Order)Session["order"]; 

     if (oldOrder == null) 
     { 
      Session["order"] = newOrder; 
      result = newOrder; 
     } 
     else 
     { 
      List<TypeHelper.DecoratedProperty<ModelPageAttribute>> props = null; 
      newOrder.GetType().GetDecoratedProperty<ModelPageAttribute>(ref props); 
      props = props.Where(p => (p.Attributes.Count() > 0 && p.Attributes.First().PageNumber.Contains(step))).ToList(); 
      foreach (var propPair in props) 
      { 
       object oldObj = oldOrder; 
       object newObj = newOrder; 
       if (!string.IsNullOrEmpty(propPair.PropertyPath)) 
       { 
        bool badProp = false; 
        foreach (string propStr in propPair.PropertyPath.Split('\\')) 
        { 
         var prop = oldObj.GetType().GetProperty(propStr); 
         if (prop == null) 
         { 
          badProp = true; 
          break; 
         } 

         oldObj = prop.GetValue(oldObj, BindingFlags.GetProperty, null, null, null); 
         newObj = prop.GetValue(newObj, BindingFlags.GetProperty, null, null, null); 
        } 
        if (badProp) 
          continue; 
       } 

       if (newObj == null) 
        continue; 

       var srcVal = propPair.Property.GetValue(newObj, BindingFlags.GetProperty, null, null, null); 
       var dstVal = propPair.Property.GetValue(oldObj, BindingFlags.GetProperty, null, null, null); 

        var mergeHelperAttr = propPair.Property.GetAttribute<MergeHelperAttribute>(); 
        if (mergeHelperAttr == null) 
        { 
         if (newObj != null) 
          propPair.Property.SetValue(oldObj, srcVal, BindingFlags.SetProperty, null, null, null); 
        } 
        else 
        { 
         var mergeHelper = (IMergeHelper)Activator.CreateInstance(mergeHelperAttr.HelperType); 
         if (mergeHelper == null) 
          continue; 

         mergeHelper.Merge(context, HttpContext.Request, newObj, propPair.Property, srcVal, oldObj, propPair.Property, dstVal); 
        } 
       } 
       result = oldOrder; 
      } 
    } 
    finally 
    { 
    PostMerge(result); 
    } 
    return result; 
} 

Ponieważ moja sprawa robi to za pomocą kreatora, tylko specyficzny wartości stosowane do każdej strony tak, aby tylko uwagę właściwości znanych bieżącej stronie kreatora, i zostały wdrożone pewne atrybuty, a (wprawdzie nad kompleks) ViewController warstwy i warstwę zwyczaj sprawdzania poprawności. Mogę udostępnić trochę więcej kodu, ale powyższy kod działa, gdy nie znajdujesz się w tak złożonej sytuacji. Jeśli jest lepszy sposób, mam nadzieję, że dowiem się o tym na podstawie odpowiedzi na to pytanie, ponieważ jest to PITA.

+0

Wow. Doceniam twoją odpowiedź. Zacząłem myśleć o zapisywaniu wartości w hiddens w JSON, a następnie odczytywaniu ich. jakieś pomysły? – rebelliard

+0

W zależności od wymagań Twojej aplikacji internetowej, która może działać. Czuje się naprawdę brudno i jeśli masz duży zestaw pól, może to być ciężkie. Prawdopodobnie to by jednak zadziałało. W moim przypadku miałem ponad 150 złożonych dynamicznych pól, które należy uwzględnić, więc nie było to naprawdę możliwe. Mam nadzieję, że znajdziesz lepsze rozwiązanie. –

0

Jestem zaskoczony, że to pytanie nie pojawia się częściej i jestem również zaskoczony, że oczywiste (IMHO) odpowiedź nie jest obecnie standardową praktyką: prawie wszystkie POST powinny być oparte na Ajax.Rozwiązuje to całą masę problemów, w tym

  1. Nie ma potrzeby ponownego wypełniania danych formularza, gdy masz np. błąd sprawdzania poprawności lub błąd aplikacji (wyjątek). Jest to szczególnie pożądane, gdy masz stan po stronie klienta (w prawdziwie bogatej modzie aplikacji internetowych).
  2. Brak przymusu wykonywania walidacji po stronie klienta. Sprawdzanie poprawności może być w 100% po stronie serwera (gdzie to musi być musi być), a wrażenia użytkownika są prawie takie same.

Oczywiście, istnieją pewne wstępne prace trzeba zrobić, aby zbudować ramy tego, na przykład, mam zestaw ajaxUpdate, AjaxNothing, AjaxRedirect, AjaxErrors ... ActionResult typy, które czynią JSON, które jest przetwarzany przez niestandardowy skrypt JavaScript. Ale gdy już to zrobisz, będzie to gładka żegluga.

+0

'Sprawdzanie poprawności może być w 100% po stronie serwera (gdzie to musi być w każdym razie)', byłem pod wrażeniem, że MS był bardziej skłonny * do * sprawdzania poprawności strony klienta przez przesuwanie rzeczy 'DataAnnotation'. To prawda, że ​​nie spędziłem wystarczająco dużo czasu w tym królestwie, aby właściwie zrozumieć, co się dzieje, ale wszystkie tutoriale, które można znaleźć na MVC3 i walidację wydają się naciskać na podejście "Unobtrustive Javascript"/DataAnnotation. Czy czegoś brakuje? –

+0

@ M.Babcock - Myślę, że to, co MS próbował zrobić z DataAnnotations i "Unftrusive Javascript", to rozwiązanie problemu zduplikowanych walidacji po stronie klienta i po stronie serwera przy użyciu innego podejścia: skonfiguruj DataAnnotations, a framework Wykonaj * jawną weryfikację zarówno po stronie serwera, jak i po stronie klienta, ale w każdym przypadku walidacja po stronie serwera jest autorytatywna, a walidacja po stronie klienta jest przeznaczona tylko dla użytkowników. Jednak moje rozwiązanie całkowicie usuwa problem (problem, który twierdzę, jest przestarzały). –

+0

@M.Babcock - Zauważ, że nadal używam DataAnnotations (cóż, ostatnio wolałem FluentValidation), ale wykonuję weryfikację po stronie serwera i zwracam błędy z odpowiedzią Jona AjaxErrors, która jest renderowana przy pomocy JavaScript przez wywołanie zwrotne Ajax onsuccess. –