Wednesday, February 22, 2012

Using jQueryMobile and Backbone.js for handling forms

Introduction

In this post, I continue the development of my basic "exercise app" that I started (and enhanced) in these posts:

Let's add the ability to create and edit exercise records.

Adding a new activity

Just like in the previous post, the first thing we need is a jQueryMobile page to hold the form content. Add the following to index.html:

    
Save

New Activity

In the code above, we see the typical jQueryMobile page. The main things of interest here are with respect to the anchor tag in the header div:

  • Specify data-rel="back" to navigate to the previous page once the save is complete. This way whether we enter the form page from "Add" or "Edit", navigation will be handled.
  • The class="ui-btn-right" moves the button to the right side of the header bar.
  • The data-theme="b" causes the save button to use the blue theme.
The next step is to define the template that the view will use to render the content for this page. Lets start with a simple template with an input for each attribute of an activity. To vary the UI slightly, we can use a drop down for the activity type. The basic template will look like this:

    

Notice that we are using the HTML 5 input types for some of the fields. Browser support for these will vary, but worst case they will deprecate to a text input type. For example, on iOS, the date will present a native date picker, whereas on Android (or Chrome, etc.) the field will behave as a standard text input.

Now we need to navigate to the form page. This requires modifying the "Add" button on the list page.

    

Activities

Add

In order to transition to the form page, the href attribute on the anchor tag (i.e. button) needs to be changed to the id of the form page, as shown in line 4 above. The final step in presenting the form page to support adding exercise activities is modifying the add button click handler.

    $('#add-button').live('click', function(){
        var activity = new exercise.Activity(),
            activityForm = $('#activity-form-form'),
            activityFormView;

        activityFormView = new exercise.ActivityFormView({model: activity, viewContainer: activityForm});
        activityFormView.render();
    });

Here we create an empty model, grab the form node, create a new exercise.ActivityFormView with these objects, and render the form. Testing at this point reveals a problem. The screen shot below doesn't look right ;)

Investigating the browsers console reveals an error: Uncaught ReferenceError: distance is not defined. This is due to the underscore template trying to access an undefined attribute of the model. The issue is described here with some work arounds. I found the easiest workaround is to provide defaults for the model, as shown below.

     exercise.Activity = Backbone.Model.extend({
        defaults: {
            date: '',
            type: '',
            distance: '',
            comments: '',
            minutes: ''
        }
     });

Now the form renders properly. With the help of some CSS, we can tighten things up a little and end up with the version on the right (the CSS is part of the source code, see the links at the end of this post).

Editing an existing activity

With the template and the view already implemented, adding edit functionality is pretty straightforward. The first step is to update the href attribute of the edit button on the activity-details page.

    
Edit

Activity Details

As we did with the add button, this should reference the activity-form page. Now we need to add the click event handler for the edit button.

    $('#edit-activity-button').live('click', function() {
        var activityId = $('#activity-details').jqmData('activityId'),
            activityModel = exercise.activities.get(activityId),
            activityForm = $('#activity-form-form'),
            activityFormView;
        
        activityFormView = new exercise.ActivityFormView({model: activityModel, viewContainer: activityForm});
        activityFormView.render();
    });

If you recall, the activityId is passed to the details page view the list item click event handler. We reuse that fact here to retrieve the activity from the collection.

Time to test. Selecting an activity renders the detail page. Clicking the edit button renders the form, pre-filled with the activity details. Everything looks great.

But wait.....that screenshot was from the browser on which I am testing. We should always test on devices as well. Earlier I mentioned that we are using some of the HTML 5 input types (i.e. date). The nice thing is that iOS 5 will render a nice date picker for date types. The challenge is that the date needs to be in a specific format for that to work. The image below shows what the form page looks like on an iOS device.


Fixing the Date

No date value is showing up on our iOS device. This is a known issue. So, for iOS we need the date format as yyyy-mm-dd, while for other platforms, I prefer the date to be shown as mm/dd/yyyy. I do realize that date formats should be localized, but for the purpose of this example I would like to keep it straightforward and specify mm/dd/yyyy as the display date format. There are many options that could be pursued here, but to demonstrate some more capabilities of Backbone.js, we will modify our model to help meet our date requirements.

