Reputation: 2080
Ok, so I have this class:
public class BackstoreInventoryUtility
{
public BackstoreInventoryInfo Item { get; set; }
public List<ItemListingUtility> ListItemUtility { get; set; }
public BackstoreInventoryUtility()
{
Item = new BackstoreInventoryInfo();
ListItemUtility = new List<ItemListingUtility>();
}
}
And here's the ListItemUtility
class:
public class ItemListingUtility
{
public int Quantity { get; set; }
public string Duration { get; set; }
public List<string> AvailableDurations { get; set; }
public ItemListingUtility()
{
AvailableDurations = new List<string>();
}
}
In a view I am building, I am displaying 1 BackstoreInventoryUtility
based on a BackstoreInventoryInfo
item my user is currently browsing.
The ListItemUtility
is a class allowing the user to proceed to certain action, like display for a set time a set quantity.
The view renders like this:
@model MyApp.Utilities.BackstoreInventoryUtility
@using (Html.BeginForm())
{
<div>
@if (Model.Item.Quantity > 0)
{
<input type="submit" value="Display"/>
}
@Html.HiddenFor(_item => _item.Item.BackstoreInventoryID)
<div class="bigFontSize bold formStyle">
<label class="center">Options will eventually be displayed here.</label>
<div>
<div class="float-left">Quantity Allocated:</div>
<div class="float-right">@Html.DisplayFor(_item => _item.Item.Quantity)
@Html.HiddenFor(_item => _item.Item.Quantity)
</div>
<div class="clear"></div>
</div>
<div class="formStyle" id="itemUtilityZone">
<label>Options</label>
@for (int i = 0; i < Model.ListItemUtility.Count; i++)
{
<div>
<div class="float-left">
Quantity To Display:
</div>
<div class="float-right">
@Html.TextBoxFor(_item => _item.ListItemUtility[i].Quantity, new { @class = "positive-integer numberTextBox" })
</div>
<div class="clear"></div>
</div>
}
</div>
@if (Model.Item.Quantity > 0)
{
<input type="submit" value="Display"/>
}
</div>
}
I'd like my user to dynamically add a new row to the view, and then when the view is submitted, all the rows would be included.
So far I am at the beginning and I am trying this:
[HttpGet]
public ActionResult AddItemUtilityRow()
{
return PartialView(new ItemListingUtility());
}
Where the partial view rendered would be identical to the div used in the table. But I am not sure how could I make this happen, should I use a jQuery call? How might I do this?
EDIT Okay, so I have tried something in jquery which VISUALLY does what I want:
<script type="text/javascript">
$(document).ready(function() {
$("#addUtility").click(function() {
$.get("@Url.Action("AddItemUtilityRow")", {
}, function(data) {
$('#itemUtilityZone').append(data);
});
});
});
</script>
So, as I said, this works but only partially because when the user submits only the default number of items in the list is submitted. How can I make it so that each time the user add a row it adds up to the model and gets later submitted?
Upvotes: 1
Views: 4891
Reputation: 2080
Woah! It was more complex than I thought, but thanks to this link : http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/ I was able to make the whole thing work!
I first transfered every row created in a partial view like this:
<div class="formStyle" id="itemUtilityZone">
<label>Options</label>
@foreach (var utilityRow in Model.ListItemUtility)
{
Html.RenderPartial("ItemUtilityRow", utilityRow);
}
</div>
Which renders like this:
@using HtmlHelpers.BeginCollectionItem
@model MyApp.Utilities.ItemListingUtility
@using (Html.BeginCollectionItem("listItems"))
{
<div>
<div class="float-left">
Quantity To Display:
</div>
<div class="float-right">
@Html.TextBoxFor(_item => _item.Quantity, new { @class = "positive-integer numberTextBox" })
</div>
<div class="clear"></div>
</div>
}
Note: for the Html.BeginCollectionItem
Html Helper, I had to search a bit for Steven Sanderson's Helper which he mentions in the upper link. You can find it here:
https://github.com/danludwig/BeginCollectionItem
Next, my javascript call looks like this:
$(document).ready(function() {
$("#addUtility").click(function () {
$.ajax({
url: '@Url.Action("AddItemUtilityRow")',
cache: false,
success: function(html) {
$('#ItemUtilityZone').append(html);
}
});
});
});
And the controller method that adds a new row:
[HttpGet]
public ActionResult AddEbayUtilityRow()
{
return PartialView("ItemUtilityRow", new ItemListingUtility());
}
And the rows shows just fine now. The catch is, how do I catch it back in my post method? Well, following Steve Sanderson's blog, I understood that the listItems
variable was actually the name of the collection which would be sent back to the post method.
So by adding this parameter to the controller post method:
IEnumerable<EBayListingUtility> listItems
The list is indeed sent back to the post method with the count being what it is supposed to be. Hurray!
Upvotes: 2
Reputation: 5692
We approach this in one of two ways:
1.) Client-side approach - you can use jquery/knockout whatever to append items to your table. This is fine for simple additions, but negates the use of c# in the view.
2.) Server-side approach (and usually used) - Basically, post your viewmodel back to an action that manually adds a list item;
[HttpGet]
public ActionResult AddItemUtilityRow()
{
return PartialView(new ItemListingUtility());
}
[HttpPost]
public ActionResult AddItemUtilityRow(BackstoreInventoryUtility viewModel)
{
viewModel.ListItemUtility.Add(new ItemListingUtility());
return PartialView(viewModel);
}
We have a number of ways using jquery of 'posting' to a different action (the one that simply adds an item). I would consider using jquery's ajax call to accomplish this.
But the premise is the same:
Upvotes: 1