Pregunta

I'm using ASP.NET MVC 4 and I built these ViewModels :

public class NotificationViewModel
{

    public string GroupDesc { get; set; }

    public bool AM { get; set; }

    public bool PM { get; set; }

    public int MaxNotif { get; set; }
}

public class SettingsViewModel
{
    public List<NotificationViewModel> ListNotification { get; set; }

    public SettingsViewModel()
    {
        ListNotification = new List<NotificationViewModel>();
    }
}

My View :

@model PortailT2A.Models.SettingsViewModel

@{
    ViewBag.Title = "Preferences";
    Layout = "~/Views/Shared/_LayoutAdmin.cshtml";
}

<h2>Preferences</h2>


@using(Html.BeginForm("Preferences", "Administrateur", FormMethod.Post))
{

    <table id="settingsTable">
        <tr>
            <th>Groupe</th>
            <th></th>
            <th>AM</th>
            <th>PM</th>
            <th>Limite de notifications</th>
        </tr>

    @for (int i = 0; i < Model.ListNotification.Count(); i++ )
    {
        var notif = Model.ListNotification[i];
        <tr>
            <td>@notif.GroupDesc </td>
            <td>Heure de notification</td>
            <td>@Html.CheckBoxFor(u => notif.AM)  </td>
            <td>@Html.CheckBoxFor(u => notif.PM)  </td>
            <td>@Html.TextBoxFor(u => notif.MaxNotif)</td>
        </tr>
        <tr/>


    }

    </table>


    <input type ="submit" value="Sauvegarder" />

}

My HttpGet method populates my ViewModel and returns it.

    [HttpGet]
    public ActionResult Preferences(long idUser)
    {
        context = new MainDatabaseEntities();

        List<NotificationViewModel> notifications = new List<NotificationViewModel>();

        SettingsViewModel settings = new SettingsViewModel();

        //Population...

        return View(settings);
    }

However, when I want to save the changes, I got a ViewModel which is null and I don't understand why. Any idea guys?

EDIT : My post method :

            [HttpPost]
            public ActionResult Preferences(SettingsViewModel sm)
            {
                //since here my ViewModel is null
                context = new MainDatabaseEntities();

                Utilisateur user = (from u in context.Utilisateurs where u.Username == User.Identity.Name select u).FirstOrDefault();

                //operations...

}

HTML generated :

<tr>
        <td>Groupe B </td>
        <td>Heure de notification</td>
        <td><input id="notif_AM" name="notif.AM" type="checkbox" value="true" /><input name="notif.AM" type="hidden" value="false" />  </td>
        <td><input checked="checked" id="notif_PM" name="notif.PM" type="checkbox" value="true" /><input name="notif.PM" type="hidden" value="false" />  </td>
        <td><input id="notif_MaxNotif" name="notif.MaxNotif" type="text" value="10" /></td>
    </tr>
¿Fue útil?

Solución

List<T> can be tricky when modelbinding since it relies heavily on the indexed keys. The helpers need to know the index, but by assigning notif within your for loop they're losing the reference. Instead, try something like the following:

@for (int i = 0; i < Model.ListNotification.Count(); i++ )
{
    var notif = Model.ListNotification[i];
    <tr>
        <td>@notif.GroupDesc </td>
        <td>Heure de notification</td>
        <td>@Html.CheckBoxFor(u => u.ListNotification[i].AM)  </td>
        <td>@Html.CheckBoxFor(u => u.ListNotification[i].PM)  </td>
        <td>@Html.TextBoxFor(u => u.ListNotification[i].MaxNotif)</td>
    </tr>
    <tr/>
}

Which should then provide you with something like:

<tr>
    <td>Groupe B </td>
    <td>Heure de notification</td>
    <td>
        <input id="ListNotification[0]_AM" name="ListNotification[0].AM" type="checkbox" value="true" />
        <input name="ListNotification[0].AM" type="hidden" value="false" />
    </td>
    <td>
        <input checked="checked" id="ListNotification[0]_PM" name="ListNotification[0].PM" type="checkbox" value="true" />
        <input name="ListNotification[0].PM" type="hidden" value="false" />
    </td>
    <td>
        <input id="ListNotification[0]_MaxNotif" name="ListNotification[0].MaxNotif" type="text" value="10" />
    </td>
</tr>

Also, make sure to check ModelState.IsValid in your posted action to confirm the model was bound correctly. If not, you should see a list of errors in ModelState that would give you some indication as to where it may have failed.

Also, I don't see you dump GroupDesc anywhere (except to output). If this is necessary on the incoming model, you may consider using @Html.HiddenFor(x => x.ListNotifications[i].GroupDesc).

Otros consejos

You're not building your HTML correctly. What is posted back will not have the paths the model binder expects.

Consider replacing this:

@for (int i = 0; i < Model.ListNotification.Count(); i++ )
{
    var notif = Model.ListNotification[i];
    <tr>
        <td>@notif.GroupDesc </td>
        <td>Heure de notification</td>
        <td>@Html.CheckBoxFor(u => notif.AM)  </td>
        <td>@Html.CheckBoxFor(u => notif.PM)  </td>
        <td>@Html.TextBoxFor(u => notif.MaxNotif)</td>
    </tr>
    <tr/>
}

with this:

@Html.DisplayModelFor(m => m.ListNotification)

and add a template like this to /Views/{YourController}/{YourAction}/EditorTemplates/NotificationViewModel.cshtml

@model NotificationViewModel
<tr>
    <td>@Model.GroupDesc</td>
    <td>Heure de notification</td>
    <td>@Html.CheckBoxFor(m => m.AM)</td>
    <td>@Html.CheckBoxFor(m => m.PM)</td>
    <td>@Html.TextBoxFor(m => m.MaxNotif)</td>
</tr>

My first suggestion would be to try adding some random property to your SettingValueModel and adding it to the form as a hidden.

Something like

 public class SettingsViewModel
 {
  public List<NotificationViewModel> ListNotification { get; set; }
  public string TestValue { get; set; }

  public SettingsViewModel()
  {
    ListNotification = new List<NotificationViewModel>();
    TestValue = "Test";   
  }
 }

Then in your view add

@Html.HiddenFor(s=> s.TestValue)

When you submit your form, check to see if the SettingsViewModel isn't null. If the problem is just in the ListNotification serialization, then you might end up with an object with a TestValue of "Test" and null List Notifications. If that is the case, at least you know the problem is the ListNotifications.

Also try changing your for loop to this

  @for (int i = 0; i < Model.ListNotification.Count(); i++ )
     {

         <tr>
             <td>@Model.ListNotification[i].GroupDesc </td>
             <td>Heure de notification</td>
             <td>@Html.CheckBoxFor(u => u.ListNotification[i].AM)  </td>
             <td>@Html.CheckBoxFor(u => u.ListNotification[i].PM)  </td>
             <td>@Html.TextBoxFor(u => u.ListNotification[i].MaxNotif)</td>

Also I don't think FormMethod.Post is required in the form definition. Try all of these things. If none of them help, then I can only assume your PC is haunted =)

In my case, I had my setters as internal in my view model definition for some strange reason, thus the binding couldn't be set and stored:

public int PaymentType { get; internal set; }

In POST action do you use right type of model?

[HttpPost]
public ActionResult Preferences(PortailT2A.Models.SettingsViewModel model)
{
   //...
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top