First, the date attribute in our JSON feed is a string. Lets convert this to a date when the data is retrieved from the server, then when we need the date, we can just manipulate it as needed. This can be accomplished several ways.

  • Implement a parse method on the collection that converts the String to a Date as the data is being fetched from the server.
  • Implement a method on the model that converts the date String to a Date and sets it on the model. This would then require the calling code to know to call this method depending upon the circumstance.
  • Override the set method on the model, look for the date attribute, then convert it to a date as needed.
The last option is the most seamless approach. Add the following method to the Activity model.

        set: function(attributes, options) {
            var aDate;
            if (attributes.date){
                //TODO future version - make sure date is valid format during input
                aDate = new Date(attributes.date);
                if ( Object.prototype.toString.call(aDate) === "[object Date]" && !isNaN(aDate.getTime()) ){
                    attributes.date = aDate;
                }
            }
            Backbone.Model.prototype.set.call(this, attributes, options);
        }

Also, update the defaults so that the date attribute is now a Date instead of a String.

        defaults: {
            date: new Date(),
            type: '',
            distance: '',
            comments: '',
            minutes: ''
        },

While this clearly isn't the most robust date handling, it is fine for now. In a future version, this should be improved.

Next, add attributes to our model that will format the date in the ways we need it (i.e. mm/dd/yyyy and yyyy-mm-dd).

        dateInputType: function(){
            return exercise.formatDate(this.get('date'), "yyyy-mm-dd"); //https://github.com/jquery/jquery-mobile/issues/2755
        },
        
        displayDate: function(){
            return exercise.formatDate(this.get('date'), "mm/dd/yyyy");
        }

The formatDate function is a simple date formatter to meet our specific needs and can be found in the source code that accompanied this post (see link at the bottom of the post).

Now, how do we use these new methods in our view. The first thing to realize is that we pass the template JSON. The default implementation of the toJSON method of a Backbone.js model will not include these functions. Therefore, we need to override the toJSON method.

        toJSON: function(){
            var json = Backbone.Model.prototype.toJSON.call(this);
            return _.extend(json, {dateInputType : this.dateInputType(), displayDate: this.displayDate()});
        }

Here, we are using the Underscore.js extend to add our attributes to the standard Backbone JSON. Now, we need to modify our view templates to use the appropriate JSON attributes.

    
    
    
    
    

Notice that lines 16-21 include some conditional logic. This is a very basic device detection check to determine which date format to use. There are plug-ins,etc. that provide more robust alternatives, but to keep things clear, this will serve our needs. The screen shots below show the desktop browser and the iOS versions of the form with the appropriate date handling.

Saving

The final step is to implement the save functionality.

    $('#save-activity-button').live('click', function(){
        var activityId = $('#activity-details').jqmData('activityId'),
            activity,
            dateComponents,
            formJSON = $('#activity-form-form').formParams();
        
        //if we are on iOS and we have a date...convert it from yyyy-mm-dd back to mm/dd/yyyy
        //TODO future version - for non-iOS, we would need to validate the date is in the expected format (mm/dd/yyyy)
        if (formJSON.date && ((navigator.userAgent.indexOf('iPhone') >= 0 || navigator.userAgent.indexOf('iPad') >= 0)) ){
            dateComponents = formJSON.date.split("-");
            formJSON.date = dateComponents[1] + "/" + dateComponents[2] + "/" + dateComponents[0];
        }
        
        if (activityId){
            //editing
            activity = exercise.activities.get(activityId);
            activity.set(formJSON); //not calling save since we have no REST backend...save in memory
        }else{
            //new (since we have no REST backend, create a new model and add to collection to prevent Backbone making REST calls)
            activity = new exercise.Activity(formJSON);
            activity.set({'id': new Date().getTime()});  //create some identifier
            exercise.activities.add(activity);
        }
    });

I used the JavascriptMVC formParams jQuery plugin (found here) to convert my HTML form into a JSON object. Then, the date is converted into the appropriate format. One thing to note here is that since we have no real back end server for Backbone to interface with, we don't call save on the model or create on the collection. These methods would cause the appropriate REST calls to the server.

