Working on my first MVC3 app. I have a simple View that has 2 objects (object A, object B). Object B is optional. I've created a Model with both objects and have the Controller working just fine passing the data back and forth to the View.
I'm trying to put in a simple DropDownList for the user to decide whether they want to enter data for object B. The dropdown list is simple - two values "Yes" and "No".
Ex: Continue with Part "B"?
[Yes]
[No]
On the View, the user fills out all of the form items of object A. Then, they have a choice to fill out the items of object B. I want them to select "Yes" if they want to do that (I'm using Jquery to show more of the page at that time).
My question: How do I make a simple Dropdownlist (or even a basic HTML input type="select"...) that I can access in the Controller after they submit the form?
In my Controller, I want to do the following:
* Do stuff for object A
* Check to see if they selected Yes
* Do stuff for object B
Does something as simple as a Yes/No dropdown need to go into the Model? Its not typed to anything - its just a decision made by the user in the View. I know there has to be a simple way of doing this (ViewBag?) but I'm new and apparently lost.
TL;DR - How do I create as simple DropdownList on a View that I can access in the Controller?
The rule of thumb for getting values from the view back in the HTTPPOST for your object is to name the input controls id and name properties the same as the Models property name. An easy way to do this is to use Html helpers.
public class Model
{
public Model()
{
List<SelectListItem> options = new List<SelectListItem>();
options.Add(new SelectListItem { Value = true.ToString(), Text = "yes" });
options.Add(new SelectListItem { Value = false.ToString(), Text = "no" });
ContinueOptions = options;
}
public bool Continue { get; set; }
public IEnumerable<SelectListItem> ContinueOptions { get; set; }
}
In your View:
#Html.DropDownListFor(m => Model.Continue, Model.ContinueOptions)
In your Controller:
[HttpPost]
public ActionResult Edit(Model model)
{
bool continueOn = model.Continue;
}
Yes your drop down should be part of the model, otherwise the controller won't have the answer from the user to Check to see if they selected Yes.
public SomeViewModel
{
public ObjectA A { get; set; }
public ObjectB B { get; set; }
public bool? IsBSelected { get; set; }
}
I usually use bool? simply because I like to know if the user selected on or the other, but using bool would be fine too.
Related
In MVC C# Web Applications with Razor, I constantly end up wanting to reuse View code for Create actions.
Imagine this scenario:
public class Taco
{
public Lunch Lunch { get; set; }
public Supper Supper { get; set; }
public string Cheese { get; set; }
}
public class Lunch
{
public IEnumerable<Taco> Taco { get; set; }
}
public class Supper
{
public IEnumerable<Taco> Taco { get; set; }
}
You have Lunch and Supper that have Tacos.
Now take these two use cases:
From Supper's Details View
Want to add a Taco
Click 'Create New Taco'
Enter Taco information
Click 'Create' Button
Redirected to Supper Details with new Taco there
From Lunch's Details View
Want to add a Taco
Click 'Create New Taco'
Enter Taco information
Click 'Create' Button
Redirected to Lunch Details with new Taco there
What is a scalable and MVC-Correct way to do this?
I have always felt like my process for this is hacked together and not very scalable. I might do something like:
Supper View:
#Url.Action("Create", "Taco", new { From = "Supper" })
Lunch View:
#Url.Action("Create", "Taco", new { From = "Lunch" })
Then take the "From" variable and pass it to
Taco Controller>Taco View Model>Taco View>Link Back To From
Is there a built in way to pass referrer information and is there a pre-defined design template for MVC to handle these situations?
Just literally reuse everything. For example, you can have just one action with one view and use the URL to determine behavior. All you need is a view model so the form can work with just one class type, and then you can map the data onto wherever it should go. For example:
[HttpPost]
[ValidateAntiForgeryToken]
[Route("{mealType:regex(supper|lunch)}/create")]
public ActionResult CreateMeal(string mealType, MealViewModel model)
{
if (ModelState.IsValid)
{
switch (mealType)
{
case "supper":
// map data to new `Supper` and save
break;
case "lunch":
// map data to new `Lunch` and save
break;
}
// do redirect
}
return View(model);
}
There's other ways to handle this without using Attribute Routing, but the general idea is that in some form or fashion you indicate which type of meal is being saved, and branch accordingly, creating and saving the appropriate class.
As far as I know there is no pre-defined template. But you can create a EditorTemplate if you want one and widely used in your razor views.
Also, instead of sending From in route parameters, you can have a property in Supper and Lunch classes like gobackUrl (just example) and generate gobackUrl in Create GET action and have it in hidden form value. So, the child action view will be more generic and you don't need to have if-else logic in parent view.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
When I select an item in a dropdownlist, I want my table to refresh data on same id. As you can see, I want my table 1 in a dropdownlist and table 2 in a table-like grid
So when I pick from table 1 id 1, my grid will refresh data on foreign key 1, and when I pick from table 1 id 2, my grid will refresh data on foreign key 2, etc.
Update: This is a fairly common problem that all new developers to MVC will encounter. I have updated my answer to include a complete working example on how to solve this. I suspect it will help other new comers in the future..
You will need to accomplish this with some JavaScript (JQuery) and another action on a controller that will return your list of related items based on the id selected OR you could have the child action return a partial view with the select list data.
Your first task is to wire up an event in script to the .change() event on your first drop down list.
Once you have that captured - you can post the id to other action and have it return your data or partial view.
Solution
Cascading drop downs in MVC are accomplished through a combination of a few techniques. You need view models to render the drop downs (select lists) and you will need some JavaScript code to capture events on the client browser and get additional data. Finally you will use a partial view to return back to the client.
In this example - we are going to create a Select List with 3 parent values in it. When a user selects a different value, we will request a new second drop down list corresponding to the parent values.
I created simple model classes
public class Catalog
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CatalogDate { get; set; }
}
public class Book
{
public int BookId { get; set; }
public int CatalogId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class CatalogViewModel
{
public int SelectedCatalog { get; set; }
public List<Catalog> Catalogs { get; set; }
}
public class BookViewModel
{
public int SelectedBook { get; set; }
public List<Book> Books { get; set; }
}
The catalog and book view models will be used in our Razor views with an DropDownListFor HTML helper. The SelectedCatalog and SelectedBook properties hold the actual selected value - while the Catalogs and Books properties are used to load the Select Lists.
On the Index action of the Home controller - I modified the method:
public ActionResult Index()
{
CatalogViewModel model = new CatalogViewModel()
{
Catalogs = Catalogs(),
};
return View(model);
}
Here will will return a list of catalogs in the CatalogViewModel.
I also added in a another action that we will use for the Ajax call:
public ActionResult Books(string catalogId)
{
int catId = Convert.ToInt32(catalogId);
var model = new BookViewModel()
{
Books = Books().Where(x => x.CatalogId == catId).ToList()
};
return PartialView("Partials/_BooksDropDownList", model);
}
This method will accept the catalog Id specified by the user and retrieve a list of books corresponding the catalog id. This will then be used in a partial view and returned to the caller.
The Index view of the Home controller:
#model WebApplication1.Controllers.HomeController.CatalogViewModel
#Html.DropDownListFor(m => m.SelectedCatalog, new SelectList(Model.Catalogs, "Id", "Name"))
<div id="bookContainer"></div>
<script>
$(document).ready(function () {
$('#SelectedCatalog').change(function () {
CatalogChanged();
});
});
function CatalogChanged() {
var catalogId = $('#SelectedCatalog').val();
$.get('#Url.Action("Books","Home")' + "?catalogId=" + catalogId,
function (data) {
$('#bookContainer').html(data);
});
}
Here we have the model defined, an Html helper that renders our first Drop Down List, an empty containing div to hold the second drop down list and some script.
The script is simply capturing the .change event of the drop down list and calling the CatalogChanged function. In this function, we get the value of the drop down list and issue a get request using a Url helper to construct the url back to the Home/books action.
Here is the partial view loaded dynamically based on the selected value from the first drop down.
#model WebApplication1.Controllers.HomeController.BookViewModel
<div id="BookDropDown">
#Html.DropDownListFor(x => Model.SelectedBook, new SelectList(Model.Books, "BookId", "Name"))
</div>
That's it. This is a fairly common way of solving this type of problem. There are other ways (like using data and generating the html from script).
I wrote this out because it can be overwhelming getting started and hopefully this will help someone else out down the road.
Is it right if I return a Model object from Controller method to the View to be able to set the datasource to the datagridview in the View?
I'm trying to use MVC in WinForms.
I have a wrapper class that holds two models and which I pass to the Controller.
public class TwoModels
{
public UserInfo user { get; set; }
public List<UserInfo> Users { get; set; }
public BindingList<MedicineProduct> Products { get; set; }
}
I have a method in my View which loads data and sets the DataGridView's datasource.
private void LoadCache()
{
productsCache = new Products();
productsCache = XMLToObjectToXML.LoadData(productsCache, path);
dataGridView2.DataSource = productsCache.Products_;
userCache = XMLToObjectToXML.LoadUser(username);
}
I want to move this method to controller. But, I don't know if it's a right approach. Should I load data in controller or should I do it inside wrapper class?
Yes. Ensure that the ActionResult method has the same name as a view you want to return and that the view is expecting a Model object the same type as you are returning in the controller.
I would need to see some example code to help determine what you plan on achieving with use of datagridview.
controller get action
IList<InputVoltage> inputVoltagesList = unitOfWorkPds.InputVoltageRepository.GetAll.ToList();
pdsEditViewModel.InputVoltageList = inputVoltagesList.Select(m => new SelectListItem { Text = m.Name, Value = m.Id.ToString() });
ViewModel
public IEnumerable<SelectListItem> InputVoltageList { get; set; }
public List<int> SelectedInputVoltages { get; set; }
View
#Html.ListBoxFor(m => m.SelectedInputVoltages, Model.InputVoltageList)
I want to receive a null list when a user makes no selections the selectedInputvoltages comes into my post controller action as null how do I get it to come in as an empty list?
I like both answers is there any benefit in using one over the other?
Either make sure it is initialized in the controller (or model/viewmodel) if null, or perhaps (ugly code though) use the coalesce operator to initialize on the fly if null:
#Html.ListBoxFor(m => m.SelectedInputVoltages, Model.InputVoltageList ?? new List<SelectListItem>())
If you initialize the list in the view model's constructor then it will always be at least an empty list. Anything which builds an instance of the view model would continue to set the list accordingly.
public class SomeViewModel
{
public List<int> SelectedInputVoltages { get; set; }
public SomeViewModel()
{
SelectedInputVoltages = new List<int>();
}
}
This way it will never be null in an instance of SomeViewModel, regardless of the view, controller, etc.
If you always want the view model's property to have a default value, then the best place to put that is in the view model. If that logic is instead placed in the controller or the view then it would need to be repeated any time you want to use it.
I have a Model which consist of Employees information. In my model there is a property called City which define the city of Employee in which he lives. The propery is shown below
public string City{get;set;}
Now I have a view which contains a form which will be filled by a employee to register. I want to use a dropdownlist for selecting cities. I think the below code will be used for dropdown as i discovered. My model name is Employee.
#Html.DropDownListFor(m=>m.City,new SelectList())
Please tell me that "is there any way to define the options for dropdownlist in SelectList() method directly Like ... in html?"
If not, where should i define the class for this drop down, where to call and where to render.I don't know where to define values? I am very confused because this is mvc and we have to seperate concern and i think we cannot define anything at anywhere?
Thanks in advance..
You have at least two options:
1.) Add a list, array, or any other collection type of cities to your model
2.) Add a SelectList property to your model
Option 1 can be something as simple as an array of strings, or can be, say, an IEnumerable of City objects. You would then need to transform this property to a collection of SelectListItem objects in the view as part of the DropDownList binding.
Option 2 has the advantage of being capable of direct binding to the DropDownList, but requires that you construct the list within the action method.
Then end result is the same, it's just a matter of how pedantic you want to be about SoC.
For example (assuming you add a property called Cities):
#Html.DropDownListFor(m=>m.City, Model.Cities.Select(city => new SelectListItem()
{
Text = city,
Value = city,
Selected = city == Model.City
})
EDIT:
To answer your comment, I have to make some assumptions. I will assume you have a model called EmployeeModel. This model has a property, City, that is a plain string. So, this is a partial of your model, as I assume it to be:
public class EmployeeModel
{
public string City { get; set; }
// ... other properties ...
}
So, if you need to add a property for binding to your dropdown, you would do one of the following:
public class EmployeeModel
{
public string City { get; set; }
public IEnumerable<string> Cities { get; set; }
// ... other properties ...
}
or
public class EmployeeModel
{
public string City { get; set; }
public SelectList Cities { get; set; }
// ... other properties ...
}
This new property will contain the list of cities that you allow your user(s) to pick from.
If you choose the first option, you load the IEnumerable from your datastore, and then use the first example above in your view, which uses LINQ to project each string in the Cities property into a new SelectListItem object.
If you go with the second option, you build a SelectList in the action prior to passing the model to the view. This isn't terribly difficult, as the class provides a constructor that takes an IEnumerable (your list of cities) and the "selected value," which will be the City property (see http://msdn.microsoft.com/en-us/library/dd460123%28v=vs.108%29.aspx). Your code would look something like:
model.Cities = new SelectList(GetCities(), model.City);
This, of course, assumes you have a helper method (GetCities()) to load your cities from wherever they are stored. Your view then would have something like this:
#Html.DropDownListFor(m=>m.City, model.Cities)
The view engine then uses these SelectListItems to build the <select> element and it's <option> elements.
You could have this in your model, it's quickly achieved, although I wouldn't recommend it:
public class Place
{
public string City{get;set;}
public SelectListItem[] Cities()
{
return new SelectListItem[2] { new SelectListItem() { Text = "London" }, new SelectListItem() { Text = "New York" } };
}
}
...and your view
#Html.DropDownListFor(m => m.City, Model.Cities())
I think the best place for something like this (but is a little more complicated) is your own htmlhelper and usage could look something like:
#html.CityDropDownFor(m => m.City)
You could cache the cities nicely and it keeps data and UI work out of your models.
If you want to learn more about creating your own helpers, I'd suggest a bit of a [read up].1