ViewModel comes as null from view to controller - asp.net-mvc

I have my code as below.Can somebody help me in this I am not able to send model to view.
ViewModel class
IEnumerable<CarList> MyCarPositions.
In my view.
#model TestMVC.ViewModel.
foreach (var item in Model.MyCarPositions)
{
#Html.TextBoxFor(x => item.Name)
#Html.TextBoxFor(x => item.Brand)
}
My Controller
ViewModel carviewmodel = new carviewmodel();
[HttpGet]
public ActionResult Index()
{
carviewmodel.MyCarPositions = repository.GetCarPositions();
return View(carviewmodel);
}
[HttpPost]
public ActionResult Index(ViewModel carvmodel)
{
// Here in httppost carvmodel comes as null.
}

Your foreach loop is generating duplicate name attributes without indexers which cannot be bound to your model. Its also generating invalid html because of the duplicate id attributes. You need to generate the collection using a custum EditorTemplate for the type in your collection, or use a for loop.
You have not shown you models, but property MyCarPositions needs to be List<T> if using the for loop option.
#model TestMVC.ViewModel
#using Html.BeginForm())
{
for (int i = 0; i < Model.MyCarPositions.Count, i++)
{
#Html.TextBoxFor(x => x.MyCarPositions[i].Name)
#Html.TextBoxFor(x => x.MyCarPositions[i].Brand)
}
<input type="submit" .. />
}
This will generate the correct name attributes necessary for binding
<input type="text" name="MyCarPositions[0].Name" .... />
<input type="text" name="MyCarPositions[1].Name" .... />
<input type="text" name="MyCarPositions[2].Name" .... />
Side note: You should be initializing the model inside the controller method
// ViewModel carviewmodel = new carviewmodel(); remove this
[HttpGet]
public ActionResult Index()
{
ViewModel carviewmodel = new carviewmodel(); // initialize it here
carviewmodel.MyCarPositions = repository.GetCarPositions();
return View(carviewmodel);
}

Right away I can see a syntax error that might be causing your issue.
foreach (var item in Model.MyCarPositions)
{
#Html.TextBoxFor(x => item Name <--- missing a '.' and a closing bracket
#Html.TextBoxFor(x=>item.Brand)
}
Should be:
foreach (var item in Model.MyCarPositions)
{
#Html.TextBoxFor(x =>item.Name)
#Html.TextBoxFor(x=>item.Brand)
}

Related

Trying to Post a Model back to the Controller, but it creates a new model instead

