Fabricio
Fabricio

Reputation: 7925

DropDownListFor giving null always

Models

public class Cidade
{
    [Key]
    public int Id { get; set; }

    [Required]
    public string Nome { get; set; }
}

public class Usuario
{
    [Key]
    public int Id { get; set; }

    [Required]
    public Cidade Cidade { get; set; }

    /* more fields... */
}

Controller

public ActionResult Registrar()
{
    using (var db = new MyContext())
    {
        ViewBag.Cidades = new SelectList(db.Cidades.ToList(), "Id", "Nome");
    }
    return View();
}

[HttpPost]
public ActionResult Registrar(Usuario usuario)
{
    if (ModelState.IsValid)
    {
        using (var db = new MyContext())
        {
            db.Usuarios.Add(usuario);
            db.SaveChanges();
        }

        return RedirectToAction("Index", "Home");
    }

    return Registrar();
}

View

@Html.LabelFor(m => m.Cidade)
@Html.DropDownListFor(m => m.Cidade, (SelectList)ViewBag.Cidades)

But ModelState.IsValid == false always, because usuario.Cidade == null.

:(

Upvotes: 0

Views: 55

Answers (1)

von v.
von v.

Reputation: 17108

You bind to the model's property and not to the model itself. So instead of doing this

@Html.DropDownListFor(m => m.Cidade, (SelectList)ViewBag.Cidades)

you need to do this

@Html.DropDownListFor(m => m.Cidade.Id, (SelectList)ViewBag.Cidades)

But that would probably NOT solve your problem entirely because you have a required attribute for name. However, the way you use your model is also not the way you expect to use it in your view. So the question you should be asking yourself is: are you creating a Cidade along with your Usuario in your view? The answer to which I think is "no". The way I understand your setup based on your question is that you want to assign a Cidade to a Usuario that you are creating/editing.

This again is another case where you would use viewmodels, which is a standard practice, escpecially for this kind of thing. If you do not want to use viewomdel then you should at least change your Usuario into this:

public class Usuario
{
    [Key]
    public int Id { get; set; }

    public Cidade Cidade { get; set; }

    [Required]
    public int CidadeId { get; set; }
}

Notice the additional CidadeId and that it is marked as required. In your view you would then do this:

@Html.DropDownListFor(m => m.CidadeId, (SelectList)ViewBag.Cidades)

UPDATE:

The only inconvenient thing is have to see "Cidade" as null when debugging. Would be nice see it filled.

It really is not necessary and really not a good approach. But as always it can still be done if you really feel it fits your project. So you add a hidden field that will map to Cidade.Nome

@Html.HiddenFor(m => m.Cidade.Nome)
// then change your binding again to this
@Html.DropDownListFor(m => m.Cidade.Id, (SelectList)ViewBag.Cidades)

add a script to populate that field when the dropdown changes:

$("#Cidade_Id").change(function(){
    $("#Cidade_Name").val($(this).text());
});

Upvotes: 1

Related Questions