PiousVenom
PiousVenom

Reputation: 6908

Add items to list via user input

I'm giving a go through some tutorials (here and here) on ASP.NET MVC, and decided to try a few things on my own. Now, I've got three tables, Resume, Descriptions, SubDescriptions. Here's the code for the three:

public class Resume
{
    public Resume()
    {
        Descriptions = new List<Description>();
    }

    [Key]
    public int ResumeId { get; set; }

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

    [DataType(DataType.Date)]
    public DateTime StartDate { get; set; }

    [DataType(DataType.Date)]
    public DateTime EndDate { get; set; }

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

    [Required]
    public virtual ICollection<Description> Descriptions { get; set; }
}

public class Description
{
    public Description()
    {
        SubDescriptions = new List<SubDescription>();
    }

    [Key]
    public int DescriptionId { get; set; }

    [ForeignKey("Resume")]
    public int ResumeId { get; set; }

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

    public virtual Resume Resume { get; set; }

    public virtual ICollection<SubDescription> SubDescriptions { get; set; }
}

public class SubDescription
{
    [Key]
    public int SubDescriptionId { get; set; }

    [ForeignKey("Description")]
    public int DescriptionId { get; set; }

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

    public virtual Description Description { get; set; }
}

Now, the problem I'm having is in the Create view. I'd like to create a new Resume entry. However, I'm running into an issue with adding Descriptions. Here's my Create view:

@model Portfolio.Models.Resume

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Resume</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Employer)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Employer)
            @Html.ValidationMessageFor(model => model.Employer)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Title)
            @Html.ValidationMessageFor(model => model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.StartDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.StartDate)
            @Html.ValidationMessageFor(model => model.StartDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.EndDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.EndDate)
            @Html.ValidationMessageFor(model => model.EndDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Location)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Location)
            @Html.ValidationMessageFor(model => model.Location)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Descriptions)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Descriptions)
            @Html.ValidationMessageFor(model => model.Descriptions)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

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

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

The problem is that since Descriptions is a list, nothing actually shows up to add. I'm not even sure about the way to go about this. I want to be able to add a Description, and then have a choice of adding a SubDescription after the fact. Is this even possible, or am I attempting something utterly convoluted?

EDIT

Adding this edit for a little more clarification. I'm trying to determine how to have the user populate the Descriptions list in the Create view. I was thinking of using a listbox for this. Then, they can select a Description, and add a SubDescription. But I'm not 100% sure on how to do that. I tried to change the @Html.EditorFor(model => model.Descriptions) to @Html.ListBoxFor(model => model.Descriptions), but I got an error that no overload for ListBoxFor takes 1 argument. And since I'm still new and learning this, I'm not sure how to go about accomplishing what I'd like to do.

Upvotes: 4

Views: 2042

Answers (2)

Binke
Binke

Reputation: 900

@RobG31415 has given you a pretty good answer but I know from experience that the first time working with ListBoxFor and DropDownListFor was a real pain so I'm going to give you a little more.

Creating resume

You can pass your ViewModel or model to your view just containing the Descriptions. Since the SubDescriptions depends on what Description you have chosen, you need to have cascading dropdowns.

Create a dropdown list for your Descriptions something along this line:

@Html.DropDownListFor(x => x.DescriptionId, new SelectList(Model.Descriptions.Select(m => new SelectListItem()
{
    Text = m.Desc,
    Value = m.DescriptionId.ToString()
})), "Choose", new { id = "Descriptions" })

After that, add a empty dropdownlist for your sub descriptions

@Html.DropDownList("SubDescriptionId", new SelectList(new List<SelectListItem>()), new { id = "SubDescriptions" })

The first expression in the DropDownListFor is pointing to a modelproperty for storing the selected value from the drop down, and that would be DescriptionId. Now we have to create the cascading drop down effect using jquery and ajax. We add an onchange event to the Descriptions dropdown to go and get all the SubDescriptions from the db.

$('#Descriptions').change(function() {
    var descId = $('#Descriptions:selected').val();
    $.ajax({
        type: 'GET',
        url: '/Resume/GetSubDescriptions'
        data: { id = descId },
        success: function(data) {
            //TODO: Add SubDescriptions to the dropdown
        }
    });
});

Creating description/sub description

This is just the part for creating the Resume. If you need to let users have the possibility of creating Descriptions of their own, i think you should have a button or an option in the dropdown saying "Create Description", and one for SubDescription. This can open a new window on top or sliding down a hidden div, or what ever you come up with. this part you can do as a partial view or just a part of the original view.
Starting with Description, this only need a string so a normal Html.TextBox and a button for saving it should probably be everything that you need for now. After the user fills in the textbox and pushes save, send the string to your controller with ajax, and let it return the created Description

$('#createDescription').click(function() {
    var desc = $('#newDescription').val();
    $.ajax({
        type: 'POST',
        url: '/Resume/CreateDescription'
        data: { desc = desc },
        success: function(data) {
            //TODO: Add the returned description to the drop-downs
        }
    });
});

public JsonResult CreateDescription(string desc)
{
    Description description = _resumeService.CreateDescription(desc);
    return Json(description);
}

To create your SubDescription you need to first choose a description and then pick a name for it, so this would look the same but with a dropdown containing all the descriptions as i showed erlier, and then a textbox for the name, and post these two values to the controller.

Some things to consider:

The problem with creating your Description and SubDescription on the same form as where you create the resume is that you probably don't want the whole page to reload everytime they create something new. This will easily result in alot of javascript/jQuery to keep your drop downs etc updated with the correct data. Posting the whole form and reloading the page will give you an easier task of having the right data in the drop downs, but will also give a less professional feeling to your site.

side note:

What I would do first of all is setting up ViewModels instead of using your real model. Since you probably will need validation and different sets of data in your view, the best way to achieve without cluttering your models is to have ViewModels. To get ease of use with your viewmodels I like to use Automapper to map data from model to viewmodel and back. You can also map data from viewmodels to other viewmodels if you desire that.

Upvotes: 1

RobG31415
RobG31415

Reputation: 21

You were on the right track! The ListBoxFor method takes two parameters. The first is what you currently have. The second parameter is an IEnumerable<SelectListItem> object.

I have personally done this sort of thing before, and it was really helpful using the ListBoxFor method. You can create a List<SelectListItem> and each item can hold the information stored in a Descriptions object.

If you pass the List<SelectListItem> object to the View from your Controller, you can show all the Descriptions objects to the user and they can select and add whichever ones they choose.

You can also create separate views and redirect the user to those views in certain situations based on what you’re trying to do.

Good luck! =)

Upvotes: 1

Related Questions