TJS UK
TJS UK

Reputation: 111

How do I use two dropdown menu instances from the same model?

I am having an issue where I think I have set things up correctly, however the results are alway nil.

Here's the three models:

public class FamilyMember
{
    [Key]
    public int id { get; set; }
    [Required]
    [Display(Name = "First Name")]
    public string firstName { get; set; }
    [Required]
    [Display(Name = "Surname")]
    public string surname { get; set; }
    [Required]
    [Display(Name = "Date of Birth")]
    public DateTime dob { get; set; }

    public virtual FamilyRelationship FamilyRelationship { get; set; }

    [Display(Name = "Full Name")]
    public string fullName
    {
        get
        {
            return string.Format("{0} {1}", firstName, surname);
        }
    }
}

public class RelationshipType
{
    [Key]
    public int id { get; set; }
    [Required]
    [Display(Name="Relationship Type")]
    public string relationshipType { get; set; }
    public virtual FamilyRelationship FamilyRelationship { get; set; }
}

public class FamilyRelationship
{
    [Key]
    public int id { get; set; }
    [ForeignKey("FamilyMembers")]
    [Display(Name = "First Family Member")]
    public int familyMemberPrimary { get; set; }
    [ForeignKey("FamilyMembers")]
    [Display(Name = "Second Family Member")]
    public int familyMemberSecondary { get; set; }
    [Display(Name = "Relationship Type")]
    public int relationshipType { get; set; }
    public virtual ICollection<FamilyMember> FamilyMembers { get; set; }
    public virtual ICollection<RelationshipType> RelationshipTypes { get; set; }
}

So, I have successfully added data to FamilyMember and RelationshipType and the CRUD is working perfectly.

The problem is found in the Create Controller/View of FamilyRelationship. The dropdown works perfectly and shows the family members in the two associated menus and the relationship also shows on the relationType dropdown. However, when I click create all values are set to null.

Create Controller:

    // GET: FamilyRelationships/Create
    public ActionResult Create()
    {
        ViewBag.familyMember = new SelectList(db.FamilyMembers, "id", "fullName");
        ViewBag.relationship = new SelectList(db.RelationshipTypes, "id", "relationshipType");
        return View();
    }

    // POST: FamilyRelationships/Create

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "id,familyMemberPrimary,familyMemberSecondary,relationshipType")] FamilyRelationship familyRelationship)
    {
        if (ModelState.IsValid)
        {
            db.FamilyRelationships.Add(familyRelationship);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(familyRelationship);
    }

Create View:

    @model FamilyTree.Models.FamilyRelationship

    @{
        ViewBag.Title = "Create";
    }

    <h2>Create</h2>


    @using (Html.BeginForm()) 
    {
        @Html.AntiForgeryToken()

        <div class="form-horizontal">
            <h4>FamilyRelationship</h4>
            <hr />
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })
            <div class="form-group">
                @Html.LabelFor(model => model.familyMemberPrimary, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @*@Html.EditorFor(model => model.familyMemberPrimary, new { htmlAttributes = new { @class = "form-control" } })
                    @Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { @class = "text-danger" })*@

                    @Html.DropDownList("familyMember", null, htmlAttributes: new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.familyMemberSecondary, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.DropDownList("familyMember", null, htmlAttributes: new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                @Html.LabelFor(model => model.relationshipType, htmlAttributes: new { @class = "control-label col-md-2" })
                <div class="col-md-10">
                    @Html.DropDownList("relationship", null, htmlAttributes: new { @class = "form-control" })
                    @Html.ValidationMessageFor(model => model.relationshipType, "", new { @class = "text-danger" })
                </div>
            </div>

            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Create" class="btn btn-default" />
                </div>
            </div>
        </div>
    }

    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>

    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
    }

Please let me know where am I going wrong and if possible provide an example to make this work.

Upvotes: 1

Views: 980

Answers (2)

JamieD77
JamieD77

Reputation: 13949

@Html.DropDownList("familyMember"

you need to use the actual property names most likely

@Html.DropDownList("familyMemberPrimary"

you'd also have to rename the viewbag property to match for the items to show up.. or use dropdownlistfor

@Html.DropDownListFor(a => a.familyMemberPrimary, (SelectList)ViewBag.familyMember , new { @class = "form-control" })

you also need to add a dropdownlist for familyMemberSecondary

@Html.DropDownListFor(a => a.familyMemberSecondary, (SelectList)ViewBag.familyMember , new { @class = "form-control" })

This should get you pretty close..

@model FamilyTree.Models.FamilyRelationship

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>FamilyRelationship</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        <div class="form-group">
            @Html.LabelFor(model => model.familyMemberPrimary, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(a => a.familyMemberPrimary, (SelectList)ViewBag.familyMember, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.familyMemberPrimary, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.familyMemberSecondary, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(a => a.familyMemberSecondary, (SelectList)ViewBag.familyMember, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.familyMemberSecondary, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            @Html.LabelFor(model => model.relationshipType, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownListFor(a => a.relationshipType, (SelectList)ViewBag.relationship, new { @class = "form-control" })
                @Html.ValidationMessageFor(model => model.relationshipType, "", new { @class = "text-danger" })
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Create" class="btn btn-default" />
            </div>
        </div>
    </div>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

make sure you re set your ViewBag properties after a failed POST

DotNetFiddle Example

Upvotes: 2

Shyju
Shyju

Reputation: 218752

This is the expected behaviour. Remember Http is stateless. So you need to reload your dropdown data before returning to the view

 [HttpPost]
 [ValidateAntiForgeryToken]
 public ActionResult Create([Bind(Include = "id,familyMemberPrimary,familyMemberSecondary,
                                  relationshipType")] FamilyRelationship familyRelationship)
 {
     if (ModelState.IsValid)
     {
         db.FamilyRelationships.Add(familyRelationship);
         db.SaveChanges();
         return RedirectToAction("Index");
     }

    //Let's reload the data for dropdown.
    ViewBag.familyMember = new SelectList(db.FamilyMembers, "id", "fullName");
    ViewBag.relationship = new SelectList(db.RelationshipTypes, "id", "relationshipType");

    return View(familyRelationship);
 }

EDIT: As per comment

The values within the FamilyRelationship so familyMemberPrimary, familyMemberSecondary and relationshipType have values of 0, where I was expecting the id's of each of these would be passed over.

Because you are using EditorFor helper method for familyMemberPrimary property in your view. So if you are not filling a value in that input field, it is going to have default value(0 for int type)

If you want that property to be filled with your dropdown selection(of family members), you should give the dropdown name value as familyMemberPrimary so that when you post the form, model binding will set the selected option value to familyMemberPrimary property.

@Html.DropDownList("familyMemberPrimary", 
           ViewBag.familyMember as IEnumerable<SelectListItem>, 
           htmlAttributes: new { @class = "form-control" })

Upvotes: 1

Related Questions