Juzu Web Framework 0.5.0

Tutorial

Julien Viet

eXo Platform

Table of Contents

Preface
1. Quickstart
Deploy the applications
Interacting with the application
2. Template overwiew
3. Dependency Injection
4. Views
5. Actions
6. Type safe templating
7. Using portlet preferences
Reading preferences
Writing preferences
8. Styling the application
The style
A la carte
Scoping the styles
Plugins in action
Less compilation
Injecting CSS
Bringing CSS to life
9. Adding Ajax
Javascript to the rescue
Adding scripts
The collapse plugin
Bridge the gap with Ajax
Resource controller
JQuery
10. Wrap up

List of Figures

8.1. The Bootstrapized application

List of Examples

8.1. The necessary Bootstrap less files
8.2. The rule to scope
8.3. The rule scoped
8.4. The rule scoped
8.5. Injecting Bootstrap CSS in our application

Juzu is a web framework based on MVC concepts for developing Portlet applications. Juzu is an open source project developed on GitHub project licensed under the LGPL 2.1 license.

This tutorial will make you familliar with Juzu, to reach our objective we will develop a weather application in several steps, each step introducing a new feature to gradually improve the application.

Deploy the applications

Before diving in the technical part of this tutorial, we need to study how to deploy the examples and how to use them. In the package you downloaded you will find a war file adapted to your portal server in the /tutorial directory:

  • juzu-tutorial-examples-gatein.war for the GateIn portal server

  • juzu-tutorial-examples-liferay.war for the Liferay portal server

The main reason we have two servers is that the jars are not exactly the same, each is adapted to the portal server you will use. When you deploy the applications, the deployment process will print information in the console, similar to:

INFO: Deploying web application archive juzu-tutorial-gatein.war
[Weather1Portlet] Using injection CDI_WELD
[Weather1Portlet] Dev mode scanner monitoring /Users/julien/java/gatein-server/webapps/juzu-tutorial-gatein/WEB-INF/src
[Weather1Portlet] Building application
[Weather1Portlet] Starting Weather1Application
[Weather2Portlet] Using injection CDI_WELD
[Weather2Portlet] Dev mode scanner monitoring /Users/julien/java/gatein-server/webapps/juzu-tutorial-gatein/WEB-INF/src
[Weather2Portlet] Building application
[Weather2Portlet] Starting Weather2Application
[Weather3Portlet] Using injection INJECT_SPRING
[Weather3Portlet] Dev mode scanner monitoring /Users/julien/java/gatein-server/webapps/juzu-tutorial-gatein/WEB-INF/src
[Weather3Portlet] Building application
[Weather3Portlet] Starting Weather3Application
....

As we can notice, there are 7 applications deployed, one for each of the topic of this tutorial

Interacting with the application

In this tutorial, Juzu applications are deployed in the dev mode. This runtime mode allows you to modify the source code of the application, Juzu will pick up the modifications and update the running application almost instantanesouly.

The source code for the five applications is in the /WEB-INF/src directory of the war file, each application has its own package, for instance the quickstart application uses the package examples.tutorial.weather1. The first version of the application shows the most basic Juzu application. Our application is declared in the examples.tutorial.weather1 package package annotated with the @Application annotation This annotation declares a Juzu application and does not require any mandatory value. Like classes, methods or fields, Java packages can be annotated, such packages declaration are represented by a special file named package-info.java.

The first think to do when developping a Juzu application is to declare the application. The package of the application must be annotated with the @juzu.Application annotation to declare the application. The Java file examples/tutorial/weather1/package-info.java contains the package declaration along with the annotation:

@juzu.Application
package examples.tutorial.weather1;

We can even go further and also annotate the package with the juzu.plugin.portlet.Portlet annotation, this annotation has the very simple purpose to generate a Java Portlet that will be used in the portlet.xml deployment descriptor:

@juzu.Application
@juzu.plugin.portlet.Portlet
package examples.tutorial.weather1;

