1

I have a SearchViewModel:

public class SearchViewModel
{
    [Required]
    public DateTime From { get; set; }
    [Required]
    public int Days { get; set; }
    public long DealerID { get; set; }
    public IQueryable<TypesAvail> TypesAvails { get; set; }
}

The IQueryable TypesAvails is another class, which holds a list of CarTypes available from the DealerID and between the From and Days properties in the SearchviewModel class - it is also used to produce a dropdownlist in the View:

public class TypesAvail
{
    public String TypeNme { get; set; }
    public long TypeID { get; set; }
    public int NumSelected { get; set; }
    public int TypeCount { get; set; }
    public IEnumerable<SelectListItem> CarsAvail 
    {
        get
        {
            return new SelectList(
                    Enumerable.Range(0, TypeCount+1)
                    .OrderBy(typecount => typecount)
                    .Select(typecount => new SelectListItem
                    {
                        Value = typecount.ToString(),
                        Text = typecount.ToString()
                    }), "Value", "Text");
        }
    } 
}

My Controller is: (EDIT: Added controller to show code accessing the data)

  public ActionResult Avail(SearchViewModel model, string id)
    {
        if (ModelState.IsValid)
        {
            var dteFrom = model.From;
            var dteTo = model.From.AddDays(model.Days);

            var prebookedCars = db.Cars
                .Where(car=> car.Rentals.Any(rental =>
                    (model.DealerID == rental.Dealerid) && (
                    (dteFrom >= rental.dateout && dteFrom < rental.datein )
                    ||
                    (dteTo > rental.dateout && dteTo <= rental.datein )
                    ||
                    (dteFrom <= rental.dateout && dteTo >= rental.datein )
                )));


            model.TypesAvails = db.Cars
                .Where(r => r.Dealerid == model.DealerID)
                .Except(prebookedCars)
                .GroupBy(p => p.CarType)
                .Select(g => new TypesAvail
                {
                    TypeNme = g.Key.type_name,
                    TypeID = g.Key.type_id,
                    TypeCount = g.Count(),
                    NumSelected = 0
                }
                );

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

The logic (I stand to be corrected) follows this:

  1. SearchViewModel is sent to the View with null against TypesAvail
  2. When the Post happens, the controller populates the From, Days and DealerID straight back into the model, using:

    [HttpPost]
    public ActionResult Avail(SearchViewModel model)

  3. In the controller, it also then populates the TypesAvail IQueryable

So now the ViewModel has the Date Searched, the number of Days, and a list of TypesAvailable, which includes the Select Lists.

This is repopulated in the view like this:

@model ebm2.ViewModels.SearchViewModel

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

    <ul data-role="listview" data-inset="true">
        <li data-role="list-divider">Check Availability</li>
        <li data-role="fieldcontain">
            @Html.LabelFor(m => m.From)
            @Html.TextBoxFor(m => m.From, new { @type = "date", data_role = "datebox", data_options = "{\"mode\":\"slidebox\", \"overrideDateFormat\":\"%A %d %b %Y\"}" })    
            @Html.ValidationMessageFor(m => m.From)    
        </li>           
        <li data-role="fieldcontain">
            @Html.LabelFor(m => m.Days)
            @Html.TextBoxFor(m => m.Days, new { @type = "range", min = 1, max = 30 })            
            @Html.ValidationMessageFor(m => m.Days)    
        </li>
        <li data-role="fieldcontain">
            <input type="submit" value="Search" />
        </li>
    </ul>
    @Html.HiddenFor(m => m.DealerID)

    if (Model.TypesAvails != null)
    {
        <ul data-role="listview" data-inset="true">
            <li data-role="list-divider">Book</li>
            @foreach (var item in Model.TypesAvails)
            {
                <li data-role="fieldcontain">      
                    @Html.HiddenFor(modelItem => item.TypeID) ->
                    @Html.DisplayFor(modelItem => item.NumSelected) ->
                    @Html.DropDownListFor(modelItem => item.NumSelected, item.CarsAvail)
                </li>
            }
        </ul>
    }
}

The second POST back to the server shows:

From=07%2F08%2F2012+00%3A00%3A00& Nights=1 & DealerID=1 & 
item.TypeID=3059 & item.NumSelected=3 & 
item.TypeID=3103 & item.NumSelected=2 & 
item.TypeID=3170 & item.NumSelected=7 & 
item.TypeID=3212 & item.NumSelected=4

The Razor to display those, isn't assinging the TypeID to the Drop Down List for each one:

@Html.HiddenFor(modelItem => item.TypeID)
@Html.DropDownListFor(modelItem => item.NumSelected, item.CarsAvail)

My question is from the POST the Model - shows NULL for TypesAvail - is there any way of representing the selected items in the drop down lists, straight into the ViewModel on postback?

If not, how do I loop through the FORM fields, to create another model - that will have:

From
Days
DealerID
{
    TypeID = 3059, NumSelected = 3,
    TypeID = 3103, NumSelected = 2,
    TypeID = 3170, NumSelected = 7,
    TypeID = 3212, NumSelected = 4
}

...that I can then use to add records to my database (basically a list of cars a Dealer wants to hire out, between two dates).

Or am I trying to get the search dates from one form, and then populating the same form with the multiple dropdownlists, to do too much - and is there a better way of:

  1. Searching from a date box and number of nights
  2. Then displayng a list of options which meet the search criteria
  3. Then posting to the server, and saving the Search data (from date, number of days, dealerID), as well as the selected drop down lists, and what was chosen in each one?

Thank you, Mark

UPDATE

With Shyu's help, I've made some progress - I've added an editortemplate:

@model ebm2.Models.TypesAvail
@Html.DropDownListFor(modelItem => modelItem.NumSelected, Model.CarsAvail)

...and rendered this in the view with:

   @Html.EditorFor(model=>model.TypesAvails)

However, when I then Post the form back to the controller (following adding the editorTemplate) I receive the following message:

[MissingMethodException: Cannot create an instance of an interface.]

I'm assuming this means that it doesn't recognise the model anymore - is this because of the EditorTemplate?

Is my approach of posting back, and then adding to the ViewModel the best way to do this, or would you suggest doing it all client side, with jQuery/Ajax - and only finally posting back to the server, when I have a completed JSON object of my model?

I'd appreciate any guidance on this.

Thanks, Mark

2 Answers 2

1

Use EditorTemplate.

Create a Folder Called "EditorTemplates" and create a view (the editor template) with the name TypesAvail.cshtml

Now Add the below code to this new view.

@model TypesAvail
<p>
   @Html.DropDownListFor(modelItem => item.NumSelected, item.CarsAvail)
</p>

Now in your Main View, Use the Html.EditorFor HTML helper method to call this editor template

@model ebm2.ViewModels.SearchViewModel
<h2>CarList</h2>
@using (Html.BeginForm())
{
    <p>Put other elements also </p>
    @Html.EditorFor(x=>x.TypesAvails)
    <input type="submit" value="Save" />
}
Sign up to request clarification or add additional context in comments.

3 Comments

Hi - thanks for that - I got an error when I replaced my HTML Helper with your one in the view: "There is already an open DataReader associated with this Command which must be closed first." - thank you,
@fixit : Should be something related to how you are reading the data. Make sure you close the DataReader in your data access code.
Hi @Shyju - I'm not using anything made up to access the data - just the out of the box code. I've added my controller to my original post. Is that where this error could be caused? Thank you, Mark
1

Try

        Dim strcon As String = "Data Source=.\SQLEXPRESS;AttachDbFilename=D:\webarticles\App_Data\mydatabase.mdf;Integrated Security=True;User Instance=True"
        Dim con As New SqlConnection(strcon)
        con.Open()


        Dim da As SqlDataAdapter
        Dim ds As New DataSet

        Dim sqlstring As String = "SELECT * FROM tblstudent "

        da = New SqlDataAdapter(sqlstring, con)
        da.Fill(ds)
        FormView1.DataSource = ds.Tables(0)
        FormView1.DataBind()
        Label1.Text = "Formview is filled"




    Catch ex As Exception
        Label1.Text = "There is Some Error"

    End Try

The FormView is an other asp.net control for handling record which gives you the ability to work with a single record from a data source, similar to the DetailsView control.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.