Since the click event on the list view passes the activityId to the details page, we can use this to determine if we are adding or editing. One thing to consider here is that the user may click an existing activity, then go back to the list, then click add. In this case, the activityId from the previously selected activity is still attached to the activity-details view. This will cause our save implementation to think we are editing. To prevent this, we should remove the activityId from the activity-details page when adding a new activity. This can be accomplished by adding the highlighted line below to the add handler.

    $('#add-button').live('click', function(){
        var activity = new exercise.Activity(),
            activityForm = $('#activity-form-form'),
            activityFormView;
    
        //clear any existing id attribute from the form page
        $('#activity-details').jqmRemoveData('activityId');
        activityFormView = new exercise.ActivityFormView({model: activity, viewContainer: activityForm});
        activityFormView.render();
    });

The final step is to make sure our views are updated accordingly when we add or modify activities. Since the details view retrieves the activity before the page loads, no change is required here. But our list view only renders during the initial page load. This is where we can take advantage of Backbone event binding. A few small additions to the ActivityListView and things will be handled.

        initialize: function() {
            this.collection.bind('add', this.render, this);
            this.collection.bind('change', this.changeItem, this);
            this.collection.bind('reset', this.render, this);
            this.template = _.template($('#activity-list-item-template').html());
        },

        ...

        changeItem: function(item){
            this.collection.sort();
        }

The change event will bubble up from the model and fires as a result of the set being called. In the changeItem method, we sort the collection to handle any changes in the date attribute. The sort method call will cause the reset event to fire, which then results in the render method invocation, causing the list to be re-rendered. This keeps everything in the correct order. The add event will be fired when we add a new activity to the collection. Since we implemented the comparator method on the collection, models added to the collection will be added in the appropriate order.

We can now add and edit activities. Start exercising :)

The source code for this post can be found here

Wednesday, February 15, 2012

Sencha Touch 2 - NavigationView, Templates and Forms

This post will build on the example application from the Introduction to Sencha Touch post. We will look at sorting, templates, navigation and forms.

Sorting

In the existing code new items appear at the bottom of the list. I’d prefer to see the newest items first. After a Sorter is added to the Store, Sencha displays records in the proper order.
var store = Ext.create('Ext.data.Store', {
    storeId: "activityStore",
    model: "Activity",
    proxy: {
        type: 'ajax',
        url: 'exercise.json'
    },
    sorters: [
        {
            property: 'date',
            direction: 'DESC'
        }
    ],
    autoLoad: true
});

Detail View

The user should able to drill into an Activity on the list to see the details.

Template

We will create an XTemplate that will be populated with the data from the Model. Templates can be created in Javascript or externalized. I prefer to put more complex templates in HTML rather than inline Javascript. Sencha recommends external templates are defined in a hidden textarea.
<textarea id="detail-template" style="display:none">
  <div>
    Date: {date:date("m/d/Y")} <br/>
    Type: {type} <br/>
    Distance: {distance} <br/>
    Time: {minutes} minutes <br/> 
    Notes: {comments} <br/>
  </div>    
</textarea>
The template is created once in code.
var template = Ext.XTemplate.from(Ext.get('detail-template'));

List item disclosure

Sencha displays a disclosure icon for each row when a function is assigned to the onItemDisclosure property of the list. Clicking on this icon allows the user to drill into the details for a row.
The onItemDisclosure function
  • receives the selected record as an argument
  • populates the template with the model’s data
  • creates a new panel
  • pushes the panel onto the navigation view
   {
       xtype: 'list',
       title: 'Activities',
       itemTpl: '{date:date("m/d/Y")} - {type}',
       store: store,
       onItemDisclosure: function (record, btn, index) {
           view.push({
               xtype: 'panel',
               title: 'Activity',
               html: template.apply(record.data),
               styleHtmlContent: true
           });
       }
   }

Pushing the panel onto the NavigationView handles a lot of stuff. The view becomes visible, the navigation bar title is set, a back button is displayed, navigation is handled automatically. This is a nice new feature in Sencha Touch 2.0.

Disclosure icon Record detail

Showing and hiding buttons