The @Portlet annotation generates a Java class examples.tutorial.weather1.Weather1Portlet that we specifies in the WEB-INF/portlet.xml deployment descriptor of the web application:

<portlet>
  <portlet-name>Weather1Portlet</portlet-name>
  <portlet-class>examples.tutorial.weather1.Weather1Portlet</portlet-class>
</portlet>

This is enough to create an empty Juzu application, now let's see the application itself!

Usually an application is made of controllers and templates, in this example, the Weather Java class contains a method annotated with the @View annotation, which turns the Weather class into a Juzu controller. The controller method index() is the name of the default method that Juzu will call.

  @View
  public void index() {
    index.render();
  }

Methods annotated by @View have the unique purpose of providing markup, they are called view. In our case, the method delegates the rendering to the index.gtmpl template. The template is injected in the controller thanks to the @Inject annotation and the @Path("index.gtmpl") annotation.

  @Inject
  @Path("index.gtmpl")
  Template index;

By default templates are located in the templates package of the application, in our case the examples.tutorial.weather1.templates package. The @Path annotation specifies the path of the template in this package. The templates are located in the same source tree than the java classes because the files must be available for the Java compiler.

Now we will improve our application by exploring a bit the templating engine. We will show a quick overview of Juzu templating system. Templates are essentially made of static part (usually markup) and dynamic parts. In this section we will focus on explaining the use of dynamic expression in a template.

The application shows how a view can provide variable input for a dynamic template with parameters. Our application has a view controller and a template, but now the template contains the ${ } expression that makes it dynamic.

The weather temperature in ${location} is ${temperature} degrees.

Like before the template is used in the view controller but now we use a Map containing the location and temperature parameters.

  @View
  public void index() {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("location", "Marseille");
    parameters.put("temperature", "20");
    index.render(parameters);
  }

During the template rendering, the location and temperature expressions are resolved to the value provided by the view controller. When a template is rendered, an optional map can be provided, this map will be available during the rendering of the template for resolving expression.

The next step is to make our application obtain real data instead of the hardcoded values we used in the previous section. For this matter we use a remote service that we encapsulate into the WeatherService.

public class WeatherService {

  public String getTemperature(String location) {
    return getTemperature(location, "c");
  }

  public String getTemperature(String location, String grade) {
    try {
      XPath xpath = XPathFactory.newInstance().newXPath();
      XPathExpression expr = xpath.compile("//temp_" + grade + "/@data");
      String url = "http://www.google.com/ig/api?weather=" + location;
      InputSource src = new InputSource(url);
      src.setEncoding("ISO-8859-1");
      return expr.evaluate(src);
    }
    catch (XPathExpressionException e) {
      return "unavailable";
    }
  }
}

Juzu uses dependency injection to interact with a service layer. The JSR-330, also knowns as @Inject, defines an API for dependency injection. The WeatherService is injected in the controller with the weatherService field annotated with the @Inject annotation:

  @Inject
  WeatherService weatherService;

This service is then simply used into our controller index() method:

  @View
  public void index() {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("location", "Marseille");
    parameters.put("temperature", weatherService.getTemperature("marseille"));
    index.render(parameters);
  }

As we can see, Juzu relies on the portable @Inject annotation to declare sinjections. Injection is performed by the dependency injection container. At the moment the following containers are supported:

There is a preliminary support for Google Guice 3.0, but it is not yet available. In the future more container support could be achieved.

By default it uses the Weld container, if you want instead to use Spring container instead the configuration is done by a portlet init param defined in the deployment descriptor of the portlet:

<init-param>
  <name>juzu.inject</name>
  <value>spring</value>
</init-param>

In the case of Spring, the file spring.xml file is needed, it contains the service declarations for the Spring container.

Juzu provides more advanced dependency injection, in particular it uses the Qualifier and Scope features defined by the JSR-330 specification that will be studied later.

Until now we have seen a basic view controller, in this section we will study more in depth view controllers. A view controller is invoked by Juzu when the application needs to be rendered, which can happen anytime during the lifecycle of an application.

