I'm a bit confused.
I have a pretty standard MVC3 setup: Home Controller/Index method, Strongly typed view to a ViewModel.
My ViewModel is a collection of three generic lists (Schemes, Types, Tables),
TableModel
Schemes List
TypesList
TablesList
Schemes and Types populate dropdowns to act as filters on the third (Tables) collection which populates a HTML table via a #foreach loop
I populate these via a call to Home/Index (no arguments). This works fine. Now I want to mark a number of rows from the TablesList collection for deletion by checking a checkbox and then, on clicking Submit, I want the deletion to be actioned via a Post and the table contents to be refreshed with the remaining tables back into the same place (replacing the existing HTML table
In my Index View I have this section:
#using (Html.BeginForm("DeleteTables", "Home"))
{
#Html.Hidden("tableList", #Model.TablesList)
<input id="deleteButton" type="submit" />
}
in my HomeController I have a POST method as follows
[HttpPost]
public ActionResult DeleteTables(List<ViewModel.ITableInformation> tableList)
{
var deleteableTableIds = from t in tableList
where t.DeleteTable
select t.TableId;
this.tableModel.DeleteTablesById(deleteableTableIds.ToList());
.... execute a db call to get remaining tables and return a PartialView or JSON
}
Problem is I'm expecting the tableList argument to this function to contain my model but it's empty.
Any idea what I'm doing wrong - pretty new to this I'm afraid.
Thanks
The hidden can't take a complex object; what you would need to do is deserialize each object and store it in the hidden as a string, something like:
#{
var tableIDs = "";
foreach (var i in Model.TablesList) {
tableIDs = //serialized object in proper format
}
}
#Html.Hidden("tableList", #tableIDs)
From the server, you can create the objects through a custom model binder, or accept an input of type string, and deserialize the collection.
Related
In the view I have a Model that consists of 2 Lists. The Model.Ingredients is full of Ingredient objects, the second one UsedIngredients is initialized but empty.
If the user clicks on an ingredient i want to add it to the empty list, because i want to pass that list to a partial view.
#foreach (var item in Model.Ingredients)
{
if (item.Type == "spirit")
{
<a onclick="#Model.UsedIngredients.Add(item)">#item.Name</a>
}
}
If I Try this I get an error:
Cannot implicitly convert type 'void' to 'object'.
I have the list initialized correctly i think:
List<Models.Extended.Ingredient> list = new List<Models.Extended.Ingredient>();
creator.UsedIngredients = list;
Ok turns out I’m kinda dumb because why would it work? We cannot run server side code onclick action like this. So I have to use session variables and httpPost method.
I have a view that has been developed by another developer in which there is already code for a javascript that should handle the JSON Object and format as an HTML Table.
I'm new into MVC, and from the Controller, I create a JSON Object that contains what I need. The issue is that from my Controller, if I just return the JSON Object, then the browser just shows the raw JSON string to the client.
It seems that when I return a JSON Object, the browser just shows the JSON, without actually calling my view that has the code to handle the JSON and making it user-friendly.
This is my controller:
public JsonResult GetPlayerNameByID(int playerID)
{
var player = GetPlayerByID(playerID);
return Json(player, JsonRequestBehavior.AllowGet);
}
This is called trough the on click event of a Dropdown List.
The view for that page is like this:
#model FirstApp.Models.PlayerViewModel
<div id="container" class="container">
<table class="table player">
<thead>
<tr>
<th class="Name">Name</th>
<th class="Overall">Overall</th>
</tr>
</thead>
<tbody id="tableBody"></tbody>
</table>
I believe my issue is that the controller doesn't return to that view, but just a raw JSON Object.
Any suggestion on how can I return to that view (this is the view from which the call has been made).
In order to return a view, you need to return a view. Something like:
return View();
Or, if it needs to include a model:
return View(someModel);
But your controller action is just returning JSON data:
return Json(player, JsonRequestBehavior.AllowGet);
You can't return both. So it sounds like you have two options:
Return a view with the player object as its model. In this case the view would render the data from the model directly and there would be no need for JSON or any involvement with JavaScript. (Well, there could be, depending on what your view is and what you need to do. But it wouldn't be at the controller level.)
Have two controller actions. One which returns the view, another which returns the JSON data. The client-side JavaScript code would make an AJAX request to the second action to fetch the JSON data.
Which one you choose is up to you really. But ultimately you can't return both a view and raw JSON from the same action.
To give an example of the second option, you would have two actions:
public ViewResult Player(int playerID)
{
return View(playerID);
}
public JsonResult GetPlayerNameByID(int playerID)
{
var player = GetPlayerByID(playerID);
return Json(player, JsonRequestBehavior.AllowGet);
}
The first method would return your Player view (assuming you have one of course), with just an integer as the model. So the view would start with a declaration for that model type:
#model int
This means that inside the view code you would be able to access the playerID in the variable Model. So in the view's JavaScript code you might capture that into a variable:
let playerID = #Model;
Which, if for example the playerID value were 123, this would render client-side as:
let playerID = 123;
From there you'd use AJAX to make a request to #Url.Action("GetPlayerNameByID") to get the JSON data. How you do this would depend on what JavaScript frameworks/libraries you're using, if any, and there are many examples available online for how to use AJAX in a variety of frameworks and in ASP.NET.
I am trying to pass a 2nd List of objects from the Controller to the
View via the ViewBag
Here is the line from my controller code.
ViewBag.FeaturedProductList = await Service.SendAsync(new ProductQuery());
The return object is the following
public class FeaturedProductListDto : IDto
{
public IEnumerable<FeaturedProductDto> Contents { get; set; }
}
In the View, I need to do a linq to select from the
ViewBag.ViewBag.FeaturedProductList in the following line.
#foreach (var productGroup in ViewBag.FeaturedProductList.Select((e,
i) => new {Product = e, Grouping = (i/3)}).GroupBy(e => e.Grouping))
{
}
I need to group the number of items from the list in sets of 3 but the
Select is throwing an error as the following
Cannot use a lambda expression as an argument to a dynamically
dispatched operation without first casting it to a delegate or
expression tree type.
I used the same code for my other List which I passed in as the Model
and it works.
This line works. #foreach (var productGroup in Model.Select((e, i) =>
new { Product = e, Grouping = (i / 4) }).GroupBy(e => e.Grouping))
Do I need to recast the ViewBag.FeaturedProductList? What is the fix
for this? Any help is greatly appreciated. Thanks.
ViewBag is a dynamic dictionary. Items in this dictionary are of type dynamic. You cannot use LINQ methods on that.
You need to cast ViewBag.FeaturedProductList to FeaturedProductListDto type and use the Contents property which is a collection type on which we can apply LINQ extension methods.
#foreach (var item in ((FeaturedProductListDto) ViewBag.FeaturedProductList).Contents
.Select(//your existing select code goes here))
{
}
I am not quite sure what you are trying to do with the GroupBy inside your Select. My personal preference is doing all such things in the action method/another layer which provides data to the action method and keep the razor markup with minimal C# code, and with more HTML markup for the page. :)
I am intending to pass a Hotel model to my Controller Action - Do some checks/processing on it, then return a potentially different Hotel model rendered in a Partial View.
The problem I'm getting is that if I pass the oHotelParameter Model to the Action then the PartialView uses the model passed to the Action instead of the one passed to the PartialView method.
If I remove the oHotelParameter Parameter from the Action then the View is Rendered as expected using oHotel.
public ActionResult _SaveMasterDetails(Hotel oHotelParameter)
{
//Do some processing on oHotelParameter
//........
Hotel oHotel = new Hotel();
oHotel.GetHotelInfoById(14); //This gets a different Hotel object just for a test
//For some reason oHotel is ignored and oHotelParameter is used instead unless I remove oHotelParameter
return PartialView("_MasterDetails", oHotel);
}
When I debug the View I see that the Model is set to the value I pass to PartialView (oHotel), yet the result I see coming back from the Action contains data from the oHotelParameter object.
In case it makes a difference, I am calling the Action from jQuery ajax.
Can anyone explain why this should happen?
when mvc handles a form post, it fills the ModelState object with the details of the model.
This is when used when the view is rendered again from the post action, this is incase you have thrown the view back out because it has failed validation.
If you want to pass out a new model and not use the view state, then you can call ModelState.Clear() before returning the view and that should let you rebind the view to the new model.
I think that it would help if you had a better understanding of how model binding works when you post back to an action method. In most cases, it is unecessary and inefficient to pass a view model as a parameter to a POST action method. What you are doing is loading the view model into memory twice when you pass your view model as a parameter (assuming a strongly typed view). When you do a post back the model becomes part of the form collection (through model binding) in the request object in the BaseController class that every controller inherits from. All that you need to do is to extract the model from the Form collection in the Request object in the BaseController. It just so happens that there is a handy method, TryUpdateModel to help you do this. Here is how you do it
[POST]
public ActionResult Save()
{
var saveVm = new SaveViewModel();
// TryUpdateModel automatically populates your ViewModel!
// TryUpdateModel also calls ModelState.IsValid and returns
// true if your model is valid (validation attributes etc.)
if (TryUpdateModel(saveVm)
{
// do some work
int id = 1;
var anotherSaveVm = GetSaveVmBySomeId(id);
// do more work with saveVm and anotherSaveVm
// clear the existing model
ModelState.Clear();
return View(anotherSaveVm);
}
// return origonal view model so that the user can correct their errors
return View(saveVm);
}
I think that the data in the form collection contained in the request object is being returned with the view. When you pass the model back to the post action method as a parameter, I believe it is passed in the query string (see Request.QueryString). Most of the time, it is best to only pass one or two primitive type parameters or primitive reverence types such as int? to an action method. There is no need to pass the entire model as it is already contained in the Form collection of the Request object. If you wish to examine the QueryString, seee Request.QueryString.
I am trying to understand the best way of implementing a DropDownList in ASP.NET MVC 2 using the DropDownListFor helper. This is a multi-part question.
First, what is the best way to pass the list data to the view?
Pass the list in your model with a SelectList property that contains the data
Pass the list in via ViewData
How do I get a blank value in the DropDownList? Should I build it into the SelectList when I am creating it or is there some other means to tell the helper to auto create an empty value?
Lastly, if for some reason there is a server side error and I need to redisplay the screen with the DropDownList, do I need to fetch the list values again to pass into the view model? This data is not maintained between posts (at least not when I pass it via my view model) so I was going to just fetch it again (it's cached). Am I going about this correctly?
Your best bet is to create a SelectList in your Controller - use my extension method here:
http://blog.wekeroad.com/2010/01/20/my-favorite-helpers-for-aspnet-mvc
Pop that into ViewData using the same key as your property name:
ViewData["statusid"]=MySelectList
Then just use Html.DropDownFor(x=>x.StatusID) and you're all set.
Answering in parts:
The best way IMHO is to pass the list in the ViewModel like this:
public SelectList Colors
{
get
{
// Getting a list of Colors from the database for example...
List<Color> colors = GetColors().ToList();
// Returning a SelectList to be used on the View side
return new SelectList(colors, "Value", "Name");
}
}
To get a blank or default option like ( -- Pick a color -- ), you can do this on the view side:
#Html.DropDownListFor(m => m.Color, Model.Colors, "-- Pick a color --")
You'll have to fetch/populate the list again if it's part of the ViewModel.
Take a look at the following blog post. It can give you some tips:
Drop-down Lists and ASP.NET MVC
You could do something like:
<%= Html.DropDownListFor((x => x.ListItems), Model.ListItems, "")%>
or
<%= Html.DropDownList("ListItems", Model.ListItems, "")%>
The last param 'optionLabel' makes a blank list item
In this case, you can see ListItems is a property of the model.
I have made the view strongly typed to the model also.
(You know this already!)
Pass the list in your model with a SelectList property that contains the data
Yes, add it when you build the SelectList. (If you build the list using LINQ, Union might come in handy.)
Yes do do, and yes you are.
I find it more intuitive to work with a sequence of SelectListItems (rather than a SelectList).
For example, this would create an IEnumerable<SelectListItem> from a sequence of customer objects that you can pass to the Html.DropDownListFor(...) helper. The 'Selected' property will optionally set the default item in the dropdown list.
var customers = ... // Get Customers
var items = customers.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCustomerId),
Text = c.Email,
Value = c.Id.ToString()
});