flexlogo.pngPreviously I have written an article about integrating flex with spring. In this article I explained the steps to take when creating a flex 3 application that calls the back end implemented using spring. To enable flex to do this, I used the blazeDS technology. The theory is not very hard, the biggest problems you’ll face are hibernate problems with lazy initialization etc. While creating an application using flex I was amazed there is no standard component for filtering data in a DataGrid. Since I am using such a component based on ajax technology I wanted to have it in flex as well. After a shot search I came across actually nothing. So I decided to create a solution for myself. I do want to stress it does not have the flexibility you usually require from a standard component.

Requirements

  • Input fields should be on top of each column.
  • Filter should be implemented on the back end.

Constraints

  • No drag able columns are supported

Read on to find out how we implemented these requirements into a custom component for flex3 using the spingframework at the back end.

Basic Architecture

For almost all my projects I start with a basic architecture. One module for business logic, this module contain the exposed services. The business services uses the integration module to access the database. The domain module contains JPA annotated configuration and finally there is a common module that contains objects accessible by al other modules. The flex module, which is actually a web project, uses the business module via blazeDS. Check the following image for an overview of the architecture.
CompBaseArchFlex.png

Ant and maven for the build

For all the modules we use maven to create a deployable unit. So we have jars for he business, integration, domain and common. We have a war for the webproject containing flex. The jars are all pretty standard maven projects. For flex we have some issues. First of all there is no handy maven plugin available that is easy to use with flex 3, at least not that I know of. I also could not find the necessary jars including poms in an online repository. Therefore we have to add the jars to our local repository. The important part of the web project follows.

        <!-- adobe dependencies -->
        <dependency>
            <groupId>backport-util-concurrent</groupId>
            <artifactId>backport-util-concurrent</artifactId>
            <version>3.1</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.3</version>
        </dependency>
        <dependency>
            <groupId>commons-httpclient</groupId>
            <artifactId>commons-httpclient</artifactId>
            <version>3.0.1</version>
        </dependency>
        <dependency>
            <groupId>concurrent</groupId>
            <artifactId>concurrent</artifactId>
            <version>1.3.4</version>
        </dependency>
        <dependency>
            <groupId>xalan</groupId>
            <artifactId>xalan</artifactId>
            <version>2.6.0</version>
        </dependency>
        <!-- created these dependencies myself from libraries provided -->
        <dependency>
            <groupId>adobe</groupId>
            <artifactId>cfgatewayadapter</artifactId>
            <version>3.0.0.544</version>
        </dependency>
        <dependency>
            <groupId>adobe</groupId>
            <artifactId>flex-messaging-common</artifactId>
            <version>3.0.0.544</version>
        </dependency>
        <dependency>
            <groupId>adobe</groupId>
            <artifactId>flex-messaging-core</artifactId>
            <version>3.0.0.544</version>
        </dependency>
        <dependency>
            <groupId>adobe</groupId>
            <artifactId>flex-messaging-opt</artifactId>
            <version>3.0.0.544</version>
        </dependency>
        <dependency>
            <groupId>adobe</groupId>
            <artifactId>flex-messaging-proxy</artifactId>
            <version>3.0.0.544</version>
        </dependency>
        <dependency>
            <groupId>adobe</groupId>
            <artifactId>flex-messaging-remoting</artifactId>
            <version>3.0.0.544</version>
        </dependency>

You can find the necessary jars in the blazeDS download. There is one thing I have not fixed yet, I still use ant to build the swf file from the flex resources. So I created a separate source folder for the flex resources and I added the ant scripts (just like in my previous post). For now I leave it to that, I guess I can use the ant scripts from within maven, but I’ll focus on the important stuff first :-). For the scripts you can check the previous post about flex. And of course you can check the sources of books-overview sample online. So be aware when trying the sample you need the flex sdk, the blazeDS download and do not forget to run the ant script at least once if you change something. During development I run the ant script while the web application is running. This makes development easier. Therefore there are two lines of code in the local.build.properties of which one is commented out. Line one copies the file to the directory of which tomcat pics up the sources, the second is the one that is added to version control and therefore packaged into the war file.

deploymentPath=../../../target/books-flex
#deploymentPath=../webapp

The search function using spring and hibernate

The following classes represent the domain model we use. Beware, we implement the domain model persistence with hibernate. It is not really advisable to create ManyToMany relationships. It might be better to use two ManyToOne relations. However for now we will do fine. There is a but. Searching for books based on author names becomes much harder. So maybe in the future I will introduce the class BookAuthor, this way you can assign properties to the relations ship as well. Think about Author type, like main author (is to be shown first in he list). Back to the sample, here is the domain model:
BooksDomain_25_3_2008.png
The Business interface BookManager contains a method that enables you to filter the books (or search for books)

List obtainFilteredBooks(BookSearchRequest searchRequest);

From this interface you can see there is a new object called BookSearchRequest, a Serializable object that is used to pass values for book title, book isbn and author name. Using this object we can perform a hibernate query that gets the results from the database.

public List<Book> loadByFilter(BookSearchRequest searchRequest) {
    String queryStr = "select b from Book as b where lower(title) like lower(:title) and isbn like :isbn ";
    Query query = getEntityManager().createQuery(queryStr)
            .setParameter("title", '%' + searchRequest.getBookTitle() + '%')
            .setParameter("isbn", '%' + searchRequest.getBookIsbn() + '%')
            .setMaxResults(maxRecords);
    return query.getResultList();
}

The file spring-business.xml in the books-business component contains the configuration of the spring application context. We use annotation to configure bean dependencies. I do not use @Component annotation. I prefer to specify the beans themselves in xml. I do want to have a good look at the spring Configuration project, but that is for a next blog item.

    <context:annotation-config/>