This version has still the index() view controller, but now it has also an overloaded index(String location) method that accept a location argument as a view parameter.

  @View
  public void index(String location) {
    Map<String, Object> parameters = new HashMap<String, Object>();
    parameters.put("location", location);
    parameters.put("temperature", weatherService.getTemperature("marseille"));
    index.render(parameters);
  }

View parameters are bound to the current navigation of the application and their value are managed by the framework. At this point it is normal to wonder how a view parameter value can change. Let's have a closer look at the index.gtmpl application template.

The weather temperature in ${location} is ${temperature} degrees.
<a href="@{index(location = 'marseille')}">Marseille</a>
<a href="@{index(location = 'paris')}">Paris</a>

The template now has two links that change the view parameters when they are processed. The links are created by a special syntax that references the view method, for instance the script fragment @{index(location = 'paris')} generates an url that updates the location view parameter to the paris value when it is processed.

The initial controller method index() is still there but now it simply invokes the index(String location) controller with a predefined value.

  @View
  public void index() {
    index("marseille");
  }

We could't close this section without talking a bit about safe urls. Juzu is deeply integrated at the heart of the Java compiler and performs many checks to detect applications bugs during the application compilation. Among those checks, templates are validated and the url syntax @{ } is checked against the application controllers. In fact Juzu will resolve an url syntax until it finds one controller that resolves the specified name and parameters. If not Juzu will make the compilation fail and give detailled information about the error. This kind of feature makes Juzu really unique among all other web frameworks, we will see some other later.

Note

Juzu leverages the Annotation Processing Tool (APT) facility standardized since Java 6. APT works with any Java compiler and is not specific to a build system or IDE, it just works anywhere, we will see later that it even works with Eclipse incremental compiler.

Now it's time to introduce action controllers, actions are method annotated by the @Action annotation. Unlike views, actions are only called when an action url is processed by the portal, whereas a view controller method can be invoked any time by the portal.

The role of an action controller is to process actions parameters. Each parameter of an action controller method is mapped to the incoming request processed by the portal, such parameters can be encoded directly in the URL or be present in the form that triggers the action.

The weather temperature in ${location} is ${temperature} degrees.

<ul><% locations.each() { location -> %>
<li><a href="@{index(location = location)}">${location}</a></li>
<% } %>
</ul>

<form action="@{add()}" method="post">
   <input type="text" name="location" value=""/>
   <input type="submit"/>
</form>

In our example, we use a form which contains the the #location# action parameters. In order to create an action url we use the same syntax shown for view url @{add()} but this time we don't need to set any parameter, instead the form parameters will be used when the form is submitted. However this is not mandatory and instead we could have url parameters such as @{add(location = 'washington'}, such syntax is valid specially when it is used without a form. Obviously there is the possibility to mix form and action parameters.

When the url is processed, the following action controller method will be invoked:

  @Action
  public Response add(String location) {
    locations.add(location);
    return Weather_.index(location);
  }

The method process the location parameter and add it to the locations set. After this the portal will proceed to the page rendering phase and will call the index() method to refresh the application.

We have seen previously how render templates from a controller by passing them parameters. Templates use ${ } expressions that often refers to parameters passed by the controller when it renders the template. For this purpose we used an HashMap in which we put the various parameters that the template will use during rendering.

This syntax is a generic way to do by using an untyped syntax, indeed if a template parameter name changes the controller will continue to compile because of the generic parameter map. To improve this situation, parameters can be declared thanks to a param tag inside the template:

#{param name=location/}
#{param name=temperature/}
#{param name=locations/}

The weather temperature in ${location} is ${temperature} degrees.

<ul><% locations.each() { location -> %>
<li><a href="@{index(location = location)}">${location}</a></li>
<% } %>
</ul>

<form action="@{add()}" method="post">
   <input type="text" name="location" value=""/>
   <input type="submit"/>
</form>

For instance the location parameter is declared by the #{param name=location/} tag. During the Java compilation, Juzu leverage those parameter declarations to provide a more convenient way to render a template.

