Sunday, May 1, 2011


NoJSF

NoJSF, it is a basic project which aims to be the base for projects that do NOT want to use any obtrusive frontend frameworks like JSF.
This variant of the project is based on the IoC pattern provided by Spring.
The project uses RESTeasy for the REST layer and Spring 3 to provide injection between layers.
So, if all you want is just html, js, css and a restful backend... you found what you were looking for.

This is how it works:
  • the user acts on the client sending an ajax request;
  • the REST controller (aka restler) intercepts the request and delegates it to the business layer;
  • the effective business component does the hard work and, if necessary, asks the persistence layer to query or persist entities/documents;
  • the persistence component goes directly to the database through the appropriate and provided driver.
Layers chain terminates with a HTTP code, hopefully a 200 or 201 code! ;)

You can download it from github.

Let's look inside of it.

The Client side
Two things that probably I didn't tell you: one is that I am a fan of Single Page Website (also SPW) approach, second I'm not good with design, graphics and all.
Ok, now we can start.
First of all you will need an "index.html" as your root for your site; in this HTML file you will declare all your main resources such as JavaScript files, style sheet files and all the other things you might need.

Anyway here is my "index.html":
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
        <title>NoJSF - foogaro.com</title>
    </head>
    <body>
        <div id="header">
            <div id="menu">
                <ul>
                    <li><a href="#home">Home</a></li>
                    <li><a href="#users">Users</a></li>
                </ul>
            </div>
        </div>
       
        <div id="content">
        </div>
       
        <div id="footer">
        </div>
    </body>
    <link type="text/css" href="css/ui-lightness/jquery-ui-1.8.9.custom.css" rel="stylesheet" />
    <script type="text/javascript" src="js/jquery-1.5.min.js"></script>
    <script type="text/javascript" src="js/jquery-ui-1.8.9.custom.min.js"></script>
    <script src="http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
    <script type="text/javascript" src="js/nojsf.js"></script>
    <script type="text/javascript">
       
        $(document).ready(function () {
            f_handleMenu();
        });
        function f_handleMenu() {
            $("a[href=#home]").click(function () {
                $("#content").empty();
                return false;
            });
            $("a[href=#users]").click(function () {
                f_loadReource("fragment/user/main.html", "js/user/main.js", $("#content"), "f_user");
                return false;
            });
        }
    </script>
</html>
There is not much to explain in the above code except for one thing: the "href".

As you have noticed I valued user anchor "href" attibute with "#user", while I could have used "#" and setted the "id" attribute and handle the element by id, but.... as we are making a SPW we want our site to be indexed by search engines. Doing so the crawler wont skip our anchors from semantic analyses giving the page a better score (in this case the word "user" wont help us much).

In the javascript code we are loading two files: "fragment/user/main.html" and "js/user/main.js".
These two files are needed to update the DOM and include a JavaScript source respectively.

main.html
<div id="mainUsers">
    <h2>Show users</h2>
    <img src="images/icons/group.png" alt="Show users" title="Show users" id="imgShowUsers" />
    <img src="images/icons/user_add.png" alt="Add user" title="Add user" id="imgAddUser" />
    <div>
        <table >
            <thead>
                <tr>
                    <th>First name</th>
                    <th>Last name</th>
                </tr>
            </thead>
            <tbody id="tblUsers" ></tbody>
            <tfoot />
        </table>
    </div>
</div>

main.js
function f_user() {
    f_handleImgAddUser();
    f_showList();
}

function f_handleImgAddUser() {
    $("#imgAddUser").click(function () {
        f_addUser();
    });
}

function f_showList() {
    $("#tblUsers tbody").empty();
    $.ajax({
        url:"rest/users",
        type: "GET",
        dataType: "json",
        success: function (backData, status, xhr) {
            f_loadUserListTpl(backData.response.userDTO);
        },
        error: function (xhr, status, ex) {
        },
        complete: function (xhr, status) {
        }
    });
}

function f_loadUserListTpl(dataList) {
    $.ajax({
        url: '/web/template/user/userList.tpl',
        data: '',
        dataType: 'text',
        success: function (templateText) {
            $.tmpl(templateText, dataList).appendTo("#tblUsers");
        }
    });
}