Tpa class is my base model.
public class Tpa
{
public bool selected { get; set; }
public int Id { get; set; }
}
Data class creates a list of Tpa objects.
public class Data
{
public List<Tpa> Tpas { set; get; }
public Data()
{
this.Tpas = new List<Tpa>();
this.Tpas.Add(new Tpa()
{
selected = false ,
Id = 1,
});
this.Tpas.Add(new Tpa()
{
selected = false,
Id = 2,
});
this.Tpas.Add(new Tpa()
{
selected = true,
Id = 3,
});
}
}
This is my Get.
[HttpGet]
public virtual ActionResult Index()
{
var model = new Data();
return View(model);
}
This is my view.
#model TpaUpload_2.Models.Data
#using (Html.BeginForm(MVC.TpaUpload_2.Home.ReceiveID(), FormMethod.Post))
<table class="table">
<tr>
#for (int count = 0; count < Model.Tpas.Count; count++)
{
var item = Model.Tpas[count];
<tr>
<td>
<input type="checkbox"
name=#Html.Raw("'s" + count + "CheckBox'")
id=#Html.Raw("'s" + count + "CheckBox'")
#*checked="#(item.selected == true)"*# />
<label for=#Html.Raw("'s" + count + "CheckBox'" )></label>
<input type='hidden'
id=#Html.Raw("'s" + count + "CheckBox'" )
name='item.selected'
value=#Html.Raw("'"+item.selected+"'")/>
</tr>
</table>
<input type="submit" value="Submit" />
This my Post.
[HttpPost]
public virtual ActionResult ReceiveID(Data myData)
{
...
}
I'm trying to use the checkbox value to change the "selected" on the model, and post back the model.
The problem is after the Form is submitted to the Post, the program will construct a new Data object, instead of using the Data model passed to the controller.
What did I do wrong? Any help will be greatly appreciated.
Your constructing html with name attributes that have absolutely no relationship to you model. When you submit, the DefaultModelBinder first initializes your Data model (which means that 3 new Tpa objects are added to its Tpas property. It then tries to find name/value pairs in the form collection that match you model properties but there are none.
First you need to modify you constructor to include only the initialization of the list, and remove the adding of the new items
public class Data
{
public List<Tpa> Tpas { set; get; }
public Data()
{
Tpas = new List<Tpa>();
}
}
And add the items in the GET method
public virtual ActionResult Index()
{
var model = new Data();
model.Tpas .Add(new Tpa(){ selected = false, Id = 1 });
// add other items
return View(model);
}
Then you need to construct you view correctly using the strongly typed html helpers so that your form controls are correctly named in relationship to your model
<table class="table">
#for (int i = 0; i < Model.Tpas.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Tpas[i].Id)
#Html.CheckBoxFor(m => m.Tpas[i].selected)
#Html.LabelFor(m => m.Tpas[i].selected)
</td>
</tr>
}
</table>
This give your controls the correct name attribute for model binding, for example
<input type="hidden" name="Tpas[0].Id" ... />
<input type="hidden" name="Tpas[1].Id" ... />
<input type="hidden" name="Tpas[2].Id" ... />
I suggest you compare that with what your currently generating to understand the difference.
Note also your current html is invalid - you have multiple <tr> elements inside a <tr> elements and you need to include the hidden input for the Id property or else this will not post back and you will end up with 3 Tpa objects all with id = 0.

MVC ERROR Illegal characters in path.

can someone tell me if im on the right track? Im trying to display my query but i get an error. I have two textbox with the same parameter and that parameter is declared as an IEnumerable.
[HttpPost]
public ActionResult Orders1(IEnumerable<int> order)
{
using (CostcoEntities1 context = new CostcoEntities1())
{
var query = string.Empty;
foreach (var orderID in order)
{
query = (from a in context.CM_Checkout_Details
where a.CheckoutDetails_ID == orderID
select a).ToString();
}
return View(query);
}
}
this is what my controller looks like..
I am trying to read the two numbers(Id) in the text box and diplay data based on those id.
#using (Html.BeginForm("Orders1", "Track", FormMethod.Post))
{
#Html.TextBox("order")<br />
#Html.TextBox("order")
<input type="submit" value="Submit" />
}
First thing, change the names of the textboxes so that they are not the same:
#using (Html.BeginForm("Orders1", "Track", FormMethod.Post))
{
#Html.TextBox("order1")<br />
#Html.TextBox("order2")
<input type="submit" value="Submit" />
}
Next, change the signature of your action method:
[HttpPost]
public ActionResult Orders1(string order1, string order2)
MVC's model binding will try to match order1 and order2 to stuff in Request.Form, for example, which should pick up the textbox values.

refreshing / reloading the PartialView inside the current view

I have a PartialView that is an image upload, and basically I am displaying some images and then the normal Upload buttons :-
#model MvcCommons.ViewModels.ImageModel
<table>
#if (Model != null)
{
foreach (var item in Model)
{
<tr>
<td>
<img src= "#Url.Content("/Uploads/" + item.FileName)" />
</td>
<td>
#Html.DisplayFor(modelItem => item.Description)
</td>
</tr>
}
}
</table>
#using (Html.BeginForm("Save", "File", FormMethod.Post, new { enctype = "multipart/form-data" })) {
<input type="file" name="file" />
<input type="submit" value="submit" /> <br />
<input type="text" name="description" />
}
Now my idea is to have this in different pages. I tried it in 1 page already and is working fine, however when I Upload an image,
public ActionResult ImageUpload()
{
ImageModel model = new ImageModel();
model.Populate();
return View(model);
}
I want to go back to the "previous" View, ie the View that is hosting this partial view? When I do return View(model) as above, I get into the ImageUpload partial view which I do not want to.
Thanks for your help and time.
***UPDATE*********
I went for the simple route for the time being, and hard coded the actual View name
public ActionResult ImageUpload()
{
ImageModel model = new ImageModel();
model.Populate();
return View("~/Views/Project/Create.cshtml", model);
}
however I got an error :-
The model item passed into the dictionary is of type MvcCommons.ViewModels.ImageModel, but this dictionary requires a model item of type MvcCommons.Models.Project.
Use the overload that takes a string of the name of the view you want.
http://msdn.microsoft.com/en-us/library/dd460310
protected internal ViewResult View(
string viewName,
Object model
)
i.e.
return View("ViewName", model);
if you have this in different pages then you can inject context via the action paramaters;
public ActionResult ImageUpload(string parentViewName)
{
ImageModel model = new ImageModel();
model.Populate();
return View(parentViewName, model);
}
NOTE: You should only need to pass the views name not the path:
return View("Create", model);