Indeed the tight integration with the Java compiler allows Juzu to generate a template class for each template of the application, such template class inherits the Template class and adds specific methods for passing parameters to a template in a safe manner.

  @View
  public void index(String location) {
    index.
      with().
      location(location).
      temperature(weatherService.getTemperature(location)).
      locations(locations).
      render();
  }

As we can see, the HashMap is not used anymore and now we use a type safe and compact expression for rendering the template. Each declared parameter generates a method named by the parameter name, for the location parameter, we do have now a location(String location) method that can be used. To make the syntax fluent, the parameter methods can be chained, finally the render() method is invoked to render the template, however it does not require any parameter since all parameters where passed thanks to the parameter methods.

The Java name of the generated template class is the name of the template in the templates package of the application. In our case we do obtain the examples.tutorial.weather6.templates.index class name. It is very easy to use our subclass by injecting the template subclass instead of the generic Template class.

  @Inject
  @Path("index.gtmpl")
  examples.tutorial.weather6.templates.index index;

Of course it is possible to import this value and use directly the index class name. We used directly the full qualified name of the class for the sake of the clarity.

A portlet provides a PortletPreferences object for loading and saveing user preferences, it is a collection of key and values of type string, very much like a map. Juzu does not wrap the preferences object, instead it can be injected via the @Inject annotation:

  @Inject
  PortletPreferences preferences;

The preferences object is available during the various phases of the application and can be used to retrieve and store user preferences, but keep in mind that storing preferences can only be done an action phase.

Note

The preferences object is bound in the IOC container with the request scope, since the validity of the preferences object is associated with a portlet request. There is no need to declared the request scope in the preferences injection.

Reading preferences

To use the preferences in the templates we have modified the template to contain an additional grade parameter that will be configured by the index method when it renders the template. The grade value is obtained from the preferences field at the beginning of the index method:

  @View
  public void index(String location) {
    String grade = preferences.getValue("grade", "c");
    index.
      with().
      location(location).
      temperature(weatherService.getTemperature(location, grade)).
      grade(grade).
      locations(locations).
      render();
  }

Writing preferences

Preferences can be updated during the action phase of the application. The action phase is triggered by a form in the template:

#{param name=location/}
#{param name=temperature/}
#{param name=locations/}
#{param name=grade/}

<form style="float:right" action="@{updateGrade()}" method="post">
    <select name="grade" onChange="javascript:this.form.submit();">
        <option value="c" <%=grade=='c'?'selected':''%>>celsius</option>
        <option value="f" <%=grade=='f'?'selected':''%>>fahrenheit</option>
    </select>
</form>

The weather temperature in ${location} is ${temperature} degrees.

<ul><% locations.each() { location -> %>
    <li><a href="@{index(location = location)}">${location}</a></li>
    <% } %>
</ul>

<form action="@{add()}" method="post">
    <input type="text" name="location" value=""/>
    <input type="submit"/>
</form>

The form action value is set to @{updateGrade()} URL that will invoke the corresponding method in the controller:

  @Action
  public void updateGrade(String grade) throws java.io.IOException,
    javax.portlet.PortletException {
    preferences.setValue("grade", grade);
    preferences.store();
  }

The updateGrade action updates the preferences and then invoke the store() method to persist the changes.

Our application is almost complete, in this section we will work on the look and feel to make the application appealing. We will use the famous Twitter Bootstrap framework and show how it integrates well in Juzu applications.

The style

Bootstrap provides a solid fundation for building quickly attractive applications. However the default look and feel does not perfectly fit our needs:

  • The default stylesheet does not work very well in a portal application as it applies to the entire screen and we need to operate only on our application

  • We don't need all the out of the box components and only a subset providing a smaller stylesheet than the default stylesheet

Fortunately Bootstrap is based on the dynamic stylesheet language Less and provides a very modular organization. We will perform trivial modifications to a subset of the Less files and integrate them in our application.

A la carte

We will then modify the bootstrap.less file to keep only what is necessary for our application:

Example 8.1. The necessary Bootstrap less files

