Quantcast
Channel: Nicholas Hagen » Grails
Viewing all articles
Browse latest Browse all 10

Designing Modular Views with Grails

$
0
0

One of the more common tasks in using a templating language like Grails is modular-based development. On a given page, you may have a common module used by several pages. For example, you may want a shopping cart on every page or have a set of social network widgets on various pages. Rather than duplicating that effort across every page, you want to separate it out on its own so the functionality lives in one place. Grails has several ways to do this, each with its own set of pros and cons. This article will help walkthrough a variety of them and the use cases they were designed for.

The most simple way of creating modules is using sub-templates in Grails. A sub-template in Grails is a GSP that starts with a leading underscore. This tells Grails that the template is a sub-template that does not live on its own but rather expects some other page or sub-template to invoke it. Let’s say we want to include a login form on a variety of pages. The login form is self-encompassing and can live on its own in terms of data models. First, create a _loginForm.gsp file and include it in a new view directory called login. You can actually place the sub
template in any view directory, but for this exercise, we will assume it is in the login directory.

<g:form controller="login" action="login">
    <g:textField name="username" />
    <g:passwordField name="password" />
</g:form>

Now to include that login form on any page, simply do <g:render template="/login/loginForm" /> We have now separated out the common login form from the main page. This method works great when the content is common and has no data associated with it. What if the sub-template required some form of model or data, though?

Let’s look at a widget that displayed a ‘Similar Items’ list on several pages, but the list of items was tied to the page displaying the widget. For example, an electronics section may show other electronics, but a book section may show other books. In this scenario, the sub-template does not know what the list of data should be, so it has to be provided. This can again use sub-templates, but this time we can pass data into it. Sub-templates can take a model as a map of variables to values such that the passed in variable names are available to be used within the sub-template. Create a new sub-template in the modules view directory named _similarItems.gsp.

<h3>Similar Items : ${title}</h3>
<ul>
<g:each var="item" in="${items}">
    <li>${item}</li>
</g:each>
</ul>

This sub-template expects a ‘title’ and list of ‘items’. We can now have various pages call this sub-template and provide the required model:

<g:render template="/modules/similarItems" 
          model="[title:'Books', items:[ 'Groovy in Action', 'Grails in Action' ]]" />

Any other page can easily include that same data but changing the model as need be. You may even have the controller look up the related items from the database based on the active content being shown by the controller and then pass that data as the model of the controller so the view template can pass it to the sub-template model.

public class TestController {
    ItemsService itemsService
 
    def index() {
        def section = params.section ?: 'home'
        def items = itemsService.getItems(section)
        def related = itemsService.getRelated(section)
        [ section : section, items : items, related : related ]
    }
}
<h1>${section}</h1>
<ul>
<g:each var="item" in="${items}">
    <li>${item}</li>
</g:each>
</ul>
 
<g:render template="/modules/similarItems"
          model="[title : section, items : related ]" />

Another method to do the same thing is using the tmpl namespace in Grails.

<tmpl:similarItems title="${section}" items="${related}" />

This example is slighly easier to read as the model is provided as attributes to the tag instead of an inline map/array to the tag. The use of one or the either is entirely a choice as long as the decision is consistent to avoid confusion. For more information on the render tag, see http://grails.org/doc/2.0.x/ref/Tags/render.html.

These are the most common use cases. What if we do not need to pass any data to the sub-template, but the sub-template needs to acquire data as part of its visual rendering. For example, you may want to show a shopping cart on every page. The shopping cart will always have the same data regardless which page calls it, but it needs to retrieve its data from the ShoppingCartService. There are a few different ways to do this, again with pros and cons in each scenario.

Let’s start again with the easy way using sub-templates. Grails contains a g:set tag that allows you to set scoped variables (typically page scoped) based on some expression. The expression can then lookup services to retrieve the necessary data. Let’s create a _shoppingCart.gsp in the modules directory.

<g:set var="shoppingCart" value="${model.shoppingCart}" />
<h3>Shopping Cart</h3>
<ul>
    <g:each var="item" value="${shoppingCart.items}">
        <li>${item.name}</li>
    </e:each>
</ul>

This is fairly simple and a page can easily include without having to pass or look up any model data. However, this requires every page controller using this sub-template to define the model.shoppingCart bean as part of its model. I will show how to do that easily next. A feature request for a future version of Grails is to allow the g:set tag to include a bean attribute on the set tag to allow easier access to beans such as services. This would allow sub-templates to be independent of tag libraries and controllers.

<g:set var="shoppingCartService" bean="shoppingCartService" />

Unfortunately, that is not yet possible howerver.

As a simple example, this was pretty straightforward. However, what if you need to do a bunch of business logic or manipulation of the data. In these use cases, you would not want to purely use a sub-template as you want to avoid mixing business logic from presentation (ie: the whole principle of MVC). I’ll skip the more standard approach first and jump to a non-traditional example for the sake of demonstrating alternative options as well as explaining how to include a common model across several pages (such as the shopping cart example above).

The non-traditional way to solve this is to have the controllers do the business logic and then include the resulting data in the model. Then, the sub-template will have access to the associated data. However, if you did this in every controller, you would end up with duplicated code. So, how do you allow any controller to perform the business logic and include it in the resulting model? The answer lies in inheritance and listeners in Grails. For example, create an abstract Groovy class BaseController and then ensure each specific controller extends this base controller. The base controller can now include an interceptor that will intercept any action in any sub-class allowing modifications to the resulting model. This modification could be the result of business logic performed.

