I have a strongly typed view with the following model.
public class ProductViewModel
{
public Product Product { get; set; }
public List<ProductOptionWithValues> ProductOptionsWithValues { get; set; }
}
public class ProductOptionWithValues
{
public ProductOption ProductOption;
public List<AllowedOptionValue> AllowedOptionValues;
}
I'm using this Model To populate a form where a user can select the options they want for a product.
This is the view.
#model AsoRock.Entities.ViewModels.ProductViewModel
#{
ViewBag.Title = "Details";
}
#using (Html.BeginForm(new { ReturnUrl = ViewBag.ReturnUrl }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<h3>
#Html.DisplayFor(model => model.Product.ProductName)
----> #Html.DisplayFor(model => model.Product.Price)
</h3>
<br/>
foreach (var item in Model.ProductOptionsWithValues)
{
<b>#Html.DisplayFor(modelItem => item.ProductOption.Option.OptionName)</b>
<br/>
#Html.DropDownListFor(m => m.ProductOptionsWithValues,
new SelectList(item.AllowedOptionValues,
"Id", "DisplayString",
item.AllowedOptionValues.First().Id))
<br/>
}
<input type="submit" value="Add to cart" />
}
In my controller I am trying to pass the model back. When I set a break point in the controller it hits it but the Product view model is empty, any ideas how I can get the values that are selected in the view back in to my controller?
[HttpPost]
public ActionResult Details(ProductViewModel ProductViewModel)
{
return View();
//return View();
}
As mentioned in the comments, you need to change the name of the viewmodel parameter from ProductViewModel to something else e.g.
[HttpPost]
public ActionResult Details(ProductViewModel viewModel)
{
}
Now it's very odd that the viewModel param is not set to an instance of the class. The MVC model binder will still create an instance of ProductViewModel even if none of it's properties are set to anything. You're not using a custom model binder by any chance?
Also I would very strongly suggest that your viewmodel class does not have a Product property. Instead, create properties in the viewmodel specifically for the Product properties you intend to use e.g.
public class ProductViewModel
{
public string ProductName { get; set; }
public decimal ProductPrice { get; set; }
public List<ProductOptionWithValues> ProductOptionsWithValues { get; set; }
}
Using Product in the viewmodel sort of defeats the point of having a viewmodel. The viewmodel should contain only the bare minimum that the view needs. Including Product means the viewmodel is now bloated with extra data it does not use/need.
EDIT:
In your shoes, I would strip down the view itself, using only little bits of the viewmodel, and POST to the controller to see what happens. If the viewmodel calss is not NULL, go back to the view and add another bit back. Keep doing this until the viewmodel is NULL again. Doing this bit by bit should help.
Related
I looked up everywhere and tried multiple solutions but none of them would work. In my MVC website, I have a cart section and a checkout section. I want to make it so inside my checkout section, i have a small cart section to show the cart.
This is my partial view _CartItems.cshtml
#model IEnumerable<ArrowDefenseSystems.Models.Cart>
#{
ViewBag.Title = "Your Cart";
}
#if (Model != null)
{
foreach (var item in Model)
{
<div class="itemInfo row">
<img class="col-md-3" src="#Url.Content("~/Content/" + #Html.DisplayFor(modelItem => item.productImage))" height="100px">
<div class="CartItemText col-md-9">
<h3>#Html.DisplayFor(modelItem => item.productName)</h3>
<i>$#Html.DisplayFor(modelItem => item.productPrice)</i><br>
Quantity : #Html.DisplayFor(modelItem => item.quantityChosen)<br>
</div>
</div>
<hr />
}
}
When I launch the Partial View by itself, it shows the items fine and everything runs correctly.
This is how I'm linking the partial view to the view Checkout.cshtml
#model ArrowDefenseSystems.Models.ParentViewModel
...
...
#Html.Partial("_CartItems", Model.Cart)
When I run this code i get the following error on the code above:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
System.Web.Mvc.WebViewPage<TModel>.Model.get returned null.
ParentViewModel:
public class ParentViewModel
{
public Checkout Checkout { get; set; }
public Cart Cart { get; set; }
}
Checkout Controller (theres more but its unnecessary):
[HttpGet]
public ActionResult _CartItems()
{
return PartialView(db.Carts.ToList());
}
public ActionResult Checkout()
{
return View();
}
I've tried many solutions but all return the same error. What am I missing?
I think there is a fundamental misunderstanding here: #Html.Partial("_CartItems", Model.Cart) will not call your _CartItems() action. Instead, it will create a new instance of the _CartItems.cshtml partial view, and use your Model.Cart parameter as it's Model.
In this case, there are two problems:
_CartItems.cshtml is expecting a model with a type of IEnumerable<ArrowDefenseSystems.Models.Cart>, whereas you are passing it a model with a type of Cart (via the Model.Cart parameter)
Model.Cart is not being instantiated in your Checkout action
So, to fix this, you should first change your ParentViewModel to be:
public class ParentViewModel
{
public Checkout Checkout { get; set; }
public IEnumerable<Cart> Carts { get; set; }
}
Then, update your Checkout action to be:
public ActionResult Checkout()
{
var viewModel = new ParentViewModel
{
Carts = db.Carts.ToList()
}
return View(viewModel);
}
And change your usage of Model.Cart to Model.Carts (due to the rename above)
After that, you can remove your _CartItems action as it is no longer used.
I have a view model that is used to display a form on one view, and then is also used to represent the POST data to an action. The action then displays another view model that contains much of the same data from the first view model. However, the first view model has several "display only" properties that are also required on the second view model (for display only on the second view also).
I am wondering what the best way to pass this "display only" data to the second view would be. Currently, the best solution I have come up with is to have a bunch of hidden form fields that contain the display only property values, and then the model gets auto-populated for the action that handles the form POST. However, using hidden form fields seems very "hackish", and there seems like there should be a better solution to passing this data to another view The action doesn't need the display only information, it is only accessing it to populate the properties of the second view model that is passed to the second view.
Let me just explain my question with code, as what I am after is probably better understood through code than words.
Models:
public class SearchFilters
{
// ...
}
public class SearchResult
{
public int Id { get; set; }
public bool Selected { get; set; }
public string SomeDisplayValue1 { get; set; }
public string SomeDisplayValue2 { get; set; }
// ...
}
public class ResultsViewModel
{
public IList<SearchResult> Results { get; set; }
// ...
}
public class DoSomethingWithSelectedResultsViewModel
{
public IList<SearchResult> SelectedResults { get; set; }
public string SomeOtherProperty { get; set; }
// ...
}
Controller:
[HttpPost]
public ActionResult Results(SearchFilters filters)
{
ResultsViewModel results = new ResultsViewModel();
// ...
return new View(results);
}
[HttpPost]
public ActionResult DoSomethingWithSelectedResults(ResultsViewModel model)
{
// ...
return View(new DoSomethingWithSelectedResultsViewModel
{
SelectedResults = model.Results.Where(r => r.Selected).ToList(),
SomeOtherProperty = "...",
// ...
});
}
View: Results.cshtml
#model ResultsViewModel
#using (Html.BeginForm("DoSomethingWithSelectedResults", "Search"))
{
<table>
for (int i = 0; i < Model.Results.Count; i++)
{
<tr>
<td>
#Html.CheckBoxFor(m => Model.Results[i].Selected)
#* I would like to eliminate these hidden inputs *#
#Html.HiddenFor(m => Model.Results[i].Id)
#Html.HiddenFor(m => Model.Results[i].SomeDisplayValue1)
#Html.HiddenFor(m => Model.Results[i].SomeDisplayValue2)
</td>
<td>#Html.DisplayFor(m => Model.Results[i].SomeDisplayValue1)</td>
<td>#Html.DisplayFor(m => Model.Results[i].SomeDisplayValue2)</td>
<tr>
}
</table>
<button type="submit">Do Something With Selected Results</button>
}
As far as I know, one of the best way to pass data from View to another View through a Controller is to use ViewBag, ViewData or TempData. As an example, you can pass the data retrieved from View I as shown below:
TempData[DataToBePassed] = model.CustomData;
And then retrieve this data in View II similar to that:
#if(TempData[DataToBePassed] != null)
{
var dataFromFirstView = TempData[DataToBePassed];
}
For more information take a look at When to use ViewBag, ViewData, or TempData in ASP.NET MVC 3 applications.
You could put the model in the TempData property of the controller, that way it's automatically available in the next request.
More here
Found what I was looking for, I just hadn't worked with MVC enough yet to know about it. The Controller.UpdateModel method does exactly what I was looking for.
Example (using the code from the question):
[HttpPost]
public ActionResult DoSomethingWithSelectedResults()
{
// Load initial model data here, in this case I had simply cached the results in
// temp data in the previous action as suggested by Emeka Awagu.
ResultsViewModel model = (ResultsViewModel)TempData["results"];
// Call UpdateModel and let it do it's magic.
UpdateModel(model);
// ...
return View(new DoSomethingWithSelectedResultsViewModel
{
SelectedResults = model.Results.Where(r => r.Selected).ToList(),
SomeOtherProperty = "...",
// ...
});
}
Using this method I was able to eliminate all the hidden form fields and did not have to write any custom copy logic, since UpdateModel deals with it automatically.
Note: I did have to implement some custom model binders to get things to work correctly with dictionaries and collections (see here, here, and here).
I have a complex model class like:
public class Client
{
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string AddressLine { get; set; }
}
My View is made of several Partial's on which I pass parts of the model into them and dispose some fields for edition:
In Index.cshtml
#using (Html.BeginForm("Index"))
{
#Html.DisplayNameFor(modelItem => model.Name)
#Html.DisplayFor(modelItem => model.Name)
<div id="divAddress">
#Html.Action("_Address", model.Address)
</div>
<div>RESULT MESSAGE GOES HERE!</div>
<input type="submit" value="Submit" />
}
In _Address.cshtml
#Html.DisplayNameFor(modelItem => model.AdressLine)
#Html.EditorFor(modelItem => model.AdressLine)
On the code-behind my Actions consist of two simple ActionResults methods:
[HttpGet]
public ActionResult Index()
{
Client = new Client();
Client.Name = "António Fonseca"
return View(model);
}
[HttpPost]
public ActionResult Index(Client model)
{
return View(model);
}
public ActionResult _Address(Address model)
{
return View(model);
}
When I submit the form, I need to call a WebService with the full Client structure and display it's resulting message.
What happens is that when hitting Index(model) -> model.Address is null.
It's only bound back when it hits _Address(model) method.
Is there a way to bind the full class structure in main Action using PartialViews?
Change the model in _Address.cshtml to be the same as the model in your main view and use #Html.Action("_Address", model) so that the form controls are correctly named - i.e. name="Address.AdressLine" (its currently just name="AdressLine" but you model does not contain a property named AdressLine).
#model Client
#Html.DisplayNameFor(m => m.Address.AdressLine)
#Html.EditorFor( m=> m.Address.AdressLine)
However using #Html.Action() is not the correct approach for this. You should be using an EditorTemplate. Rename _Address.cshtml to Address.cshtml (to match the name of your class name) and place it in the /Views/Shared/EditorTemplates folder and then in the view use
#Html.EditorFor(m => m.Address)
which will correctly name your form controls.
I am new to ASP.NET MVC. Is it possible to pass data from view to model in mvc? this question was asked in interview.Please anyone give me right answer.
Thanks in Advance
To pass data to controller through model you should wrap all the inputs (checkboxes, textboxes, radio etc.) with <form ...> tag. You could do it using HTML tag or with help of ASP.NET MVC helper #Html.BeginForm(...). Once you submit your form, all the input data will be sent to a controller action and mapped to a targeted model. Please see an example:
Model:
public class UserModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
View:
#model UserModel
#using (Html.BeginForm("Search", "Events"))
{
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
<input type="submit" value="Search" />
}
Controller:
public class EventsController: Controller
{
public ActionResult Search(UserModel model)
{
//do something
return View(); //return "Search" view to the user
//return View(model); //You can also return view with the model to the user
//return View("SpecificView"); //You can specify a concrete view name as well
}
}
No, we do not pass any information from view to model directly, both the view and model are different module. we can pass data, value or any information from view to model via controller.
I have a List of about 20 items I want to display to the user with a checkbox beside each one (a Available property on my ViewModel).
When the form is submitted, I want to be able to pass the value of each checkbox that is checked back to my controller method via the Selections property on my ViewModel.
How would I go about doing this using the Form Helper class in MVC? Is this even possible?
PS: I don't want a listbox where the user can just highlight multiple items.
Model:
public class MyViewModel
{
public int Id { get; set; }
public bool Available { get; set; }
}
Controller:
public class HomeController: Controller
{
public ActionResult Index()
{
var model = Enumerable.Range(1, 20).Select(x => new MyViewModel
{
Id = x
});
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<MyViewModel> model)
{
...
}
}
View ~/Views/Home/Index.cshtml:
#model IEnumerable<AppName.Models.MyViewModel>
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="OK" />
}
Editor template ~/Views/Home/EditorTemplates/MyViewModel.cshtml:
#model AppName.Models.MyViewModel
#Html.HiddenFor(x => x.Id)
#Html.CheckBoxFor(x => x.Available)
The best thing to do would be to create a template that can be reused. I have some code at home that I can post later tonight.
Maybe check SO for similar posts in the mean time.
Dynamic list of checkboxes and model binding
This blog post also could help;
http://tugberkugurlu.com/archive/how-to-handle-multiple-checkboxes-from-controller-in-asp-net-mvc-sample-app-with-new-stuff-after-mvc-3-tools-update