/*!
 * Bootstrap v2.0.3
 *
 * Copyright 2012 Twitter, Inc
 * Licensed under the Apache License v2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Designed and built with all the love in the world @twitter by @mdo and @fat.
 */

// CSS Reset
@import "reset.less";

// Core variables and mixins
@import "variables.less"; // Modify this for custom colors, font-sizes, etc
@import "mixins.less";

// Grid system and page structure
@import "scaffolding.less";

// Base CSS
@import "type.less";
@import "forms.less";

// Components: common
@import "component-animations.less";

// Components: Buttons & Alerts
@import "buttons.less";
@import "button-groups.less";

// Components: Misc
@import "accordion.less";


This version of bootstrap.less is a trimmed down of the original files.

Scoping the styles

By default, the Bootstrap rules will affect the entire page, we want it to affect only our application. We can modify the CSS rules to operate on a portion of the page based on a specific class name with a very simple pattern: add a class prefix to a rule to reduce the scope of that rule. For example:

Example 8.2. The rule to scope

div.red { color:red; }


Adding to the rule the .tutorial prefix transforms the rule and reduce its scope to an area boxed by the tutorial class name:

Example 8.3. The rule scoped

.tutorial div.red { color:red; }


In the previous example we modified a single CSS rule, in reality we should modify all the rules of a stylesheet, which means many rules. To achieve our goal we need a global approach, here again the Less framework provides what we need: great power at a very tiny cost.

Indeed the Less language provides nested rules, our previous example can be rewritten in the Less language as:

Example 8.4. The rule scoped

.tutorial {
  div.red { color:red; }
}


This approach allows us to encapsulate an entire stylesheet and mass prefix all the rules contained within the scope of the outter rule. For this reason all the Bootstrap less sources have been modified to be scoped with the tutorial class name.

Plugins in action

Juzu can be extended with plugins, in this section we will use two of them

  • The Less plugin compiles less files into css files

  • The Asset plugin inject asset such as stylesheet or javascript in the application page

Less compilation

Juzu provides native support for the Less language via the Less plugin and the @Less annotation. It allows a set of less files to be transformed into the corresponding css files during the java compilation, achieving two important steps during the compilation phase:

  • The less files are transformed into ready to use css files

  • It ensures a maximum of safety: the Less parser will report any error in the source

Our first step is to create the examples.tutorial.assets package, we copy the Bootstrap Less files into this package and annotate the examples.tutorial package with the @Less annotation to trigger the compilation of the stylesheet in the assets package:

@Less(value = "bootstrap.less", minify = true)
package examples.tutorial;

import juzu.plugin.less.Less;

This annotation triggers the compilation of the bootstrap.less in the assets package, the minified parameter instruct Less to minify the resulting css.

Injecting CSS

Now that we have worked out the CSS details we need to make our stylesheet available in the application page. The asset plugin will achieve this result for us. This plugin provides declarative configuration for the various assets required by an application. It works both for stylesheets and javascript, in this section we use it for stylesheet:

Example 8.5. Injecting Bootstrap CSS in our application

@Application
@Assets(stylesheets = @Stylesheet(
  src = "/examples/tutorial/assets/bootstrap.css",
  location = AssetLocation.CLASSPATH)
)
package examples.tutorial.weather8;


The usage is fairly straightforward with the @Assets and Stylesheet annotations. We configure the location parameter to be CLASSPATH because the Less plugin created it there.

Bringing CSS to life

After this step we need to modify our application template to use the various styles provided by Bootstrap:

Figure 8.1. The Bootstrapized application

The Bootstrapized application


We will not explain that in details, however we will study the important modifications:

Scoping the rules

As seen previously we need to scope our markup with the tutorial class, it is achieved by wrapping our application markup with a div tag:

<div class="tutorial">
  ...
</div>

Accordion

The Bootstrap provides the Collapse component. We will not use the entire Collapse component here but instead reuse the CSS rules to display the available cities:

