How to load TreeView data only once in a _Layout page - asp.net-mvc

Our users asked to have the Kendo Tree show up in the MVC web app in a panel on the left hand side of the page. They want that left hand panel to be present on every screen.
Currently I have a section of my _Layout.cshtml page that renders the Kendo Tree:
<div>
#{Html.RenderAction("GetTree", "Tree");}
</div>
Inside that Action I make a database call to get the contents of the tree and bind the model to the view as follows:
#model IEnumerable<TreeViewItemModel>
But obviously with this pattern every page I go to the _Layout gets called and the tree data is fetched from the database again. This is not very efficient.
What is a better way so that I only make a single database call?

I'm going to assume that the content of the tree is the same for every page and every user.
In that case, you can cache the retrieved database data so that you don't have to retrieve it on every page render. There are lots of ways to cache it: the simplest is probably to use ASP.NET's own caching, which is described in detail (with walkthroughs) here.
You probably will still have to render it on every page (unless you want to get into partial page caching, and I'm not sure how that works in MVC) but you definitely can avoid the repeated database hit.
ETA: You can create a wrapper or helper class that retrieves the necessary tree data by company. The relevant method would look a bit like this:
public IEnumerable<TreeViewItemModel> GetCachedTreeDataByCompany(int companyId)
{
var data = Cache["TreeData"] as IEnumerable<TreeViewItemModel>;
if(data == null)
{
data = GetTreeData(); // whatever you need to do to get the data
Cache.Insert("TreeData", data);
}
return data.Where(tvim => tvim.CompanyId == companyId).ToArray();
}

Related

Saving Cascading drop downs in MVC

I have cascading dropdowns in my MVC partial view. DD1 drives the values in DD2. When I select DD1 I want DD2 to be populated with, based on a DB table, the correct values based on DD1's value.
My thought was to make DD2 a partial view and nest in my form. Then, with ajax, I can tell the partial view to refresh and pass it the value of DD1.
The problem is, when I submit the whole view (with both DD1 and DD2 and a bucnh of other stuff, how do I get the value that is in DD2?
I'm trying to solve this problem using MVC, rather than triggering a javascript function on change of DD1 to make a JSON call to get the options and then using javascript to modify DD2 to the correct values.
How should I do this?
How big are your value sets for each drop down?
I was attempting to do this same thing a few years ago. DD1 was United States and Canada, and DD2 was the associated States and Provinces. If your data set is relatively small, you're better off just putting all the select list markup for both cases in the page, and then swapping it out with javascript (jQuery). You'll be saving yourself the request round trip, regardless of whether you go ajax or a full page refresh.
If the data set is large and it doesn't make sense to put all values in the markup, and you want to use MVC views instead of modifying the DOM with an ajax call, then just refresh the entire page. If you want to go ajax, then just modify the DOM with jQuery; you don't need a partial view to accomplish this.
You are going to have to use javascript unless you want to do an entire page postback. For this type of thing, javascript/ajax is the way to go. I personally had a hard time when I switched to MVC having to accept that all this business logic was happening outside of the MVC model. But in the end, it's whatever makes the website work best (user doesn't see your code and know how pretty it is).
Anyway, partials won't work either unless you post the whole page since without using javascript, the partial is rendered as part of that page/form.
I would just add a onchange event to the first dropdown that triggers a json call to a method in the same controller...something like
...jquery...
$("#mydropdown").change(function() {
$.post("/Controller/DropdownChangedJSON", { firstdropdownvalue: $("#mydropdown").val() }, function(data) {
$("#seconddropdown").empty();
// loop through "data" to populate dropdown
}); //post
}); //mydropdown_change()
and in your controller:
public JsonResult DropdownChangedJSON(string firstdropdownvalue) {
//get results
List<datamodel> myarray = //something
return new JsonResult { Data = new { success = true, rows = myarray } };
}
hope this helps

Merge ViewModel

Im starting MVC & would like your advice about Repositories & View models, specifically combining a few sets of repository data into one VM.
If I have a web page with three distinct sections (columns) that require their own repositories (sorry for the basic nature of this):
UserInfoData - shown in Left col
DiaryData - shown in Middle col
CommentData - Shown in Right col
Im doing the following :-
public ActionResult Index(int id)
{
UserInfoData uid = repository.GetUserInfoData(id);
UserInfoDataVM UserViewModel = Mapper.Map<UserInfoData, UserInfoDataVM>(uid);
//RETRIEVE DiaryData & CommentData using same principle as above
return ??
}
How would I combine these three VM's into something I can return to the view - do I use another VM which accepts these three VM's?
Is there a best practice when dealing with smaller segments of a web page that have their own data?
In my opinion, it depends on your usage scenario.
You could create a View Model that contains the three sub view models, but you would need to ask yourself, am I going to use any of the three sub view models anywhere else? If not, then you could just use one large view model. Else, there is nothing wrong with the 'wrapper' view model that encapsulates smaller view models.
You could keep the three view models as you have defined above and you could use the Html.RenderAction syntax in your view to actually go and fetch each of the sub parts of your page. You would have defined an action on your controller whose responsibility it is to populate each of the small view models and return the view for that model.
<div id="leftCol">
#{Html.RenderAction("LeftCol", "Controller");}
</div>
<div id="midCol">
#{Html.RenderAction("MiddleCol", "Controller");}
</div>
<div id="rightCol">
#{Html.RenderAction("RightCol", "Controller");}
</div>
This would give you the ability to use the sub parts anywhere on your site and you only have to define the action method and partial views once.
The second option is also a personal favorite because you can quickly leverage JQuery to do partial page loading if these sections take a few seconds to populate and render (if this makes sense for your page). Now, your page opens quickly, you have a JS function that goes and fetches these sections, put a little loading graphic over them and are loaded/rendered using an AJAX request. Again, only if this makes sense for you, but using the Partial Views/Render action methods typically give you more flexibility in reuse across the site and AJAX partial page rendering.

ASP.NET MVC - Complicated view logic

I'm making the transition from webforms to MVC (I know, 3 years late) and I "get it" for the most part, but there's a few things I'd like advice and clarification on:
First off, what happens if you want to dynamically add inputs to a view? for example, in an old webform for generating invoices I had a button with a server-side click event handler that added an extra 5 invoice item rows. The stateful nature of webforms meant the server handled the POST event "safely" without altering the rest of the page.
In MVC I can't think how I'd do this without using client-side scripting (not a showstopper, but I would like to support clients that don't have scripting enabled).
The second problem relates to the invoices example. If my Model has a List, how should I be generating inputs for it?
I know data binding is a possible solution, but I dint like surrendering control.
Finally, back to the "stateful pages" concept - say I've got a Dashboard page that has a calendar on it (I wrote my own calendar Control class, the control itself is stateless, but can use the webform viewstate to store paging information) - how could a user page through the calendar months? Obviously POST is inappropriate, so it would have to be with GET with a querystring parameter - how can I do this in MVC? (don't say AJAX).
Thanks!
In MVC you design your actions to accommodate your needs. For example, if you wanted to be able to add 5 rows to an invoice NOT using client-side scripting, you'd probably have your GET action for the invoice generation take a nullable int parameter for the number of rows. Store the current number of rows in the view model for the page and generate a link on the page to your GET action that has the parameter value set to 5 more than the current value. The user clicks the link and the GET view generates the page with the requested number of rows.
Controller
[HttpGet]
public ActionResult Invoice( int? rows )
{
rows = rows ?? 5; // probably you'd pull the default from a configuration
...
viewModel.CurrentRows = rows;
return View( viewModel );
}
View
#Html.ActionLink( "Add 5 Lines", "invoice", new { rows = Model.CurrentRows + 5 }, new { #class = "add-rows" } )
You would probably also add some script to the page that intercepts the click handler and does the same thing via the script that your action would do so that in the general case the user doesn't have to do a round trip to the server.
<script type="text/javascript">
$(function() {
$('.add-rows').click( function() {
...add additional inputs to the invoice...
return false; // abort the request
});
});
</script>
Likewise for your calendar. The general idea is you put enough information in your view model to generate all the actions that you want to perform from your view. Construct the links or forms (yes you can have multiple forms!) in your view to do the action. Use parameters to communicate to the controller/action what needs to be done. In the rare case where you need to retain state between actions, say when performing a wizard that takes multiple actions, you can store the information in the session or use TempData (which uses the session).
For things like a calendar you'd need the current date and the current view type (month/day/year). From that you can construct an action that takes you to the next month/day/year. For a paged list you need the current page, the current sort column and direction, the number of items per page, and the number of pages. Using this information you can construct your paging links that call back to actions expecting those parameters which simply do the right thing for the parameters with which they are called.
Lastly, don't fear AJAX, embrace it. It's not always appropriate (you can't upload files with it, for example), but your users will appreciate an AJAX-enabled interface.
In MVC you can store application state in various ways. In your controller you have direct access to the Session object and you can also store state to the database.
your view can contain basic control flow logic, so, if your model has a list you can iterate over it in the view and, for example, render an input control for each item in the list. you could also set a variable in a model to be the maximum number of rows on the viewpage and then render a row in a table for the number specified by the model.
paging is basically the same thing. you can create a partial view (user control in the webform world) that shows page numbers as links, where each link calls an action that fetches the data for that page of results.
i'm not sure what your beef is with ajax or javascript

Can a controller influence the _layout.cshtml file?

I'm stuck! I'm under the impression that the _layout.cshtml file is used for MasterPage-like content. Everything there is rendered on every page. Naturally, I want to write the code for rendering my sidebar menu in that file.
I want to dynamically display a list of Categories from my DB, but I'm having a problem with passing the actual model of categories to Layout.cshtml since it seems no controller actually touches it.
Any suggestions?
Otherwise please tell me how to approach this problem. I've been wracking my brain for the past three days and still no elegant solution.
I need to:
Dynamically fetch a list of Categories from the DB.
Display this list of Categories on every single view. (Hence the use of _layout.cshtml)
Elegantly handle each different categories click.
I'm at my wits end. :P How would you solve this?
_layout.cshtml
#if(isSectionDefined("Categories"))
{
<div id="sidebar">
#RenderSection("Categories", required: false )
</div>
}
index.cshtml
#section Categories {
<ul>
<li>Category One</li>
<li>Category Two</li>
<li>Category Three</li>
</ul>
}
see this : http://weblogs.asp.net/scottgu/archive/2010/12/30/asp-net-mvc-3-layouts-and-sections-with-razor.aspx
Any viewmodel that you pass to your view is automatically available within your master page. If you do not use RenderAction/Action which is the best approach, then you must create the necessary master page data in every action and add it to viewdata - either by having a common base class for your strongly typed viewmodel that contains all master page data or by using the viewdata dictionary.
I would strongly recommend that you go down the html.action approach though. In this way, you have a totally separate controller action for dealing with your list of categories. This action can retrieve the neccesary category data and return the categorylist usercontrol as a partialview and you will not have to worry about polluting all your other actions with this data.
As I see it, ViewData (and its relatives like ViewBag, Model, etc.) is meant for the specific current view. Your _Layout.cshtml is not specific to the current view; and it would be awkward if EVERY controller would have to pass the categories data in addition to whatever else data it needs to pass for the view.
Instead, what I do, is provide a static method in one of my helper classes that retrieves the categories from the DB. I also do some caching there, so that I do not have to hit the DB on every single request. The _Layout.cshtml then simply calls this static method. Simple and elegant.
If you wish, you can bring this out to a partial view, make it a helper method, whatever.
One note of caution though - my custom error view also uses the same _Layout.cshtml, and if the DB goes down, you get an exception trying to display the exception. ASP.NET MVC is smart enough to detect this and abort processing, but you're left with a nondescript default error page. What I did was to place try...catch statements around these dangerous calls, which quietly ignore the exception if the current page is the error view.
I've achieved something similar by having my ViewModels implement an Interface which has members that contain the menu data. In my action method I set that data. Then in my view I check to see if my view-model implements that inteface, pull the menu data out and render the menu (in a partial view actually)

asp.net mvc: What is the correct way to return html from controller to refresh select list?

I am new to ASP.NET MVC, particularly ajax operations. I have a form with a jquery dialog for adding items to a drop-down list. This posts to the controller action.
If nothing (ie void method) is returned from the Controller Action the page returns having updated the database, but obviously there no chnage to the form. What would be the best practice in updating the drop down list with the added id/value and selecting the item.
I think my options are:
1) Construct and return the html manually that makes up the new <select> tag
[this would be easy enough and work, but seems like I am missing something]
2) Use some kind of "helper" to construct the new html
[This seems to make sense]
3) Only return the id/value and add this to the list and select the item
[This seems like an overkill considering the item needs to be placed in the correct order etc]
4) Use some kind of Partial View
[Does this mean creating additional forms within ascx controls? not sure how this would effect submitting the main form its on? Also unless this is reusable by passing in parameters(not sure how thats done) maybe 2 is the option?]
UPDATE:
Having looked around a bit, it seems that generating html withing the controller is not a good idea. I have seen other posts that render partialviews to strings which I guess is what I need and separates concerns (since the html bits are in the ascx). Any comments on whether that is good practice.
look at the ContentResult you can specify the mime type of what you return (text/html)
You could alternatively make a control that take a IEnumerable of whatever you put in the selectlist, and build it using the view engine. That way you keep the formatting of the html (in this case a list of options) into a view, and not in your code.
<%# Control Language="C#"Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<Article>>"%>
<%foreach (var article in Model){%>
<option><%:article.Title %></option>
<%} %>
I think I would go for that second one
From what I understood, the jQuery dialog contains a form that, when submitted, will post to an action which updates the database with some information. You want to get the newly added database information and update the same form that was used to trigger the database update.
If that is the case, then I think the best clean and logical option is to return JSON serialization of the items to be put in the drop down right after you update the database. Then, using jQuery, you would clear the drop down and append option tags into it.
You can also write a new, seperate action that returns the JSON serialization of the database objects you need. You would have jQuery call another post to this action as a callback to your first ajax post (the one used to update the database).
Here is a quick snippet
public ActionResult UpdateDatabase(string something)
{
/// update the database
IEnumerable<Items> items = getItemsFromDatabase(); // or w/e
var vals = items.Select(x=> new { value = x.ID, text = x.Name }); // something similar
return Json(vals);
}
Personally, I would write a separate function that returns JSON. This ensure separation of concerns, and gives me a function I can use in many different places.
Returning a JsonResult with all the items is the most versatile and least-bandwidth intensive solution as long as you are happy to iterate through the list in jQuery and update your drop-down list.
Using a partial view is nice for HTML that you can .load(...) directly into your select, but less versatile.
I would go with the JsonResult.
In your Controller:
public JsonResult UpdateItem(string sItem)
{
// 1. Insert new item into database if not exist...
// {update code here}
// 2. retrieve items from database:
IEnumerable<Item> Items = GetItems();
// 3. return enumerable list in JSON format:
return new JsonResult{ Data = new {Items = Items, Result = "OK" }};
}
On client-side:
Iterate through Items array and add the items to your list.

Resources