Displaying Grails domains in jquery tabs - jquery-ui

What would be the best way to display data from Grails database in JQuery UI tabs? What I would like is to have a tab interface and on each tab is a list of the records from a different domain. For instance, Tab1 displays the record list from Domain1, Tab2 displays the record list from Domain2, etc.
I have the JQuery UI tab interface set up and working and am currently using createLink to call the method from the controller to return the model of the appropriate domain. The tabs look like this:
<div id="tabs">
<ul>
<li>Hardware records</li>
<li>Model records</li>
<li>Building records</li>
</ul>
</div>
The method from the controller looks like this:
def listHardware() {
[hardwareList:Hardware.list(), hardwareInstanceTotal:Hardware.count()]
}
I've also played around with rendering a whole GSP within the tab by using "render(view:'HardwareList', model:[hardwareList:Hardware.list(), hardwareInstanceTotal:Hardware.count()]", but that takes a VERY long time (at least 5 seconds) to load each time the tab is selected and is not at all ideal, especially if it were to take that long for each tab.
UPDATE
As noted in one of my answers to Rimero's answer below, I was able to use templates to display tables of my domains' data. I'm also trying to implement pagination on each tab using the tag, but each time I click on one of the pages to view another page, it takes me to the full template itself outside of the tab interface. Any thoughts on how to format the tag so that everything stays within the tab??

Here's my suggestion:
You can fetch everything at once in your controller in your index method for example.
You can implement your tab contents as templates
(g render template). Each tab == 1 template.
You can fetch your domain objects buildingList,
etc. from the index method of your controller.
The g:render template code for each tab may only need to be passed a map or a collection for rendering.
In this case you don't need hyperlinks to controllers endpoints. You just keep anchors to the tab(div id) as in the default example here -> http://jqueryui.com/tabs/.
UPDATED ANSWER
As you said that sending all the data at once takes a long time, you could fetch it asynchronously. If possible populate the data only for the first tab directly.
Create a business method for each tab, that will return the model as JSON, data is only fetched if not already retrieved from the server (Need to keep state or see for example if the tab id has some DOM nodes.
Using JQuery, when the DOM is ready, get the current tab and if you didn't fetch the data for the first tab eagerly, fetch it at this moment with the busy image spinning.
As soon as you select a new tab, you need to check if the data was already fetched, if not, you send an ajax call and your callback function populate the data in the tab div container for example.
Hope it helps.

Related

Pre-populate ListBox / MultiSelectList with selected items

Is there a way to pre-populate a MultiSelectList with selected items?
Example:
I have a single View that has the following ListBoxFor that will cause the page to update what it's displaying by allowing filtering of Model.Companies.
#Html.ListBoxFor(m => m.SelectedCompanies, new MultiSelectList(Model.Companies, "IdString", "CompanyName")
What I'd like to have happen is after the update, the MultiSelectList will have the items that were selected before the page updated and refreshed. Does that mean I need to return SelectedCompanies with what was selected, or is there another way?
I am using the javascript library Chosen for usability of the ListBox on the client side, but I don't think that this affects what I'm trying to do.
Sometimes, JS libaries can interfere with your desired results. I can't speak for Chosen JS library, but inspect the markup and see how it renders. As long as it still has the listbox on the client (it must have some input element defined somewhere; my guess it hides it and updates the values as they are selected), then yes it should integrate fine.
However, when the controller posts back, you have to repopulate the Model.SelectedCompanies property with whatever values came back from the POST operation to the controller. The property should still have the selected companies if you return a View from the POST operation. If you are using a RedirectToAction instead, you'd have to store the selections in TempData.

pass value to renderpartial in master page from user control

Is it possible to have a master page that has a renderpartial it in and supply a value to be passed to that partial before it is rendered.
For example we have a common menu structure across our site, I would like to put this as a partial in the master page, the problem comes in that depending on what area in the page you are we need to set a value for the selected tab so it gets highlighted and you can see where you are.
Currently the render partial is on ever page and passes in a value for the selected tab, I would like this to be in the master page and just have some way to pass the value to the master page.
I have a feeling this might be possible with the changes introduced with razor but I'm not sure, in any case we are using asp.net mvc 2 (although I'm still interested in things that would apply to more recent versions)
You could use RouteData property to get currently called page using the following:
ViewContext.ParentActionViewContext.RouteData.Values["controller"]
ViewContext.ParentActionViewContext.RouteData.Values["action"]
// OR
ViewContext.RouteData.Values["controller"]
ViewContext.RouteData.Values["action"]
Either in the controller associated with the menu or in the view itself (if it's partial). Then you can filter the output upon the current route.

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)

Providing data to Menu in my ASP.NET MVC Master Page

We are beginning the process of moving from Web Forms to MVC for all of our new applications. I am working on porting our Master Page over and am trying to satisfy the requirements that we need a single master page to be used by all applications. The primary navigation for the application needs to be in a menu within the master page. Accomplishing this was easy, the hard part is that each application may need to determine what to display in the menu using a unique set of rules. Some apps can simply say, here's the menu structure to use via something like a SiteMap. Others need to determine what is displayed in the menu based on what roles the user has, this can also be handled easily with a SiteMap. The situation that I'm struggling with is that some apps need to generate the menus based on the roles the user has, but also on the data on which they are working. i.e. The same user may have different option in the menu for a page if they are working on object 'foo' than they do if working on object 'bar'.
What I've done at this point, is I've created an HtmlHelper that is called by the master page view and takes a list of objects of a custom type and returns an unordered list that is styled by a jQuery plugin to display the menu. The list of objects the helper method takes are passed to the view using the ViewData dictionary. Currently, the value of this ViewData node is set within the constructor of each controller. This allows each page, and potentially each method, to set a different menu without having to set the value in each action method, unless its needed. I have also created a class that parses a SiteMap and returns the list of items needed to build the menu. This class is what I'm using to set the ViewData value in the controller. The idea being that if an application needed more control of how the menu data was generated, they could create their own class to generate the data as long as it returns a list of the correct type of objects.
This solution seems to work fine so far, it just doesn't 'feel' right for some reason. I'm hoping that I can either get some ideas of better way to do this or some reassurance that this is a valid approach to solving this problem.
If it is something that will be on every page, do something like this:
Create a base controller:
public class MyBaseController : Controller
Have this controller get the data it needs and send that data in the ViewData["menu"] to the View. Then have all your controllers inherit from this one:
public class HomeController : MyBaseController
In the Master Page, loop through your ViewData and create your menu.
(I did something like this for my sub-menu which displayed a list of categories.)
In the book I am reading (Pro ASP.NET MVC Framework by Apress) they use Html.RenderAction for the menu in the masterpage. I am a Asp.net MVC novice so maybe somebody else can give more info about this.
You can download the sourcecode at apress.com though so maybe that could help.

Resources