<div class="accordion-group">
  <div class="accordion-heading"><a class="accordion-toggle" href="@{index(location = current)}">${current}</a></div>
  <div class="accordion-body">
    <div class="accordion-inner">The weather temperature in ${current} is ${temperature}&deg; ${grade.toUpperCase()}.</div>
  </div>
</div>

Temperature selection

The switch between Celsius and Fahrenheit is done thanks to Boostrap button groups and inverse rules:

  <a class="btn <%=grade=='c'?'btn-inverse':''%>"
    href="@{updateGrade(grade='c',location=location)}">C</a>
  <a class="btn <%=grade=='f'?'btn-inverse':''%>"
    href="@{updateGrade(grade='f',location=location)}">F</a>

Adding a city

Finally the form for adding is modified to reuse Bootstrap form support:

<form action="@{add()}" method="post">
  <fieldset>
    <div class="controls">
      <div class="input-append">
         <input class="span2" type="text" size="16"name="location" value=""/>
         <button type="submit" class="btn">Add</button>
      </div>
    </div>
  </fieldset>
</form>

Now that our application look great, we are going to add a final touch to our application and make it more dynamic.

In the previous section we introduced the accordion user interface component, but it was used in a static way. In this section we will make it dynamic and introduce the Ajax plugin.

The accordion component can be combined to the JQuery collapse plugin providing the capability to unfold an item to display the weather of the location. When the item is unfolded an ajax request is performed to the application to retrieve the markup that will be inserted.

Javascript to the rescue

The application will use several Javascript libraries:

  • The JQuery library provides the fundations for building our application

  • The Bootstrap accordion component provides scripts as a JQuery plugin

  • Juzu provides Ajax helpers as a JQuery plugin

  • A small custom script to setup the accordion plugin with the ajax plugin

Adding scripts

The Asset plugin was introduced in the previous section to handle the serving of the Bootstrap stylesheets. The @Asset annotation can be used also for embedding scripts, but it provides more control about the script, in particular a dependency mechanism to control the order of scripts that we will use: indeed JQuery plugins have to be loaded after the JQuery library is loaded.

We extend the @Asset annotation to add our scripts:

@Assets(
   scripts = {
      @Script(
         id = "jquery", (1) 
         src = "jquery-1.7.1.min.js",
         location = AssetLocation.CLASSPATH),
      @Script(
         id = "transition",
         src = "bootstrap-transition.js", (2) 
         location = AssetLocation.CLASSPATH,
         depends = "jquery"), (1) 
      @Script(
         id = "collapse",
         src = "bootstrap-collapse.js",
         location = AssetLocation.CLASSPATH,
         depends = {
            "jquery", // 1
            "transition"}), (2) 
      @Script(
         src = "weather.js",
         location = AssetLocation.CLASSPATH,
         depends = {
            "jquery",  (1) 
            "collapse"}) (3) 
   },
   stylesheets = @Stylesheet(src = "/examples/tutorial/assets/bootstrap.css", location = AssetLocation.CLASSPATH)
)
package examples.tutorial.weather9;


1

All other scripts than JQuery depends on JQuery

2

The collapse JQuery plugin depends on the transition script

3

Our weather script depends also on the JQuery collapse plugin

The declaration is straightforward, any @Script annotation may configure

  • an optional id used for creating dependencies between scripts

  • a mandatory src for the script name

  • an optional location for resolving the script

  • an optional depends that declares the ids a script depends on

The collapse plugin

The Bootstrap collapse plugin allows the user to unfold a city to display its weather, you can see how it works in the Bootstrap manual. In our case we modify the index.gtmpl to use it, in particular the accordion-group part:

<div class="accordion-group">
  <div class="accordion-heading">
    <a class="accordion-toggle" href="#${current}" data-parent="${id}" data-toggle="collapse">${current}</a>
  </div>
  <% def expanded = i != index ? 'in' : ''; %>
  <div id="${current}" class="accordion-body collapse ${expanded}">
    <div class="accordion-inner">
    </div>
  </div>
</div>