For transaction I also do not like to use annotations. I prefer to have it all in one place, it is a separate concern and therefore I want to configure it seperate (within xml). The code shows the pointcut that is used to wrap the txAdvice around all methods in classes ending with the name Manager in the business package. I do make a difference between methods beginning with obtain and the other methods. All methods beginning with obtain get the read-only context.

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="obtain*" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<aop:config>
    <aop:pointcut id="serviceOperation"
                  expression="execution(* nl.gridshore.samples.books.business.*Manager.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation"/>
</aop:config>

I want to make a last remark about the spring configuration. For simplicity I define a bean that inserts some books and authors. This bean uses storeBook method in the mentioned manager class.

<bean id="datastoreSetupBean" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject">
        <bean class="nl.gridshore.samples.books.business.helpers.PrefillDatastore" />
    </property>
    <property name="targetMethod" value="initializeDatastore"/>
</bean>

That is everything I want to tell about the server related components. Let’s move on to the web project and of course flex.

Show the remote object usage

If you want to learn more about the remoting using spring, please refer to my previous post about this topic. For this sample the following files are important:

  • Home.mxml – Contains the main panel, the menu (a few buttons) and an even handler for choosing the content to show like books or authors.
  • FilteredBooks.mxml – Most important part, contains the DataGrid component and the input boxes for the filter content. We are going into a detailed discussion on this component.
  • BookAuthorsRenderer.mxml – Special renderer object to present all authors of a book into one DataGrid field.
  • BookSearchRequest.as – Object which is the same as the one specified in Java. This make it easier to create an instance of the object to be passed to the remote service.

Now let’s have a look at FilteredBooks.mxmlx. We will start with the definition of the remote object. Before we can use a remote object, we need to configure it in the remoting-config.xml located in he WEB-INF/flex folder. To be able to have access to the spring bean called BookManager we add the configuration like this.

<destination id="bookManager">
    <properties>
        <factory>spring</factory>
        <source>bookManager</source>
    </properties>
</destination>

If you have read the post I mentioned before you by now should understand that we use a special spring factory class here to configure BlazeDS. Then within our mxml component we can define the remote object.

<mx:RemoteObject id="srv" destination="bookManager" fault="faultHandler(event)"/>

Within the same component I have created a function that creates a BookSearchRequest object and calls the remote object.

function doFilterBooks():void {
    var bookSearchRequest:BookSearchRequest = new BookSearchRequest();
    bookSearchRequest.bookTitle = titleFilter.text;
    bookSearchRequest.bookIsbn = isbnFilter.text;
    srv.obtainFilteredBooks(bookSearchRequest)
}

Within the function I assign two values to the BookSearchRequest object. I am talking about titleFilter.text and isbnFilter.text. These are two TextInput fields used by the user to enter the filter texts. Let’s have a look at the code for the form containing the TextIinput fields. I tried to have these fields in the header columns of the DataGrid itself, but this did not work for me.

<mx:VBox width="{myBooks.width}">
    <mx:Form defaultButton="{doFilterButton}" paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0">
        <mx:HBox width="100%">
            <mx:Spacer width="100%"/>
            <mx:Button id="doFilterButton" label="filter" click="doFilterBooks()"/>
            <mx:Button label="clear" click="clearFilter()"/>
        </mx:HBox>
        <mx:HBox width="100%" horizontalGap="0" borderStyle="solid" paddingTop="10" paddingBottom="10">
            <mx:Spacer width="10"/>
            <mx:TextInput id="titleFilter" width="{titleColumn.width-20}"/>
            <mx:Spacer width="20"/>
            <mx:TextInput id="isbnFilter" width="{isbnColumn.width-20}"/>
            <mx:Spacer width="20"/>
            <mx:Text id="authorFilter" width="{authorsColumn.width-20}" text=""/>
            <mx:Spacer width="10"/>
        </mx:HBox>
    </mx:Form>
</mx:VBox>

As you can see there are two horizontal boxes wrapped within a vertical box. They must both have the same width and the buttons filter and cancel must be positioned at the right. The biggest problem is to position the input boxes on top of the DataGrid columns. We use the actual columns to measure the width titleColumn.width. The width of the VBox is set to the width of the DataGrid width=”{myBooks.width}”. I did have a problem when not using fixed width for the DataGrid and the columns. I also used the defaultButton property of the Form element. This enables the user to push the enter button when done typing the filter text. So you do not need to use the mouse to click the button. Is very handy when working with the application. The following code block shows the actual DataGrid component.

<mx:DataGrid id="myBooks" dataProvider="{srv.obtainFilteredBooks.lastResult}" verticalScrollPolicy="on" height="100%">
    <mx:columns>
        <mx:DataGridColumn id="titleColumn" dataField="title" width="400" draggable="false"/>
        <mx:DataGridColumn id="isbnColumn" dataField="isbn"  width="200" draggable="false"/>
        <mx:DataGridColumn id="authorsColumn" sortable="false"  width="400" draggable="false">
            <mx:itemRenderer>
                <mx:Component>
                    <rend:BookAuthorsRenderer/>
                </mx:Component>
            </mx:itemRenderer>
        </mx:DataGridColumn>
    </mx:columns>
</mx:DataGrid>

As you can see in the code, the data provider is listening to the lastResult of the RemoteObject call. You can also see we used fixed widths for the columns.

That is about it, you now have a component that presents data in a grid and has fields that you can use to filter the content in the underlying data set using a remote component on the server side. It is not a very generic components that you can reuse, but you can reuse the technology.

Freddie made a good suggestion about adding a screenshot, so here it is.

Screenshotdatagrid_25_3_2008.png

Hope you liked this article, stay tuned for more articles about flex.

Tagged on:         

10 thoughts on “Creating a Flex 3 DataGrid component with backend filtering

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>