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.
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.
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)
};
...
}
}
I am having trouble understanding and implementing a view model. For example, say I have a Blog object, where each Blog object represents one blog post. I have a view that contains a list of each blog (title, text, date posted, etc...). Currently I am passing a list of blog objects to the view, but I would rather pass a list of BlogViewModel objects to the view. How do I do it? Does anyone have any good resources that will help me understand View Models?
Edit
The BlogViewModel I want to pass will contain abbreviated fields for the title and the text of the Blog. For example, I only want to show the first 10 characters of the title and the first 25 characters of the text.
Assuming you are currently doing something like:
public ActionResult GetBlogs()
{
var someService = new FooService();
var blogs = someService.GetMeMyBlogs();
return View("bloglist", blogs);
}
To use view models you need to either return them from your service, or convert the objects in the controller before sending them on to the view.
One option is to create an extension method for the Blog object.
Say we have a few properties something like:
public class BlogVM
{
public string Title {get;set;}
public string Body {get;set;}
public string AuthorName {get;set;}
public int Id {get;set;}
}
We could write an extension method:
public static BlogVM ToBlogVM(this Blog source)
{
return new BlogVM
{
Title = source.Title.SubString(0, 10),
Body = source.Body.SubString(0, 25),
AuthorName = source.Author.Name,//assuming you have some kind of Author table, I'm sure you get the idea..
Id = source.Id
};
}
Now in your controller you can do something like
public ActionResult GetBlogs()
{
var someService = new FooService();
var blogs = someService.GetMeMyBlogs();
return View("bloglist", blogs.Select(x => x.ToBlogVM()));
}
Which passes a list of BlogVM objects to your view.
edit: probably worth adding a few words on why ViewModels.
why send the view everything if it doesn't need it? In your example, your body might be a big block of text. If you are only going to display 25 chars, only send it 25 chars
some of the info in the object might be sensitive. You might want to send the Author's name, but certainly not other information you might hold such as his name, email or even password or address.
similarly, in a POST scenario you can control what information can potentially be sent back to you. If you allow the user to POST back to a full object, they can potentially send you back updated fields you might not expect. If you use a VM, you can control what information you will accept.
I find it easier/quicker for building views
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
Here's the relevant part of my Index view (Index.cshtml):
#foreach (var item in Model) {
<li>
#Html.ActionLink(item.name, "Index", "Filler", new { cap = item }, null)
</li>
}
As you can see, the ActionLink is tied to the Index action on the Filler Controller, and is passing in the entire item (the model)- "item" is of type "capsule".
Now, on my Filler Controller, in the Index action:
public ActionResult Index(capsule cap)
{
var fillers = db.fillers.ToList();
return View(fillers);
}
The capsule class that was automatically generated by Entity Framework is:
namespace CapWorx.Models
{
using System;
using System.Collections.Generic;
public partial class capsule
{
public capsule()
{
this.fillers = new HashSet<filler>();
}
public int pk { get; set; }
public string name { get; set; }
public virtual ICollection<filler> fillers { get; set; }
}
}
The problem is "cap" is NULL in the above Index action. But, if I change the type to "object" instead of "capsule", I do get some weird non-null data, but I can't cast the object to "capsule". Does anyone know why this is NULL?
Thanks,
Mike
You usually just have to pass in the id to the action. For example, can you refactor your code so that it can take in a capsuleId, get the capsule from db and do whatever processing is needed. Adding the entire object to route values in ActionLink doesn't make any sense. Have a look at the link being generated. It is probably just something like ...?cap=Namespace.Capsule as the object would have be ToStringed
The first problem is in MVC you can't bind to an interface (ICollection). You'll need to change it to a List - List<filler>. The second problem you will face is that Lists/Arrays need to be represented in array notation for proper posting, something like name="books[0].book_id". Even though MVC does a lot of magic, the model in your link still has to be represented as a query string eventually.
Depending on what you are trying to do, you may be better off representing your model as a JSON object and posting with .ajax().
See this SO post for other ideas - Need help with binding Set with Spring MVC form
I'm not totally sure why this would work(I think you're nulling out the html attributes), but try to remove the "null" part of the actionlink.
Or, the controller which created the models is wrong.
Again, don't kill me for this.
#Html.ActionLink just generates an anchor element (...), so it makes no sense to attempt to bind a complete object to the routeValues parameter. As #manojlds says, it make much more sense to just pass the relevent key value, since you'll be performing the lookup then anyway (remember, the web is "stateless").