MVC3 ASP using if statements in model-based elements (TextboxFor / CheckboxFor etc)

I am using a model in MVC3 to populate textboxes on my page like so :
<input name="test" value="a" type="radio" id="emp_contributions_gbp" #if (Model.myvalue.ToString() == "x"){<text>checked=true</text>}>
This works perfectly fine for saying - "if model.myvalue = x, then check this box"
However, I want to be able to return this model to the controller so I can persist the data should it be updated.
I am now using:
#Html.TextBoxFor(m => m.someField) which works perfectly with the model, however I have no idea how I could use .CheckboxFor and my IF statements together
You can use the CheckBoxFor method like so:
#Html.CheckBoxFor(m => m.SomeProperty, new { checked = Model.myvalue.ToString() == "x" })
See here for the MSDN documentation of this overload.
Update
Consider using the RadioButtonFor method instead:
Model:
public class MyViewModel
{
[Required]
public string SomeProperty { get; set; }
}
View:
#using (Html.BeginForm())
{
<div>A: #Html.RadioButtonFor(x => x.SomeProperty, "a")</div>
<div>B: #Html.RadioButtonFor(x => x.SomeProperty, "b")</div>
<div>C: #Html.RadioButtonFor(x => x.SomeProperty, "c")</div>
<input type="submit" />
}
You can then preselect some radio by setting the view model property to the corresponding value:
public ActionResult Index()
{
var model = new MyViewModel
{
SomeProperty = "a" // select the first radio
};
return View(model);
}

CheckboxFor not binding with nested objects

CheckBoxFor is not bounded when a property is defined in an object nested in the model?
Here is an example. I have a SearchOptions model that contains a List<Star> property. Each Star has a number, a name and a bool property that should be bounded:
public class SearchOptions
{
public SearchOptions()
{
// Default values
Stars = new List<Star>()
{
new Star() {Number=1, Name=Resources.Home.Index.Star1,
IsSelected=false},
new Star() {Number=2, Name=Resources.Home.Index.Star2,
IsSelected=false},
new Star() {Number=3, Name=Resources.Home.Index.Star3,
IsSelected=true},
new Star() {Number=4, Name=Resources.Home.Index.Star4,
IsSelected=true},
new Star() {Number=5, Name=Resources.Home.Index.Star5,
IsSelected=true},
};
}
public List<Star> Stars { get; set; }
}
In my strongly typed View (of SearchOptions) i loop over Stars property:
#using (Html.BeginForm("Do", "Home"))
{
<fieldset>
<legend>#MVC3TestApplication.Resources.Home.Index.Search</legend>
#{
foreach (Star s in Model.Stars)
{
#Html.CheckBoxFor(m => s.IsSelected)
<label>#s.Name</label>
}}
</fieldset>
<input type=submit value="Invia" />
}
The (relevant part of) controller is:
public ActionResult SearchOptions()
{
return View(new SearchOptions());
}
[HttpPost]
public ActionResult Do(SearchOptions s)
{
// Do some stuff
return View("SearchOptions", s);
}
It's because of how you're accessing the properties in the CheckBoxFor expression.
#for (int i = 0; i < Model.Stars.Count(); i++) {
#Html.CheckBoxFor(m => m.Stars[i].IsSelected)
<label>#Model.Stars[i].Name</label>
}
This should work for you.
Here's the output from the different methods:
//using the for loop
<input id="Stars_2__IsSelected" name="Stars[2].IsSelected" type="checkbox" value="true" />
//using the foreach
<input checked="checked" id="s_IsSelected" name="s.IsSelected" type="checkbox" value="true" />
You'll notice that the for foreach doesn't contain the proper name for it to match to when doing model binding.

Resources