In my controller I set the items in the ViewBag:
List<ShopItemModel> items = new List<ShopItemModel>();
/* populate my items */
ViewBag.Items = items;
So on the cshtml i run thru the list, but how do I connect it so on postback sets the argument of the Post method in the controller?
The CSHTML:
#model Models.ShopItemModel
<h2>Webshop</h2>
#foreach( var item in ViewBag.Items)
{
using (Html.BeginForm())
{
<p>#item.Name</p> <!-- List the item name, but not bounded? -->
#Html.LabelFor(model => model.Name, new { Name = item.Name }) <!-- outputs just "Name", not the items name -->
<input type="submit" value="Buy" />
}
}
The post version of the method in the controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(ShopItemModel m)
{
return View();
}
But how do I fix this binding? So I fetch the selected item from the list?
In your view:
using (Html.BeginForm())
{
for (int i = 0; i < Model.Count; i++)
{
#Html.LabelFor(model => model[i].Name)
}
}
This will produce html controls like this:
<input name="ShopItemModel[3].Name" ...
If you're using this in a form, in your controller, iterate over the POSTed model data:
foreach (var item in model)
{
... do something to each item
}
You can use a foreach loop in the view rather than a for loop, example here
Related
So I have the following code:
#model Project.Models.ViewModels.SomeViewModel
#using (Html.BeginForm("SomeAction", "SomeController", new { id = Model.Id}))
{
for(int i = 0; i < Model.SomeCollection.Count(); i++)
{
#Html.HiddenFor(x => Model.SomeCollection.ElementAt(i).Id)
<div class="grid_6">
#Html.TextAreaFor(x => Model.SomeCollection.ElementAt(i).Text, new { #style = "height:150px", #class = "grid_6 input" })
</div>
}
<div class="grid_6 alpha omega">
<input type="submit" value="Next" class="grid_6 alpha omega button drop_4 gravity_5" />
</div>
}
On the Controller Side I have the following:
[HttpPost]
public ActionResult SomeAction(int id, SomeViewModel model)
{
return PartialView("_SomeOtherView", new SomeOtherViewModel(id));
}
My View Model is set up like this:
public class SomeViewModel
{
public SomeViewModel()
{
}
public IEnumerable<ItemViewModel> SomeCollection { get; set; }
}
public class ItemViewModel{
public ItemViewModel(){}
public int Id {get;set;}
public string Text{get;set;}
}
The SomeCollection is always empty when SomeAction if performed. What do I have to do in order to show the updated values by users. Text Property and Id field.
Use an EditorTemplate
Create an EditorTemplate folder under your Views/YourcontrollerName and create a view with name ItemViewModel.cshtml
And Have this code in that file
#model Project.Models.ViewModels.ItemViewModel
<p>
#Html.EditorFor(x => x.Text)
#Html.HiddenFor(x=>x.Id)
</p>
Now from your Main view, call it like this
#model Project.Models.ViewModels.SomeViewModel
#using (Html.BeginForm("SomeAction", "Home", new { id = Model.Id}))
{
#Html.EditorFor(s=>s.SomeCollection)
<div class="grid_6 alpha omega">
<input type="submit" value="Next" class="grid_6 alpha omega button drop_4 gravity_5" />
</div>
}
Now in your HTTPPOST method will be filled with values.
I am not sure what you want to do with the values( returning the partial view ?) So not making any comments about that.
I am not sure you have posted all the code.
Your action method does not do anything, since it returns a partial view (for some reason from a post call, not an ajax request) using a new model object.
Your effectively passing a model back to the action and then discarding it, and returning a new model object. This is the reason your collection is always empty, its never set anywhere.
Well, for one thing, why do you have both the model AND id, a property of model, sent back to the controller? Doesn't that seem a bit redundant? Also, you're using a javascript for loop in the view. It'd be much easier to just use #foreach.
Anyway, your problem is that when you tell an action to accept a model, it looks in the post for values with keys matching the names of each of the properties of the model. So, lets say we have following model:
public class Employee
{
public string Name;
public int ID;
public string Position;
}
and if I'm passing it back like this:
#using(Html.BeginForm("SomeAction", "SomeController"))
{
<input type="text" name = "name" [...] /> //in your case HtmlHelper is doing this for you, but same thing
<input type="number" name = "id" [...] />
<input type="submit" name = "position" [...] />
}
To pass this model back to a controller, I'd have to do this:
Accepting a Model
//MVC matches attribute names to form values
public ActionResult SomethingPosted(Employee emp)
{
//
}
Accepting a collection of values
//MVC matches parameter names to form values
public ActionResult SomethingPosted(string name, int id, string postion)
{
//
}
or this:
Accepting a FormCollection
//same thing as first one, but without a strongly-typed model
public ActionResult SomethingPosted(FormCollection empValues)
{
//
}
So, here's a better version of your code.
Your new view
#model Project.Models.ViewModels.SomeViewModel
#{
using (Html.BeginForm("SomeAction", "SomeController", new { id = Model.Id}))
{
foreach(var item in Model)
{
#Html.HiddenFor(item.Id)
<div class="grid_6">
#Html.TextAreaFor(item.Text, new { #style = "height:150px", #class = "grid_6 input" })
</div>
}
<div class="grid_6 alpha omega">
<input type="submit" value="Next" class="grid_6 alpha omega button drop_4 gravity_5" />
</div>
}
}
Your new action
[HttpPost]
public ActionResult SomeAction(int Id, string Text)
{
//do stuff with id and text
return PartialView("_SomeOtherView", new SomeOtherViewModel(id));
}
or
[HttpPost]
public ActionResult SomeAction(IEnumerable<ItemViewModel> SomeCollection) //can't use someviewmodel, because it doesn't (directly) *have* members called "Id" and "Text"
{
//do stuff with id and text
return PartialView("_SomeOtherView", new SomeOtherViewModel(id));
}
I have this this View for rendering form
#using ExpertApplication.ViewModels
#model IEnumerable<QuestionViewModel>
#{
ViewBag.Title = "GetQuestions";
}
#using(Html.BeginForm("ProcessAnswers", "Home", FormMethod.Post))
{
foreach(QuestionViewModel questionViewModel in Model)
{
Html.RenderPartial("QuestionPartialView", questionViewModel);
}
<input type="submit" value="Send data"/>
}
}
<h2>GetQuestions</h2>
And partial view
#using ExpertApplication.ViewModels
#model QuestionViewModel
<div id="question">
#Model.Text
<br />
<div id="answer">
#foreach(var answer in Model.AnswerViewModels)
{
#(Model.IsMultiSelected
? Html.CheckBoxFor(a => answer.Checked)
: Html.RadioButtonFor(a => answer.Checked, false))
#Html.LabelFor(a => answer.Text)
<br />
}
</div>
</div>
I want get data from From
[HttpPost]
public ActionResult ProcessAnswers(IEnumerable<QuestionViewModel> answerForQuesiton)
{
//answerForQuestion always is null
}
but parameter answerForQuesiton is null. How to fix this problem ?
MVC binds lists by using zero indexed names. Unfortunately, for this reason, while foreach loops will create inputs that contain the correct values, they will not create input names that use the correct names. Therefore, you cannot bind lists using foreach
For example:
for (int i = 0; i< Model.Foo.Count(); i++)
{
for (int j = 0; j < Model.Foo[i].Bar.Count(); j++)
{
#Html.TextBoxFor(m => m.Foo[i].Bar[j].myValue)
}
}
Will create textboxes with names like "Foo[1].Bar[2].myValue" and correctly bind. However,
foreach (var foo in Model.Foo)
{
foreach (var bar in foo.Bar)
{
#Html.TextBoxFor(m => bar.myVal);
}
}
will create text boxes with the exact same values as the prior loop, but all of them will have "name="bar.myVal" so none of them can bind.
So to fix your issue:
1) You could replace your foreach loops with for loops. Note: this requires using IList or List instead of IEnumerable
2) You could use EditorTemplates which automatically apply the correct names for you.
You are using the wrong mechanism. You should be using EditorTemplates rather than partial views. Editor Templates know how to deal with collections and creating properly formatted name attributes so they can be bound on post-back.
http://coding-in.net/asp-net-mvc-3-how-to-use-editortemplates/
this is my Model1 class
namespace chetan.Models
{
public class Model1
{
public string selectedItem { get; set; }
public IEnumerable<SelectListItem> items { get; set; }
}
}
this is my controller class
public class HomeController : Controller
{
private rikuEntities rk = new rikuEntities();
public ActionResult Index()
{
var model = new Model1
{
items = new[]
{
new SelectListItem { Value = "Theory", Text = "Theory" },
new SelectListItem { Value = "Appliance", Text = "Appliance" },
new SelectListItem { Value = "Lab", Text = "Lab" }
}
}; return View(model);
}
public ActionResult viewToController(Model1 m)
{
string getSelectedName = m.selectedItem;
return Content(getSelectedName);
}
}
this is my view...
#using (Html.BeginForm("viewToController", "Home"))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>emp</legend>
<div class="editor-field">
#Html.DropDownListFor(x => x.selectedItem,
new SelectList(Model.items, "Value", "Text"))
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
i want to add a drop downlist and i want to use selected value in viewToController action of homeController. and there is also one error in View page is "an expression tree may not contain dynamic operation" in (x=>x.selectedItem). Please solve my problem .
I don't understnad what you exactly need. You want to dynamicly add items to the drop down from the database?
I'm big fan of jQuery. You can do everything what you want with HTML using jQuery. So if you are looking how to automaticly add items to the drop down, take look at this: How do I add options to a DropDownList using jQuery?
this is my action of controller class
public ActionResult checking()
{
rikuEntities db = new rikuEntities();
IEnumerable<SelectListItem> items = db.emp.Select(c => new SelectListItem
{
Value = c.name,
Text = c.name
});
ViewBag.saan = items;
return View();
}
Now i want to use this dropdownlist value in my UserCreate.cshtml.
when i select a value from dropdownlist values and press submit then that value should store in another table student.
please suggest me what should i do for it ?
First of all you need to define your form:
using (Html.BeginForm())
{
#HTML.DropDownList("SomeName", (SelectList)ViewBag.saan)
<input type="submit" value="save" />
}
Then in your post controller:
[HttpPost]
public ActionResult checking(string SomeName)
{
...
}
If this was a problem with MVC3, there would be posts out there about this, but I can't find any. I must be doing something wrong. I have a simple view (Index.cshtml) that iterates through a list using a for loop. In each iteration, I output two text inputs with values from one of the list items.
#{Html.BeginForm();}
#Html.Encode("\n")
#for (int i = 0; i < Model.SortOptions.Count; i++ )
{
#Html.TextBoxFor(m => m.SortOptions[i].ColumnName);
#Html.Encode("\n");
#Html.TextBoxFor(m => m.SortOptions[i].Direction);
#Html.Encode("\n");
}
<input type="submit" value="Submit" />
#{Html.EndForm();}
I have two controllers for the view, one for GET requests and one for POST. The POST version adds different items to the list than the GET version. This is where the problem comes in. After the page has re-loaded, the text boxes have the same value as when the page loaded on the GET.
At first I thought it must be a caching issue, but if I modify the code (as seen below), to manually add the text inputs and inject the values into the html, the new values are sent to the browser.
#{Html.BeginForm();}
#Html.Encode("\n")
#for (int i = 0; i < Model.SortOptions.Count; i++ )
{
var columnNameName = string.Format("SortOptions[{0}].ColumnName", i);
var columnNameID = string.Format("SortOptions_{0}__ColumnName", i);
var directionName = string.Format("SortOptions[{0}].Direction", i);
var directionID = string.Format("SortOptions_{0}__Direction", i);
<input type="hidden" name="#columnNameName" id="#columnNameID" value="#Model.SortOptions[i].ColumnName" />
<input type="hidden" name="#directionName" id="#directionID" value="#Model.SortOptions[i].Direction" />
}
<input type="submit" value="Submit" />
#{Html.EndForm();}
I've stepped through the code to ensure that the model contains the expected values at the time they are sent to the view. I even inspected the values of the list by stepping through the code in the view. It appears to have the correct values, but when I view it in the browser, it has the values that should correspond to when the page responded to the GET request. Is this a problem with the editor templates? I just started using mvc3 and the razor engine, so there is a lot I don't know. Any help would be appreciated.
----- UPDATE: ADDED CONTROLLER CODE ----
[HttpGet]
public ActionResult Index()
{
var inv = new InventoryEntities();
var model = new IndexModel(inv);
model.SortOptions = new List<SortOption>();
model.SortOptions.Add(new SortOption { ColumnName = "Model", Direction = SortDirection.Ascending });
model.SortOptions.Add(new SortOption { ColumnName = "Make", Direction = SortDirection.Ascending });
//Load data
model.LoadEquipmentList();
return View(model);
}
[HttpPost]
[OutputCache(Duration = 1)]
public ActionResult Index(List<SortOption> sortOptions, SortOption sort)
{
var inv = new InventoryEntities();
var model = new IndexModel(inv);
ModelState.Remove("SortOptions");
model.SortOptions = new List<SortOption>();
model.SortOptions.Add(new SortOption { ColumnName = "Type", Direction = SortDirection.Descending });
model.SortOptions.Add(new SortOption { ColumnName = "SubType", Direction = SortDirection.Descending });
model.EquipmentList = new List<EquipmentListItem>();
model.EquipmentList.Add(new EquipmentListItem { ID = 3, AssignedTo = "Mike", Location = "Home", Make = "Ford", Model = "Pinto", Selected = false, SubType = "Car", Type = "Vehicle" });
return View(model);
}
Remember that Html helpers such as TextBoxFor first use the model state when binding their values and after that the model. Let's consider a very simple example in order to illustrate what this means:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel { Name = "foo" });
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
model.Name = "bar";
return View(model);
}
}
and the view:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.TextBoxFor(x => x.Name)
<input type="submit" value="OK" />
}
Now when you submit the form you will expect that the value in the textbox changes to "bar" as that's what you've put in your POST action but the value doesn't change. That's because there is already a value with the key Name in the model state which contains what the user entered. So if you want this to work you need to remove the original value from the model state:
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// remove the original value if you intend to modify it here
ModelState.Remove("Name");
model.Name = "bar";
return View(model);
}
The same thing happens in your scenario as well. So you might need to remove the values you are modifying from the model state in your POST action.
A couple things pop out at me - without seeing a bit more it's hard to say but...both of these could be rewritten as such. The extra # symbols are not necessary.
#using(Html.BeginForm()) {
Html.Encode("\n")
for (int i = 0; i < Model.SortOptions.Count; i++ ) {
Html.TextBoxFor(m => m.SortOptions[i].ColumnName);
Html.Encode("\n");
Html.TextBoxFor(m => m.SortOptions[i].Direction);
Html.Encode("\n");
}
<input type="submit" value="Submit" />
}
#using (Html.BeginForm()) {
Html.Encode("\n");
for (int i = 0; i < Model.SortOptions.Count; i++ ) {
var columnNameName = string.Format("SortOptions[{0}].ColumnName", i);
var columnNameID = string.Format("SortOptions_{0}__ColumnName", i);
var directionName = string.Format("SortOptions[{0}].Direction", i);
var directionID = string.Format("SortOptions_{0}__Direction", i);
<input type="hidden" name="#columnNameName" id="#columnNameID" value="#Model.SortOptions[i].ColumnName" />
<input type="hidden" name="#directionName" id="#directionID" value="#Model.SortOptions[i].Direction" />
}
<input type="submit" value="Submit" />
}
Otherwise your stuff looks right and I can't see anything wrong off the top of my head.