When the user is viewing the details, the Add button should disappear. We can manually hide the button in the onItemDisclosure before pushing the view.
onItemDisclosure: function (record, btn, index) {
                      view.down('#addButton').hide();
                      view.push({
                      // ...
The Add button needs to be shown again when the user navigates from the detail back to the main screen. Since the NavigationView handles the navigation automatically, we listen for the pop event and make the Add button is visible.
var onNavigationPop = function(v, item) {
   v.down('#addButton').show();
};

var view = Ext.create("Ext.NavigationView", {
   fullscreen: true,
   items: [
      // ...
   ],
   listeners: {
       pop: onNavigationPop
   }
});
Similarly, we can add onNavigationPush to hide the Add button whenever we push a new view on the stack. This is better than hiding the button in onItemDisclosure.
var onNavigationPush = function(v, item) {
    v.down('#addButton').hide();
};

var view = Ext.create("Ext.NavigationView", {
   fullscreen: true,
   items: [
      // ...
   ],
   listeners: {
       pop: onNavigationPop,
       push: onNavigationPush
   }
});

Record detail with button Record detail without button

Finally, instead of word Add on the button, I think it looks better with an icon.
view.getNavigationBar().add([
  {
      xtype: 'button',
      id: 'addButton',
      align: 'right',
      ui: 'action',
      iconCls: 'compose',
      iconMask: true, 
      handler: addNewRow
  }
]);

Add button text Add button icon

Adding new records with a form

The current implementation puts a fake row into the store when the Add button is pressed. We want to display a form to collect the user input.
We are going to change the implementation of addNewRow to create a FormPanel and push it onto the view. The NavigationView destroys components as they are popped, so we recreate the form every time. Alternately we could set autoDestroy to false on the NavigationView.
var addNewRow = function() {
    view.push(Ext.create('Ext.form.Panel', formBase));
};
The formBase object contains the form fields.
var formBase = {
    title: "Activity",
    itemId: "form",
    items: [
        {
            xtype: 'datepickerfield',
            name: 'date',
            label: 'Date',
            value: new Date()
        },
        {
            xtype: 'selectfield',
            name: 'type',
            label: 'Type',
            options: [
                { text: "" },
                { text: "Run", value: "Run" },
                { text: "Bike", value: "Bike" },
                { text: "Swim", value: "Swim" },
                { text: "Walk", value: "Walk" }
            ]
        },
        {
            xtype: 'textfield',
            name: 'distance',
            label: 'Distance'
        },
        {
            xtype: 'numberfield',
            name: 'minutes',
            label: 'Minutes'
        },
        {
            xtype: 'textfield',
            name: 'comments',
            label: 'Notes'
        }
    ]
};

Saving data

The navigation bar needs a save button. Initially this button will be hidden.
{
    xtype: 'button',
    id: 'saveButton',
    text: 'Save',
    align: 'right',
    ui: 'action',
    handler: save,
    hidden: true
}
The addNewRow function makes the save button visible.
var addNewRow = function() {
    view.push(Ext.create('Ext.form.Panel', formBase));
    view.down('#saveButton').show();
};
The save button is hidden in onNavigationPop.
var onNavigationPop = function(v, item) {
    v.down('#addButton').show();
    v.down('#saveButton').hide();
};
The save function
  • gets the form using the item id
  • creates an new Activity model from the form data
  • adds the new model to the store
  • navigates back to the list view
var save = function() {
   var form = view.down('#form');
   var record = Ext.create('Activity', form.getValues());
   store.add(record);
   // TODO validation and error handling
   view.pop();
};

Note that Sencha automatically displays the new data in the list without any effort on our part.

Form New row

Additional enhancements to improve this example could include things like validating form data, editing existing rows, sending data back to the server, converting to MVC.

The source code is available on github.

Monday, January 30, 2012

From a List to a Details View using jQueryMobile and Backbone.js

In my previous post I built a basic application to demonstrate the use of Backbone.js with jQueryMobile (JQM). The introduction can be found here, with a brief subsequent post on sorting collections here. In this post, I would like to add the capability to view the details of the items presented in the list view.

The first step is to create a new JQM page to display the details view. JQM makes it pretty easy to add pages to your application. I added the following code to the index.html file:

This will create the structure for the page, and Backbone.js will be used to fill in the content div based on the record tapped (or clicked) within the list view. The next step is to define the template for the details view. This is the pattern that I follow when developing using jQueryMobile and Backbone.js. The template can be inserted below the list item template in index.html.

I decided to embed the details in a read-only list view, this way jQueryMobile will provide some reasonable styling. Since the point here is to demonstrate Backbone and jQueryMobile playing together, I didn't want to have to spend much time on style :)

Next, we need to define the Backbone view that will use the template to render the appropriate content. All this View needs to do is apply the model to the template and append it to the HTML container defined when the View is instantiated.

In order to retrieve the correct model to bind to the details view, we need to know what row in the list view was clicked (or tapped). To do this, we can bind to the click event of the item in the list view. This can be accomplished by modifying the ActivityListView render method. Here is the current version of the list view:

The key area to focus on here is lines 18-20, where the activity item HTML is rendered and appended to the list view. This is where the modification needs to occur. Each activity item HTML element needs to be bound to a click event. In this click event, the activity id will need to somehow be passed to the details view so the appropriate look up can occur. There are several ways to do this. Approaches I have used in the past include the use of jQuery.jqmData(...) or session local storage (assuming HTML5). In this example, we will use the jqmData method. The trick is to capture the id during the rendering of the list so that it can be used during the execution of the click event. Below are the required modifications to lines 18-20 of the previous code snippet.

The first thing is to capture the rendered activity item HTML in a variable and cast it to a jQuery object, as can be seen in lines 2-3. Line 4 is where the activity id data is attached to our activity item HTML element. Then, the bind event (lines 5-8) retrieves the attached data and sets it on the activity details HTML element, which is our activity details page HTML. Per the jQuery documentation, this within a bind method refers to the DOM element to which the event handler is bound. We can use that to get at the activity id data and attach it to the details view. This gives us the ability to pass the appropriate id at runtime to the details page.

Now we need to wire all this together. The typical pattern I follow is to use the jQueryMobile pagebeforeshow event to set up everything needed to render a complete page. This acts as my controller.

This code retrieves the id data attached to the activity details element, looks up the model using the Backbone API (line 5), instantiates the view with the model and the view container, and calls the render method to render the final HTML. The end result looks like this:

The source code for this post can be found here. Note that this is a branch of my repository for this sample app. Each blog post associated with this code base resides on a separate branch.

Related Posts:

Tuesday, January 24, 2012

Spring 3.1 - Constructor Namespace

Spring Namespaces


Spring provides several namespaces to simplify XML configuration, such as jdbc, tx, aop, etc. We Spring developers are already familiar with the required beans namespace.

<beans>
  <bean id="dataSource" class="..."/>
  <bean id="messagingProvider" class="..."/>
</beans>

Spring Namespaces are defined at the top of the XML file. Here we define the namespaces we need to use, and reference the schema (XSD) that validates the XML.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.chariotsolutions"/>

    <bean id="simpleBean"
      class="com.chariotsolutions.springthreeone.SimpleBean"/>

</beans>

In Spring XML configuration we declare dependency injection with either "setter injection" (via properties tag) or "constructor injection". Spring already provided the Property or "p" namespace, to simplify property setting, in other words, invoking setters on a bean.

Here we have a simple bean that can be configured with either a constructor or setter.

public class SimpleBean {
  private String myString;
  private String myOtherString;

  public SimpleBean() {}

  public SimpleBean(String myString) {
    this.myString = myString;
  }

  public SimpleBean(String myString, String other) {
    this.myString = myString;
    this.myOtherString = other;
  }

  public String getMyString() {
    return myString;
  }

  public String getMyOtherString() {
    return myOtherString;
  }
}


Instead of

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="setterBean"
      class="com.chariotsolutions.springthreeone.SimpleBean">
        <property name="myString" value="Setter Set"/>
    </bean>

</beans>

We could use

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="setterBean"
    class="com.chariotsolutions.springthreeone.SimpleBean"
      p:myString="Setter Set"/>

</beans>

With the Property namespace, there is no significant benefit, it is simple a matter of preference.


Spring Constructor Injection

Some developers choose to use "constructor injection" for several reasons.
  • Setting required elements
  • Immutable classes for thread safety
  • Third party class that only provides constructor setting

One of the main issues with "constructor injection", prior to Spring 3.1, is that it is unclear which arguments are being set in the constructor injection. Also, if the constructor has multiple arguments, one can inadvertently set arguments in the wrong sequence.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="setterBean" class="com.chariotsolutions.springthreeone.SimpleBean">
    <constructor-arg value="squibbity doo"/>
    <constructor-arg value="flabbity doo"/>
  </bean>
</beans>

Spring 3.1 Constructor "c" Namespace

Unlike the "p" namespace, the "c" namespace provides us with descriptive references, allowing us to reference the constructor's arguments by name. This feature is even more useful when we have multiple constructor arguments, as below.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:c="http://www.springframework.org/schema/c"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

  <bean id="setterBean" class="com.chariotsolutions.springthreeone.SimpleBean"
    c:myString="squibbity doo"
    c:myOtherString="flabbity doo"/>

</beans>

Deployment Requirements

NOTE: Code using the "c" namespace, MUST be deployed with Java Debugging Tables (the -g option)!


Summary

We Spring developers have shortcuts at our disposal to make configuration easier or less terse. The new "c" namespace provides us the benefit of named variables that we know from using the "p" namespace or <property ... />. We should strive to make our beans immutable for thread safety. By using the "c" namespace get the developers lean towards property setting for readability.

Notice that both the "p" ad "c" namespaces do not have a schema reference in the XML header.

Monday, January 23, 2012

Introduction to Sencha Touch

Steve Smith recently looked at jQuery Mobile and Backbone using a simple application that allowed a user to track their daily exercise. I'm going to duplicate his example using Sencha Touch.

Sencha Touch is a framework for building mobile applications using HTML5, CSS3, and Javascript.  This example uses the Developer Preview of Sencha Touch 2.0.

Here's what our project looks like.

A Sencha Touch application is loaded from an HTML page. The HTML loads CSS and Javascript. Sencha Touch applications typically build the UI using Javascript so the index.html is very simple.

The touch/ directory contains the Sencha Javascript and CSS from the SDK.  Since this is an example, all if the application code is in one file, app.js. Larger applications will break the code up into more files and directories.

Sencha Application

Sencha Touch has a global namespace, Ext, that holds the framework and application code. The Ext.application helper function takes a configuration object where we define our application.

Ext.application({
    launch: function() {
        // define our app here
    }
});

Model

We extend Ext.data.Model to define a Model object describing our data. The data is supplied by the server in a JSON file, exercise.json. The fields in the model, match the properties in the JSON file.

Ext.define("Activity", {
    extend: "Ext.data.Model",
    fields: [
        {name: 'id', type: 'int'},
        {name: 'date', type: 'date'},    
        {name: 'type', type: 'string'},    
        {name: 'distance', type: 'string'},    
        {name: 'minutes', type: 'int'},  
        {name: 'comments', type: 'string'}
    ]
});
/* exercise.json */
[
    {
        "id":3,
        "date": "12/10/2011",
        "type": "Walk",
        "distance": "2.5 miles",
        "comments": "Shouldn't have taken the dog",
        "minutes": 45
    },
    ...
]  

Store

A Store is used to load and manage data. The Activity model we created is associated with the store. We define a proxy to load the JSON data from a URL. Finally, we tell the store to automatically load the data when the store is created.

var store = Ext.create('Ext.data.Store', {
    storeId: "activityStore",
    model: "Activity",
    proxy: {
        type: 'ajax',
        url: 'exercise.json'
    },
    autoLoad: true
});

Once the store is created, we can use the Javascript console to interact with the data.

User Interface

Now we need to display the data. A NavigationView shows a navigation bar and holds the List component. The list is defined in the code using the object literal syntax. The data store we created is assigned to the list so it can display the records. The itemTmp property defines how to display each item of data. This can be a template or a string.

var view = Ext.create("Ext.NavigationView", {
    fullscreen: true,
    items: [
        {
            xtype: 'list',
            title: 'Activities',
            itemTpl: '{date} - {type}',
            store: store
        }
    ]
});
Unformatted Date

This works but the date doesn't display very well. A format can be added to the template definition. {date} in the itemTpl becomes {date:date("m/d/Y")}.

var view = Ext.create("Ext.NavigationView", {
    fullscreen: true,
    items: [
        {
            xtype: 'list',
            title: 'Activities',
            itemTpl: '{date:date("m/d/Y")} - {type}',
            store: store
        }
    ]
});
Formatted Date

Adding New Data

Let's add a button to the header to add a new row of data.

view.getNavigationBar().add({
    xtype: 'button',
    text: 'Add',
    align: 'right',
    handler: addNewRow
});

The handler property of the button defines the function that is called whenever the button is pressed. Eventually we need a form so the user can add new data. For now, let's define a function addNewRow that will create a new row and add it to the store.

var addNewRow = function() {
    // future versions should display a form for adding a record
    var fakeRecord = Ext.create('Activity', {
        date: new Date(), 
        type: 'Walk', 
        distance: '2 miles', 
        minutes: 28, 
        comments: 'Auto generated record.'
    });

    store.add(fakeRecord);
};

Now when the button is pressed a new row appears in the UI.  The store notifies the list, so rows appear as records are added to the store.

Add Button

New Row

That should be enough to get you started. In future posts we'll look at features like sorting, detail views, navigation, and forms. The source code is available on github.

Tuesday, January 17, 2012

Spring 3.1 - Environment Profiles

Spring 3.1 Environment Profiles

Profiles

Spring 3.1 now includes support for the long awaited environment aware feature called profiles. Now we can activate profiles in our application, which allows us to define beans by deployment regions, such as "dev", "qa", "production", "cloud", etc.

We also can use this feature for other purposes: defining profiles for performance testing scenarios such as "cached" or "lazyload".

Essential Tokens

Spring profiles are enabled using the case insensitive tokens spring.profiles.active or spring_profiles_active.

This token can be set as:
  • an Environment Variable
  • a JVM Property
  • Web Parameter
  • Programmatic
Spring also looks for the token, spring.profiles.default, which can be used to set the default profile(s) if none are specified with spring.profiles.active.

Grouping Beans by Profile

Spring 3.1 provides nested bean definitions, providing the ability to define beans for various environments:
<beans profiles="dev,qa">
  <bean id="dataSource" class="..."/>
  <bean id="messagingProvider" class="..."/>
</beans>
Nested <beans> must appear last in the file.
Beans that are used in all profiles are declared in the outer <beans> as we always have, such as Service classes.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="businessService"
       class="com.c...s.springthreeone.business.SimpleBusinessServiceImpl"/>

    <beans profile="dev,qa">
        <bean id="constructorBean"
          class="com.chariotsolutions.springthreeone.SimpleBean"
              c:myString="Constructor Set"/>

        <bean id="setterBean"
          class="com.chariotsolutions.springthreeone.SimpleBean">
            <property name="myString" value="Setter Set"/>
        </bean>
    </beans>

    <beans profile="prod">
        <bean id="setterBean"
          class="com.chariotsolutions.springthreeone.SimpleBean">
            <property name="myString" value="Setter Set - in Production YO!"/>
        </bean>
    </beans>
</beans>

If we put a single <bean> declaration at below any nested <beans> tags we will get the exception org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'bean'.

Multiple beans can now share the same XML "id"
In a typical scenario, we would want the DataSource bean to be called dataSource in both all profiles. Spring now allow us to create multiple beans within an XML file with the same ID providing they are defined in different <beans> sets. In other words, ID uniqueness is only enforced within each <beans> set.

Automatic Profile Discovery (Programmatic)

We can configure a class to set our profile(s) during application startup by implementing the appropriate interface. For example, we may configure an application to set different profiles based on where the application is deployed - in CloudFoundry or running as a local web application. In the web.xml file we can include an Servlet context parameter, contextInitializerClasses, to bootstrap this class:
<context-param>
  <param-name>contextInitializerClasses</param-name>
  <param-value>com.chariotsolutions.springthreeone.services.CloudApplicationContextInitializer</param-value>
</context-param>

The Initializer class
package com.chariotsolutions.springthreeone.services;

import org.cloudfoundry.runtime.env.CloudEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;

public class CloudApplicationContextInitializer implements
  ApplicationContextInitializer<ConfigurableApplicationContext> {
    
  private static final Logger logger = LoggerFactory
    .getLogger(CloudApplicationContextInitializer.class);

  @Override
  public void initialize(ConfigurableApplicationContext applicationContext) {
    CloudEnvironment env = new CloudEnvironment();
    if (env.getInstanceInfo() != null) {
      logger.info("Application running in cloud. API '{}'",
        env.getCloudApiUri());
      applicationContext.getEnvironment().setActiveProfiles("cloud");
      applicationContext.refresh();
    } else {
      logger.info("Application running local");
      applicationContext.getEnvironment().setActiveProfiles("dev");
    }
  }
}

Annotation Support for JavaConfig

If we are are using JavaConfig to define our beans, Spring 3.1 includes the @Profile annotation for enabling bean config files by profile(s).

package com.chariotsolutions.springthreeone.configuration;

import com.chariotsolutions.springthreeone.SimpleBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
@Profile("dev")
public class AppConfig {
  @Bean
  public SimpleBean simpleBean() {
    SimpleBean simpleBean = new SimpleBean();
    simpleBean.setMyString("Ripped Pants");
    return simpleBean;
  }
}

Testing with XML Configuration

With XML configuration we can simply add the annotation @ActiveProfiles to the JUnit test class. To include multiple profiles, use the format @ActiveProfiles(profiles = {"dev", "prod"})
package com.chariotsolutions.springthreeone;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@ActiveProfiles(profiles = "dev")
public class DevBeansTest {

  @Autowired
  ApplicationContext applicationContext;

  @Test
  public void testDevBeans() {
    SimpleBean simpleBean = 
      applicationContext.getBean("constructorBean", SimpleBean.class);
    assertNotNull(simpleBean);
  }

  @Test(expected = NoSuchBeanDefinitionException.class)
  public void testProdBean() {
    SimpleBean prodBean = applicationContext.getBean("prodBean", SimpleBean.class);
    assertNull(prodBean);
  }
}

Testing with JavaConfig

JavaConfig allows us to configure Spring with or without XML configuration. If we want to test beans that are defined in a Configuration class we configure our test with the loader and classes arguments of the @ContextConfiguration annotation.

package com.chariotsolutions.springthreeone.configuration;

import com.chariotsolutions.springthreeone.SimpleBean;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;

import static org.junit.Assert.assertNotNull;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles(profiles = "dev")
public class BeanConfigTest {

  @Autowired
  SimpleBean simpleBean;

  @Test
  public void testBeanAvailablity() {
    assertNotNull(simpleBean);
  }
}

Declarative Configuration in WEB.XML

If we desire to set the configuration in WEB.XML, this can be done with parameters on ContextLoaderListener.

Application Context
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
<context-param>
  <param-name>spring.profiles.active</param-name>
  <param-value>DOUBLEUPMINT</param-value>
</context-param>
Log Results

DEBUG PropertySourcesPropertyResolver - Found key 'spring.profiles.active' in [servletContextInitParams] with type [String] and value 'DOUBLEUPMINT'


Environment Variable/JVM Parameter
Setting an environment variable can be done with either spring_profiles_default or spring_profiles_active. In Unix/Mac it would be export SPRING_PROFILES_DEFAULT=DEVELOPMENT for my local system.

We can also use the JVM "-D" parameter which also works with Maven when using Tomcat or Jetty plugins.

Note: Remember the tokens are NOT case sensitive and can use periods or underscores as separators. For Unix systems, you need to use the underscore, as above.

Logging of system level properties DEBUG PropertySourcesPropertyResolver - Found key 'spring.profiles.default' in [systemProperties] with type [String] and value 'dev,default'

Summary

Now we are equipped to activate various Spring bean sets, based on profiles we define. We can use  traditional, XML based configuration, or the features added to support JavaConfig originally introduced in Spring 3.0.

Thursday, January 12, 2012

Philly ETE - Speaker Interview, Ken Rimple co-author of Spring Roo in Action

For our first look at what’s in store for ETE 2012, we talk with our own Ken Rimple, co-author of Spring Roo in Action. Ken is Director of Education Services and is a regular co-host of Chariot’s TechCast – our monthly podcast series focusing on development and training in Spring, Rails, Scala, Hibernate, Maven and other emerging technologies in the field

Q: After last year’s tremendous following and turnout for ETE, people are really expecting this year to raise the bar. Tell us a bit about what you’ll be discussing at this year’s ETE and what people can expect from your presentation in particular.

Ken: I’ll be talking about the subject I actually wrote a book around – Spring Roo. It’s a software tool that helps people develop applications in Spring, which is a very popular Java application development platform. I’m going to talk about building Roo add-ons, which is kind of extending the features of the platform with ones that don’t exist today.

Q: So it’s all about building new features from scratch?

Ken: Exactly! For example, there are add-ons that configure different web frameworks, like Vaadin and JSF. At one point, there was an add-on for programming Flex applications [ed. note: this is currently non-functional], which are Flash-based frontends, and add-ons for other integrations to other systems.
I wrote two for the book: One to install JQuery, which is a Javascript framework, and another one that installs CoffeeScript, which is a language simplifying JavaScript. So, in my talk I’m going to go through how to write add-ons, because it’s such a new community. And I really want to boost the community’s presence so that people can start contributing to the framework.