I have a bit of functionality that I need to put on a number of pages, so I've implemented it as a partial view.
Within this partial view, I have a dropdown, an add button, and an of "items", each of which includes a delete button. The functionality is obvious. Clicking on any of the delete buttons removes the associated item, selecting an item from the dropdown and clicking add adds that item.
The complexity is that this needs to happen entirely in javascript - changes to the list of items needs to happen entirely client-side, and nothing happens on the server until the form as a whole is submitted. (That is, we don't want to update the server via ajax on every change, we want to collect the changes and submit them in toto on form submit.)
The second complexity is that this form needs to be pretty much drop-in, with as few requirements on the parent view as possible.
So I've created a viewmodel for the partial:
public class ItemsModel
{
// The list to be displayed in the dropdown
public List<KeyValuePair<string, string>> itemsList { get; set; }
// The list of selected items
public List<string> items { get; set; }
public string itemsJson
{
get { return JsonConvert.SerializeObject(this.items); }
set { this.items = JsonConvert.DeserializeObject<List<string>>(value);
}
public ItemsModel()
{
this.itemsList = new List<KeyValuePair<string, string>>();
this.items = new List<string>();
}
}
And the viewmodel for the page contains an instance of this:
public class MyViewModel
{
// Assorted stuff
public ItemsModel itemsModel;
}
When the Controller builds the model, during the HttpGet, it populates the ItemsModel object with the two lists. The view for the page includes the partial, passing the itemsModel:
#{Html.RenderPartial("_itemsList", Model.itemsModel);
Within the partial, I construct the dropdown:
#Html.DropDownList("itemsList", new SelectList(Model.itemsList, "Key", "Value")
And I populate the with javascript:
var items = $.parseJSON('#Html.Raw(Model.itemsJson)');
var itemsUl = $('#itemsUl');
itemsUl.empty();
var iTemplate = $('#itemTemplate').html();
for (var i=0; i<items.length; i++)
{
var template = iTemplate.nformat("{item}": items[i]);
itemsUl.append($(template));
}
And that's as far as I've gotten. My intent was to add javascript to handle the inserts and deletes, but there's no point, at this point. Because when I submit the page with the unmodified lists, MyViewModel.itemsModel is null. Browsing around on the web, I've seen a number of posts about how MVC binds Request items to complex lists, but none of them are relevant to my problem, because nothing is getting to MVC to be bound.
I've watched in Fiddler, and the request that is being sent includes "...&itemsList=&..." - it's not sending any data at all.
So I'm wondering if I'm chasing down the wrong path, entirely. What is the normal way for including complex data in a form submit? I've read up on FormData(), but that seems to apply only to an Ajax-style send, it doesn't affect a normal form submit.
Any ideas?
As is usual, in these sorts of problems, I just wasn't thinking things through.
If I want a value to be included in the request during a form submit, I need to put in a form element.
First, I removed the parallel json properties - it's easy enough to serialize/deserialize inline, and it makes it more obvious what is going on. So my model becomes:
public class ItemsModel
{
// The list to be displayed in the <select>
public List<string> itemsList { get; set; }
// The list of selected items
public List<string> items { get; set; }
public ItemsModel()
{
this.itemsList = new List<string>();
this.items = new List<string>();
}
}
The list is only sent to the browser, it doesn't need to come back, so it can be injected straight into a javascript variable:
var itemsSelect = $('#itemsSelect');
itemsSelect.find('option').remove();
var itemsList =
$.parseJSON('#Html.Raw(JsonConvert.SerializeObject(Model.itemsList))');
for (i = 0; i < itemsList.length; i++)
{
var item = itemsList[i];
itemsSelect
.append($("<option>", { value: item })
.text(item ? item : "Select Item"));
}
The items, though, need to come back. Which means that the json needs to be injected on the page as a hidden element:
#Html.Hidden("itemsJson", JsonConvert.SerializeObject(Model.items))
And then in the javascript, we extract the data into an array:
var items = JSON.parse($('#itemsJson').val());
And then I build <li>'s from these elements, and add them to my <ul>. The user then chooses an item from the <select> and clicks the "add" button, or clicks the "delete" button in one of the <li>'s, and I either remove an <option> from the dropdown and add a <li> to the <ul>, or I remove a <li> from the <ul> and add an <option> to the <select>. In either case, I need to reserialize the array and update the hidden element:
$('#itemsJson').val(JSON.stringify(items));
Then, when I submit the form, the browser will include a JSON string in the request, named "itemsJson". The only remaining question is how to get that into the HttpPost action. There are three possibilities:
I could just extract the value from Request["itemsJson"]. I don't like that.
I could build a custom binding to deserialize the JSON and update the model. That seems like unnecessary work.
Or, I could just add another argument to my Action, and deserialize it there.
I chose option 3:
[Authorize]
[HttpPost]
public ActionResult Perishable(MyViewModel model, string itemsJson)
{
if (ModelState.IsValid)
{
model.itemsModel= new ItemsModel
{
items= JsonConvert.DeserializeObject<List<string>>(itemsJson)
};
...
}
}
Related
public ActionResult Dashboard()
{
var data = Processor.LoadBarangays();
List<BarangayModel> barangays = new List<BarangayModel>();
**int totalConfirmed = 0;
int totalPUI = 0;
int totalPUM = 0;**
foreach (var row in data)
{
***totalConfirmed = +row.Confirmed;
totalPUI = +row.PUI;
totalPUM = +row.PUM;***
barangays.Add(new BarangayModel
{
Barangay = row.Barangay,
Confirmed = row.Confirmed,
PUI = row.PUI,
PUM = row.PUM
});
}
return View(barangays);
}
I have the above code for displaying this table: red mark now I added the codes in emphasis above so that I can show it in the cards in blue mark: blue mark. How can I display the values of the variables in the view? Can I use multiple views in a single controller? Thank you.
It seems like what you are looking for is show multiple views in a single view, as from the controller you can only return one, in your case Dashboard (when you dont specify, they have to be the same name as the method)
You can do that with partial views. and it depends a little bit in which version you are, if .NET framework, net core etc, also If you are using razor or any other framework for the front end.
if you are using razor if you type #HTML.Partial("viewname") will load a partial view inside your "main" view. and you can send a model (an object) to it as second parameter #HTML.Partial("viewname", model)
for your example, you're returning return View(barangays); which is the red box.
then to include the blue ones what you have to do is expand your model. so your BarangayModel should become a DashboardModel and one of the properties has to be List<BarangayModel> .
class DashboardModel{
public List<BarangayModel> BarangayModelList {get;set;}
}
the reason to do this is because you have to add now, your red box. lest call the object PersonStatus and the example will be like:
class PersonStatus{
int confirmed {get;set;}
int recovered {get;set;}
//rest of the properteis
}
and then you expand your DashboardModel to contain this new object:
class DashboardModel{
public List<BarangayModel> BarangayModelList {get;set;}
public PersonStatus PersonStatus {get;set;}
}
so in your Dashboard.cshtml now you need to specify the new model.It is common to be in the first line of the view. And it looks like this #model List<BarangayModel> you will need to update it to #model DashboardModel
as you change for List<BarangayModel> to the new one, the foreach to print the table will fail. which means that you probably have something like foreach(var bm in Model) now you have to update it to foreach (var bm in Model.BarangayModel)
now you have both informations on the view, but you are only printing the BarangayModel
what you have to do is in the same folder as this view (or in the Shared one) create a view, which will receive the new PersonStatus object as parameter.
then you need to call it as a partial view from your main view, passing the parameter:
#Html.Partial("PersonStatusView", Model.PersonStatus);
or
<partial name="PersonStatusView" model="#Model.PersonStatus);
depends of which version/framework you're running.
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.
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
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.