I'm still relatively new to MVC 3. I need to pass data from my #Html.Action methods through the controller to a partial view.
So here is my flow.
I'll call #Html.Action like this:
#Html.Action("SidebarMain", "Home", new List<int>(new int[] {1, 2, 3}))
Then it will hit my Controller. Here is my method in my Home Controller:
public ActionResult SidebarMain(List<int> items)
{
return View(items);
}
Then my Partial View should be able to access the data like so:
#model List<int>
#{
ViewBag.Title = "SidebarMain";
Layout = null;
}
<div>
#foreach (int item in Model)
{
<div>#item</div>
}
</div>
BUT: I'm getting a null exception for the Model, meaning It's not passing through.
Try this:
Html.Action("SidebarMain", "Home", new { items = new List<int>(new int[] {1, 2, 3}) })
And put a breakpoint in your SidebarMain Action to see, if you are getting items
In short: your code is missing the items parameter name in the Html.Action().
Other than that the code should be functional.
Html.Action("SidebarMain", "Home", new {items = new List<int>(new int[] {1, 2, 3}) })
As a suggested practice, i would use a dedicated ViewModel in my view rather than just sending the array of integers. Because, in this way of a clean ViewModel - a container of your properties that you display in the view, your code may add other properties later on, as our code always evolves.
Reference to the usage of a ViewModel concept: Exercise 5: Creating a View Model
Good answer from DarthVader. Are you returning this as Ajax? If you are embedding it in a main view, you should really return it as a PartialView with
return PartialView("SidebarMain", model);
This is where SidebarMain is the name of the partial view that you are returning. Try this in combination with what DarthVader suggested and make sure that you're getting a model to pass back to the view.
After posting, I realized that you are using Html.Action. If this is a true sidebar, it SHOULD be loaded with ajax as a partial view and you should be calling
Ajax.ActionLink("SidebarMain", "Home", new { items = new List<int>(new int[] {1, 2, 3}) })
This will allow you stay on your current page. If you aren't looking for ajax functionality, I apologize for the rabbit trail :)
DarthVader's suggestion may have worked. This is what I ended up doing:
1) Removed the controller
2) Called it like this:
#{Html.RenderPartial("SidebarMain", new int[] {1,3,4,2});}
3) Here is my view code:
#model int[]
#foreach( int item in Model){
...
Related
#{
HotelManagementEntities db = new HotelManagementEntities();
var list = db.tblCategories.Select(m => new { m.intseqid, m.varCategory}).ToList();
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
}
#Html.DropDownList("tblCategories",
ViewBag.Category as SelectList,
new
{
#class = "drplist",
id = "drpItemCat",
ng_show = "selectfieldforcat"
})
There are problems in your code, you are populating list from database here:
var list = db.tblCategories.Select(m => new { m.intseqid, m.varCategory}).ToList();
but down in the code when creating SelectList, you are again getting records form the database using the dbContext:
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
// note the db.tblCategories as first parameter, this is wrong
and if you have actually written this code in view, then you don't even need ViewBag, but your approach is not right, you should be writing the poupulation code in the controller action and then use it in the view:
public ActionResult SomeAction()
{
HotelManagementEntities db = new HotelManagementEntities();
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
}
In Controller:
public ActionResult Add()
{
ViewBag.countryList = GetCountries();
return View();
}
In razor View .csHtml
#Html.DropDownListFor(x => x.countryId, new SelectList(ViewBag.countryList, "id", "Name"), 'Select Country',
new { #id = "ddlCountry", #rows = 1 })
Despite the fact, it's not a good practice to have data retrieval in views, based on you code, you could try the following:
#{
var db = new HotelManagementEntities();
var list = db.tblCategories.Select(m => new
{
m.intseqid,
m.varCategory
}).ToList();
}
#Html.DropDownList("tblCategories", new SelectList(#list, "intseqid", "varCategory"));
A more appropriate approach it would be to create a model, which would contain as a property a SelectList object. Then at the corresponding action at the controller to create a new model and set the categories to mentioned above property. Last pass at your view (so now you have to define that your view expects a model of a specific type, that you declared above) the model and at the dropdownlist html helper do the following:
#Html.DropDownList("tblCategories", Model.Categories)
This way you are more adhered to separation of concerns that is built in the MVC pattern and your code would be more readable and maintainable in the long run. Your example is pretty simple. So that I mentioned before cannot be seen at this point. However, while you model would be more complex and you have to do more things in the views, you will see that I mean by saying more readable and maintainable.
Last but not least, try to avoid using the ViewBag. Apparently you can accomplish what you have to do with the use of ViewBag, but it's far better you pass a strongly typed object as a model to your view and have there all you need to show on the view rather than adding things in the ViewBag at the controller and then use casts in the view to do whatever you want.
This has been a thorn in my side for a while. If I use EditorFor on an array of objects and the editor Template has a form in it ex.
public class FooController:Controller {
public ActionResult Action(Foo foo) {
// ...
}
}
Index.cshtml
#model IEnumerable<Foo>
#Html.EditorFor(m=> m)
EditorTemplate
#model Foo
#using (Html.BeginForm("action", "controller"))
{
#Html.TextBoxFor(f=> f.A)
#Html.CheckBoxFor(f=> f.B)
#Html.LabelFor(f=> f.B)
}
So I'll hit a few problems.
The checkbox label's for doesn't bind correctly to the checkbox (This has to do with the label not receiving the proper name of the property ([0].A as opposed to A).
I'm aware I can get rid of the pre- text by doing a foreach on the model in Index but that screws up ids and naming as the framework doesnt realize there are multiples of the same item and give them the same names.
For the checkboxes I've just been doing it manually as such.
#Html.CheckBoxFor(m => m.A, new {id= Html.NameFor(m => m.A)})
<label for="#Html.NameFor(m => m.A)">A</label>
However I cant solve the inability of the controller to accept the item as a single model. I've even tried allowing an array of Foo's in the Action parameters but that only work when its the first item being edited ([0]...) if its any other item in the array (ex. [1].A) the controller doesn't know how to parse it. Any help would be appreciated.
Make your model a class with the properties you need.
create a class in your Models subfolder
public class MyModel {
public IEnumerable<Foo> Foolist { get ; set;}
public string Something { get;set;}
}
your EditorFor will have to have a foreach loop for Foolist...
MVC will attempt to put your model together from the form and return it to your POST action in the controller.
Edit:
You could create an EditorTemplate for foo. In Views/Shared/EditorTemplates folder, create FooTemplate.cs
#model Foo
<div class="span6 float-left" style="margin-bottom: 6px">
#Html.TextBoxFor(m => m.A, new { style = "width:190px" })
#Html.CheckBoxFor(m => m.B, new { style = "width:40px" })
#Html.ValidationMessage("foo", null, new { #class = "help-inline" })
</div>
then in your view
#foreach (var myFoo in Model)
{
#EditorFor(myFoo)
}
This still suffers from the "model gets passed back as a whole" requiredment of yours. Not sure about why there is a need to process these individually.
Hah finally solved this - Here's how I did it. As a bit of background HTML forms use the name attribute when submitting forms, but the label for element uses Id . so I only adapt the id tag to have the prefix and not the name tag.
--In the cshtml file
#{
var prefix = ViewData.TemplateInfo.HtmlFieldPrefix;
ViewData.TemplateInfo.HtmlFieldPrefix = "";
}
then I can specify the id for the properties by their prefix while letting the name remain the same like so
#Html.CheckBoxFor(m => m.A,
new {id = prefix+"."+ Html.NameFor(m => m.A)})
<label for="#prefix.#Html.NameFor(m => m.A)">A!</label></div>
I have a view which has a model that is an IEnumerable. I use DropDownListFor Html helper in a foreach loop to output dropdown lists. But it doesn't set the selected item to true. Code as below:
#model IEnumerable<Example>
#foreach (var item in Model) {
#Html.DropDownListFor(modelItem => item.FilePath, (IEnumerable<SelectListItem>)ViewBag.ConfigFiles, string.Empty, null)
}
The above code output a Html select element. But none of the options are selected even though the item.FilePath has the same value as one of the options.
This is an unfortunate limitation of using DropDownListFor() in a loop, and you need to generate a new SelectList in each iteration. However, your use of a foreach loop to generate the form controls will not work. Its creating duplicate name attributes which have no relationship to your model therefore will not bind, and its also generating duplicate id attributes which is invalid html.
Change your model to IList<T> and use a for loop and generate a new SelectList in each iteration using the constructor that sets the selectedValue
#model IList<Example>
....
#for(int i = 0; i < Model.Count; i++)
{
#Html.DropDownListFor(m => m[i].FilePath, new SelectList(ViewBag.ConfigFiles, "Value", "Text", Model[i].FilePath), string.Empty, null)
}
Note that this now generate name attributes which binds to your model
<select name="[0].FilePath">....<select>
<select name="[1].FilePath">....<select>
.... etc
Note that its not necessary to create IEnumerable<SelectListItem> in the controller. Your could instead assign a collection of your objects to ViewBag
ViewBag.ConfigFiles = db.ConfigFiles;
and in the view
new SelectList(ViewBag.ConfigFiles, "ID", "Name") // adjust 2nd and 3rd parameters to suit your property names
Rookie Question. So I have a IEnumerable Model And as you can see my question is simple. I just want to get the Value of the First item in the model Without iterating through the whole model.
As this is only one instance to happen. I don't want to itterate from the model just to get 1 Entity from it and I wouldn't also want to rely on the ViewBag or ViewData for this.
Is this possible?
Client = Model.FirstOrDefault().ClientID
Object reference not set to an instance of an object.
I also tried select()
#model IEnumerable<RMQGrainsFinalCement.ModelsCorn.POCorn>
#{
ViewBag.Title = "POIndex";
}
<h2>PO Index</h2>
#Html.ActionLink("Add PO", "Create", "POCorns", new {ClientID = Model.FirstOrDefault().ClientID },null)
To mitigate unexpected errors, do something like this in your view:
#{
var clientId = 0;
if (Model.Any())
{
clientId = Model.First().ClientId;
}
}
#Html.ActionLink("Add PO", "Create", "POCorns", new {ClientID = clientID },null)
I have 2 properties in my ViewModel
class ViewModel1
{
Dictonary<int, string> PossibleValues {get;set;}//key/value
int SelectedKey {get;set}
}
I want to edit this using a Html.DropDownListFor
I want to get MVC to auto serialize the data into/from the ViewModel so I can the following
public ActionResult Edit(ViewModel1 model) ...
What's the best way to accomplish this?
As womp said, a browser will only submit the selected value of a drop down list. This is easily bound by the default model binder, see below.
If you are not editing the PossibleValues list on the client then there is no need to submit them back. If you need to repopulate the list then do it server side in your post action by using the same method you originally populated the Dictionary with.
For example in you page:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<ViewModel1>" %>
<!-- some html here -->
<%= Html.DropDownListFor(x => x.SelectedKey, new SelectList(Model.PossibleValues, "key", "value"))%>
In your controller
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Edit() {
var model = new ViewModel1 {
PossibleValues = GetDictionary() //populate your Dictionary here
};
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Edit(ViewModel1 model) { //default model binding
model.PossibleValues = GetDictionary(); //repopulate your Dictionary here
return View(model);
}
Where GetDictionary() is a method that returns your populated Dictionary object.
See this similar question for more details
I don't think you'll be able to construct a dictionary from a dropdownlist on a form. A dropdownlist will only post one value back, which you could set as your SelectedKey property, but you won't be able to reconstruct the PossibleValues dictionary from it.
In order to reconstruct a dictionary, you're going to need to have a form field for every entry in it. You could do something like this, generated with a foreach loop over your dictionary:
<input type="hidden" name="PossibleValues[0].Key" value="key0">
<input type="hidden" name="PossibleValues[0].Value" value="value0">
<input type="hidden" name="PossibleValues[1].Key" value="key1">
<input type="hidden" name="PossibleValues[1].Value" value="value1">
.
.
.
Ultimately I would question the need to repopulate the dictionary from the form. If they can only choose one value, why wouldn't the PossibleValues just be a lookup from somewhere outside your ViewModel (like in your repository?) Why store it with the ViewModel?
The solution is custom ModelBinding in ASP.NET MVC framework here are some examples..
stevesmithblog.com/blog/binding-in-asp-net-mvc
www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx
odetocode.com/Blogs/scott/archive/2009/04/27/12788.aspx
odetocode.com/Blogs/scott/archive/2009/05/05/12801.aspx
hope you find them useful...
Thanks