function f_addUser() {
    $("#userDialog").remove();
    $("#mainUsers").append("<div id=\"userDialog\"></div>");
    $("#userDialog").dialog({
        bgiframe: true,
        autoOpen: false,
        height: 200,
        width: 550,
        modal: true,
        resizable: false,
        draggable: true,
        closeOnEscape: true,
        title: "Add new user",
        zIndex: 2000,
        close: function() {
            $("#userDialog").remove();
        }
    });
    $("#userDialog").load("/web/fragment/user/edit.html");
    $("#userDialog").dialog("open");
}

function f_saveUser() {
    $.ajax({
        url: "/web/rest/users/user",
        type: "POST",
        dataType: "json",
        contentType: 'application/x-www-form-urlencoded; charset=iso-8859-1;',
        data: $("#userForm").serialize(),
        success: function (backData, status, xhr) {
            $("#userDialog").dialog("close");
            f_showUsers();
        },
        error: function (xhr, status, ex) {
        },
        complete: function (xhr, status) {
        }
    });
}

function f_viewUser(userId) {
    $("#userDialog").remove();
    $("#mainUsers").append("<div id=\"userDialog\"></div>");
    $("#userDialog").dialog({
        bgiframe: true,
        autoOpen: false,
        height: 200,
        width: 550,
        modal: true,
        resizable: false,
        draggable: true,
        closeOnEscape: true,
        title: "User information",
        zIndex: 2000,
        close: function() {
            $("#userDialog").remove();
        }
    });
    $.ajax({
        url:"/web/rest/users/" + userId,
        type: "GET",
        success: function (backData, status, xhr) {
            $("#userDialog").load("/web/template/user/view.tpl", function(template) {
                $.tmpl(template, backData).appendTo(this);
            });
        },
        error: function (xhr, status, ex) {
        },
        complete: function (xhr, status) {
        }
    });
    $("#userDialog").dialog("open");
}

function f_updateUser() {
    $.ajax({
        url: "/web/rest/users/user",
        type: "PUT",
        dataType: "json",
        contentType: 'application/x-www-form-urlencoded; charset=iso-8859-1;',
        data: $("#userForm").serialize(),
        success: function (backData, status, xhr) {
            $("#userDialog").dialog("close");
            f_showUsers();
        },
        error: function (xhr, status, ex) {
        },
        complete: function (xhr, status) {
        }
    });
}

function f_deleteUser(userId) {
    $.ajax({
        url: "/web/rest/users/" + userId,
        type: "DELETE",
        dataType: "json",
        contentType: 'application/x-www-form-urlencoded; charset=iso-8859-1;',
        success: function (backData, status, xhr) {
        },
        error: function (xhr, status, ex) {
        },
        complete: function (xhr, status) {
            f_showUsers();
        }
    });
}

The interesting part here is in these two methods: "f_showList()" and "f_loadUserListTpl(dataList)".

The first one is responsible to call the rest service to get the user list from the backend and then giving it to the second method.
The latter is doing probably something new for some people: it is loading an external resource "/web/template/user/userList.tpl" which is actually a HTML template, as follow:

<tr>
    <td>${firstName}</td>
    <td>${lastName}</td>
    <td>
        <img id="${identifier}" class="userViewIcon" src="images/icons/user_go.png" alt="View" title="View" />
        <img id="${identifier}" class="userEditIcon" src="images/icons/user_edit.png" alt="Edit" title="Edit" />
        <img id="${identifier}" class="userDeleteIcon" src="images/icons/user_delete.png" alt="Delete" title="Delete" />
    </td>
</tr>

Here the jQuery Template engine will loop through the "dataList" which contains the users, evaluating the above expression in the HTML just like JSTL would do. That is nice, isn't it? :D

Without going through all the application (which actually it doesn't go any further :P) what is all about?
First of all an assumption: if you are a programmer, then you like coding, so you like doing things your way, and if you are here is cause you don't want to use the JSF trap anymore.

Now if you download the project from GitHub, compile it and deploy it and... and ... and if you open firebug or the Chrome developer tool, you will notice that most of the resources are cached and what goes back and forth from the server are just JSON requests, which means a lot's of bytes, kilobytes saved... now calculate this for 1 user, 10th users, 100th users... 1000th users.

How much bandwidth will you save?
How much control will you earn?

;)

Ciao,
Foogaro

No comments:

Post a Comment