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:
- SearchViewModel is sent to the View with null against TypesAvail
When the Post happens, the controller populates the From, Days and DealerID straight back into the model, using:
[HttpPost]
public ActionResult Avail(SearchViewModel model)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:
- Searching from a date box and number of nights
- Then displayng a list of options which meet the search criteria
- 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