abstract class BaseController {
    ShoppingCartService shoppingCartService
 
    static afterInterceptor = { model ->
        model.shoppingCart = shoppingCartService.getShoppingCart()
    }
}
class ProductController extends BaseController {
    def productService
 
    def index(Integer productId) {
        def product = productService.getProduct(productId)
        [ 'product' : product ]
    }
}

Notice that the product controller has no notion of the business logic and associatd data for the shopping cart, yet the model that is given to the page will include the ‘shoppingCart’ bean. This clearly separates the concerns but still allows the base controller to inject the data into the model. Because the data is in the model, GSPs can now pass the data to the sub-templates:

Cart:
<g:render template="/moules/cart" model="[ cart : shoppingCart ]" />

You still use the power of sub-templates and separation of concerns. However, the downfall is that every controller using that sub-template needs to extend the base controller. If the base controller is not implemented, then the model data will be unavailable.

The more traditional way to solving this is through tag libraries. Tag libraries are powerful, very modular, and written in standard Groovy. Tag libraries are similar to tag libraries in JSP or JSF, but without all the annoying headaches of configuration. You simply create a new tag library class and it all gets autowired by Grails. Tag libraries are great for performing common logic and outputting common HTML.

class UserTagLibrary {
    static namespace = 'user'
 
    UserService userService
 
    /**
     * This displays a welcome message to the logged in user.
     *
     * @attr message Message to display to the user, defaults to 'Welcome'
     */
    def profile = { attrs, body ->
        def user = userService.getUser(session.user)
        def message = attrs.message ?: 'Welcome';
        out << "${message} ${user.firstName} ${user.lastName}"
    }
}

Tag libraries all end with the common suffix TagLibrary by convention. The first thing to note is the static namespace constant. This tells Grails what prefix to use in GSPs. By default, tag libraries default to the global Grails namespace g. Providing the namespace constant overrides that value. You will also note that tag libraries can automatically inject other services and beans. Finally, you define closures as the available tags. The closures take one argument as the list of key/value pair attributes and one argument as the optional body of the tag. You then define the logic within the closure and use the predefined out variable to send content to the page. In this example, we could write <user:profile message="Hello" /> on any page and have it generate “Welcome Nicholas Hagen” for example.

The problem, however, is that we are going back to the good ol’ days of pure Servlet programming where we are generating HTML and concatenating strings together. The Groovy Strings simplifies the concatenation, but we are still outputting HTML and having to worry about escaping characters, etc. There is a reason why JSP was created to remove the annoyance of writing HTML inline with your logic. So, what can we do to avoid this and get the power of GSP’s for HTML output and the power of tag libraries for performing business logic and generating models?

In typical Grails fashion, the answer is quite simple yet again. Tag libraries, along with controllers, can actually invoke other tag libraries. This means that the tag library can act as the business logic processor and invoke the render tag to pass the resulting data model to a GSP to actually render the contents. This is exactly what we were doing earlier, only this time the tag library is invoking render rather than the page.

class ProductTagLibrary {
    static namespace = 'p'
 
    ProductService productService
 
    /**
     * Generate a list of similar items based on a given product id.
     *
     * @attr productId REQUIRED The id of the product being displayed
     */
    def simlarItems = { attrs, body ->
        // validate the data
        def productId = attrs.productId;
        if (!productId) { throw new IllegalArgumentException('productId'); }
 
        // lookup the associated product
        def product = productService.getProduct(productId)
        if (!product) {
            out << "No product available"
            return
        }
 
        // get the name of the product section
        def section = product.section.name
 
        // lookup similar items and process
        def products = []
        def similar = productService.getSimilarItems(product)
        similar.each { it ->
            // do stuff and logic
            products << it
        }
 
        // render the similar items template passing the model
        g.render([ template:'/modules/similarItems', model:[ title: section, items:products ] ]);
    }
}

This example basically looks up a given product, finds similar products to it, performs some level of business logic against those products and then calls the sub-template to actually render the data. Instead of having pages invoke the similar items sub-template, the pages simply invoke <p:similarItems productId="${params.productId}" />. This creates a clear separation in the system. The page only knows about tags it can invoke and does not have to carry about where those tags live, what they do, etc. The tag library only has to perform business logic and setup the data model without worrying about HTML to invoke. The sub-template only has to worry about the HTML to output and not how to setup data.

One final thing I will note is there is an active GSP tags plugin that allows you to define tag libraries purely as a GSP where the only thing the tag library is doing is outputting HTML. This can come in very handy as well. However, if you have complex business logic to perform, separating the logic into a tag library and the content into a GSP is still the better option typically.

There are probably several other useful approaches other than these and feel free to drop a comment on your experiences. Ultimately, it comes down to a use case decision and strategy decsion. Here are some simple rules that I tend to follow when making decisions:

  1. Use sub-templates for common code with no business logic (purely HTML output driven by an optional model)
  2. Use basic tag libraries for utility functions or very short one line HTML snippets
  3. Use tag libraries along with sub-templates for complex business logic and complex HTML snippets

Hopefully this posts helpd you understand the various ways of handling modular programming within Grails.


Viewing all articles
Browse latest Browse all 10

Trending Articles