I'm creating a C# application using MVC 4, LINQ to SQL, and Razor Syntax.
I have created a class that gets the contents of a given row in the db based on a requested id.
I have made a controller that has a get and post handler. I created a strongly-typed view based on one of the model classes, and everything works great. Data is retrieved and displayed in the form. Data can be updated and submitted.
The problem is the dropdowns. I don't want it to show a textfield for the id. I want the dropdowns. I have about five dropdowns, all to be generated from the database. I've created models to create them.
I can use ViewData or ViewBag to pass in the dropdowns with no problem. But then, how do I select the selected option when the user loads the page?
The "model" in MVC is supposed to model the page, not your data. If you have dropdowns on your page, then you should have a collection of some kind (likely a List<T>) on your model that represents the choices, along with another property that represents the selected value. This property will be used to populate the initial value of the dropdown as well as send the selected value back up to the controller.
I would recommend avoiding ViewData or ViewBag in favor of a ViewModel.
A ViewModel is essentially a hybrid Model that aggregates all of the data that your View needs into a single entity. Rather than typing the View to a Model and passing the additional information needed by your View that is not in the Model in ViewData or the ViewBag you have everything you need in the Model that your View is typed to.
In your case the ViewModel might look something like this:
public class MyViewModel
{
public DropDown1 DropDownA { get; set; }
public DropDown2 DropDownB { get; set; }
public Model ModelData { get; set; }
}
public class DropDown1
{
public int SelectedValue { get; set; }
public List<T> DropDownValues { get; set; }
}
public class DropDown2
{
public int SelectedValue { get; set; }
public List<T> DropDownValues { get; set; }
}
Your View would by typed to MyViewModel. At this point, setting the data source of the drop downs to the drop downs in your ViewModel and setting the SelectedValue would be trivial.
Related
In my controller I have 2 simple methods for viewing and ordering products
[HttpGet]
public ActionResult Product(int id)
{
Product product = Repository.GetProduct(id);
return View(product);
}
[HttpPost]
public ActionResult Order(Order order)
{
var orderResult = OrderProcessor.Process(order);
return orderResult.Success ? View("OrderSuccess") : View("OrderFail");
}
The Order class contains fields from the view that customers must fill:
public class Order
{
public int ProductId { get; set; }
[Range(1, 10, ErrorMessage = "Quantity must be a number from 1 to 10")]
public int Quantity { get; set; }
[Required(ErrorMessage = "Address not specified")]
public string ShippingAddress { get; set; }
// other order info
}
In my view I want to use HTML helpers for form fields like #Html.TextBoxFor(x => x.ShippingAddress) to enable Javascript client validation. But the problem is that MVC only allows these helpers to be used for properties of the view model (in my case the Product class) and my view model doesn't contain ShippingAddress.
To get these helpers working I need to have the same class for the model of the Product view and the parameter of the Order method. But this doesn't make any sense in my case. The view model shold contain product properties (name, price, description, etc.) while the POST method parameter should contain user input (quantity, address, etc).
I would like to be able to use HTML helpers with the properties of the Order class like this:
#Html.TextBoxFor<Order>(x => x.ShippingAddress)
Is there a way to achieve this (maybe via an extension to MVC)? Or is there some other workaround for this problem?
There's a difference between your models and your view models. Models should reflect the database and your view models should be custom made to fit the view.
What you could do is creating a view model that contains all the properties you are interested in having contained in your view, and populate those fields with data from your database.
Perhaps it's not the neatest of solutions, but your view model could be as simple as:
public Order Order {get; set;}
public Product Product {get; set;}
And in the GET-method you get the order and product you want to display and and assign it to the view model.
No need To look for any magic. You can directly add "shipping address" in your Product Model as well.
What is the problem in adding that??
I cannot for the life of me figure out how to do a viewmodel correctly. We have two models, tblInventory and tblGlobalhardware, both of which need to be shown on the Details page of an inventory item, based on the InventoryID (Primary key in tblInventory and is a column in tblGlobalhardware).
I've followed numerous tutorials but get errors such about the model etc when changed on the view.
Can anyone possibly point me in the direction of a basic, easy to follow tutorial?
Thanks
In MVC you usually pass a single model instance to the view. You want to show details of the product and the inventory. So you create a view model type:
public class ProductViewModel
{
public Inventory Inventory { get; set; }
public Product Product { get; set; }
}
Now you can bind your view to this type and display the data from both objects. Just create an instance with the desired values in your controller and send it to the view:
var viewModel = new ProductViewModel { Inventory = inventory, Product = product};
return View(viewModel);
I have the same drop down list for 30 fields on a view. Is there any way to use the same selectlist in the viewbag for all 30 with the default value or do I have to have 30 separate viewbag items with the same select list and default value?
I add the selectlist to the viewbag in my contoller edit method:
ViewBag.Pulmonary_01 = new SelectList(PulmonaryList(), "Text", "Value", admission.Pulmonary_01);
The fields are Pulmonary_01 through Pulmonary_30. In my view I use:
#Html.DropDownList("Pulmonary_01", String.Empty)
If I use ViewBag.Pulmonary instead of the _01 it doesn't match it on save. Two obstacles are matching a general "Pulmonary" view and to all the fields so they save and the other is having the selected value. I don't see a way to avoid having 30 ViewBags.
This is not a problem at all. You can use the same view bag as many times as you want, you just have to cast the ViewBag into a SelectList. So for example if you have a model like:
public class Pulmonary
{
public int Pulmonary_01 { get; set; }
public int Pulmonary_02 { get; set; }
public int Pulmonary_03 { get; set; }
and in your action you create a viewbag like this:
ViewBag.Pulmonaries = new SelectList(PulmonaryList(), "Text", "Value");
you should be able to do the following in the view:
#model PulmonaryClassFullNamespace.Pulmonary
// Form declaration
#HtmlDropDownListFor(model => Model.Pulmonary_01, (SelectList)ViewBag.Pulmonaries)
#HtmlDropDownListFor(model => Model.Pulmonary_02, (SelectList)ViewBag.Pulmonaries)
//.....
// Form closure
The only thing you really have to watch is that ViewBag property name does not match any model property names. For example if you name your ViewBag.Pulmonary_01 and you have a model property called Pulmonary_01 then this will cause mapping issues because these values will be overwriting each other in form collection.
How can I have multiple inputs in a page all feed into a list of my model where the model is defined as
public class MatrixSet
{
List<MatrixPoints> matrixPoints { get; set; }
}
public class MatrixPoints
{
double x { get; set; }
double y { get; set; }
}
I am not sure what to use in the view to have say, 4 input fields which all input matrix points and then when posted the controller will have the model of type matrixset which will contain a list of the matrix points entered in the view. I know how to do this without passing the model but I am trying to adhere to best practice methods. Can I just have each input field be #Html.TextBoxFor() and then it will just fill a list of MatrixPoints in MatrixSet assuming that at the top of my view I am using #model Models.MatrixSet?
Found the answer:
#Html.TextBoxFor(model => model.matrixPoints[0].x)
#Html.TextBoxFor(model => model.matrixPoints[0].y)
In this regard you can add items to your model objects iteratively and dynamically while still holding the entire model when the form is posted and retaining the validation from the defined model.
You have to look for binding collection to View and later when you post form all collection get collected in ActionResult.
This is the link that help you
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
It works for me.
Thanks.
How can I override keys in FormCollection (I need this because I have a property bound to multiple CheckBoxes in a View)? I did try this when a post back is in Action:
formCollection["DecisionReasons"] = formCollection["DecisionReasons"].Replace(",false", "").Replace("false,", "").Replace(",","|");
...but when I UpdateModel only the first value is updated in the model (in my model I have a DecisionReason string).
Do I need a ModelBinder (how can I do that?) or is there another way to do this?
Part of View
<div style="width:300px;height:250px;overflow:auto;">
#foreach (var a in (IEnumerable<SelectListItem>)ViewBag.AllDecisionReasons)
{
#Html.CheckBox("DecisionReasons", a.Selected, new { value = a.Value })
<label>#a.Text</label><br />
}
#Html.ValidationMessage("DecisionReasons", (string)Model.DecisionReasons)
if i check more than one checkbox in my View my Model property wich is string is updated with only one value example (if in View i check 2 checkboxes my Model will be updated with first value, so i need that Model property have value "valu1,valu2" and so on.)
sorry for my bad english.
I commented, above, asking for more details, but I get the impression that you're using checkboxes to represent the individual values in a Flags enumeration.
The default model binder doesn't handle mapping flags in this way, but I found a custom model binder that does in this article.
Edit:
OK, I see from your update (which would be better if you added it to your question, rather than posting it as an answer), your model has a comma-delimited string property containing each of the selected DecisionReasons.
I suggest that you consider the use of a ViewModel. The ViewModel is an abstraction of your model that is tailored for the way in which you present it in your view.
You can derive your ViewModel from your Model class, to reduce the amount of work. Consider this (untested) code:
public class MyModel
{
public virtual string DecisionReasons { get; set; }
}
public class MyViewModel : MyModel
{
public string[] DecisionReasonValues { get; set; }
public override string DecisionReasons
{
get
{
return string.Join(",", DecisionReasonValues);
}
set
{
DecisionReasonValues = value.Split(',');
}
}
}
Use MyViewModel as the model for your View and use DecisionReasonValues to render the checkboxes, not DecisionReasons. ASP.NET MVC will populate DecisionReasonValues from your checkboxes but you can access them as a comma-delimeted string through the overridden DecisionReasons property.