There are two noticeable points to explain:

  • The accordion-inner is empty now, the reason is that the weather will be loaded using JQuery ajax capabilities

  • The div.collapse element id is set to the current item location, this value will be reused during an ajax interaction, we will see more about this later.

Bridge the gap with Ajax

Now our application loads a set of dynamic tabs managed by JQuery and the collapse plugin, it's time to develop the ajax part: our goal is to load a markup fragment to insert in an accordion item when it is unfolded. We will develop a bit of client side Javascript and a resource controller on the Weather controller.

Resource controller

We need a controller method to server the markup of the weather of a particular city. Until now we have studied view and action controllers, for this use case we will use a new type of controller : the resource controller.

Resource controllers are pretty much like a view controllers except that they must produce the entire response sent to the client and that is precisely what we want to achieve on the server side.

Our application requires a single resource controller method that we will call getFragment, it will render a markup fragment for a specific city location:

  @Inject
  @Path("fragment.gtmpl")
  examples.tutorial.weather9.templates.fragment fragment;

  @Ajax
  @Resource
  public void getFragment(String location) {
    String grade = preferences.getValue("grade", "c");
    String temperature = weatherService.getTemperature(location, grade);
    fragment.
      with().
      location(location).
      temperature(weatherService.getTemperature(location, grade)).
      grade(grade).
      render();
  }

The fragment template is very simple and only renders the portion of the screen that will be updated by the client side javascript code when an item is unfolded:

#{param name=location/}
#{param name=temperature/}
#{param name=grade/}
The weather temperature in ${location} is ${temperature}&deg;
${grade.toUpperCase()}.

JQuery

The last part of our application is the javascript part that will react on the collapse plugin component to load the markup from the getFragment controller when the item is unfolded. We create the javascript file weather.js in the examples.tutorial.weather9.assets:

$(function () {

  // Setup an event listener on the .collapse element when it is shown
  $('.collapse').on('show', function () {

    // Get the location attribute from the dom
    var location = $(this).attr("id");

    // Update the .accordion-inner fragment
    $(this).
      closest(".accordion-group").
      find(".accordion-inner").
      each(function () {

      // Load the fragment from the resource controller
      $(this).jzLoad(
        "Weather.getFragment()",
        {"location":location});

    });
  });

  // Setup the collapse component
  $(".collapse").collapse();
});

The following construct is important because it ensures that the code inside the function will be executed when JQuery is loaded:

$(function() {
  //
});

If you take time to read the collapse plugin component documentation you will see that there are two important things to do for integrating it in our application:

  • We must setup an event listener on .collapse elements to react to the shown event

  • The collapase component is setup with the $(".collapse").collapse() code

The important code is inside the show event listener:

  • The location to display is extracted from the element id on the div.collapse component

  • We find the appropriate element to update with the $(this).closest(".accordion-group").find(".accordion-inner") selectors

  • On the .accordion-inner element we invoke the resource controller with the jzLoad(...) function

The jzLoad function is a JQuery plugin provided by the Juzu ajax plugin. It allows to invoke a controller method using ajax and cares about propagating the call to the resource controller method. It replaces the standard JQuery load function and accepts the same argument. However the url part is replaced by a controller method, Weather.getFragment() in our case.

The Juzu ajax plugin takes care of finding the right URL for invoking the controller method. It is designed to work in a standalone javascript file without requiring <script> tags in the page and works even when multiples instances of the portlets are on the same page.

When the ajax plugin operates on @Ajax controller method, it wraps the markup with a DOM structure that contains the URL for the @Ajax method of the application:

<div class="jz">
  <div data-method-id="Weather.getFragment()## data-url="http://localhost:8080/....."/>
  ...
</div>

The jzLoad function is invoked on a portion of the DOM that is wrapped by the div.jz element and this function will simply locate the correct URL using the data-method-id attribute. This makes the same script work with different portlets on the same page.

Note also that in our code we never used any JQuery selector containing id in order to allow several instances of the same application to work without conflicts.

We reached the ends our walk through Juzu, now you can learn more and study the Booking application. This application can be found in the package you downloaded in the booking directory.