Wednesday, November 04, 2009

How to use Ajax with CakePHP using jQuery

With this post, I am going to explain how to make AJAX calls using jQuery in cake php. Effective posting related to this topic is very rare. Cake php is a PHP framework designed using MVC pattern. You have model, controller and view. In the cake php layout page, normally you add the fallowing code somewhere in the page.

<?php echo $content_for_layout; ?>

After executing the controller funcitons, the relavent view *.ctp file will be rendered where you have placed above code inside the layout page. If you consider blog example, after executing PostsController's view() action, view.ctp file will be rendered. This is the default behaviour of cake php. But, you may need more control when rendering view files in cake php than the above default behaviour. Sometime, you may need to load the relavent view file's contents into a spacific DIV tag in currently rendered page without refreshing the page. In this case, you may need AJAX support with cake php. I will explain how to do this with cake php. And also, I am going to explain how to get JSON response from cake php's controller function and update some contents of the page with javascript function. I am going to use jQuery support for this functionality. To have jQuery support for my cake php project, I include the fallowing in my laytout page.(This layout may be default.ctp or your own another layout page).

<?php echo $javascript->link('jquery-1.3.2');?>

The above line links 'jquery-1.3.2.js' file to my layout page.
Now, you we have jQuery support for our business. But not all. jQuery has sevaral UI supports. Next, we will start the real task. I am going to create the client side javascript function that makes AJAX request to cake php controller's action.

I am going to create Post data view function(Think about cake php blog example) with ajax support. Post title will be listed as links, and when user clicks on link, the relavent post information will display in a DIV tag somewhere in the same page.

<?php foreach($posts as $post): ?>
<a href="#" onclick="viewPost(<?php echo $post['Post']['id'];?>)">
<?php echo $post['Post']['title'];?><br/>
</a>
<?php endforeach; ?>

$posts is a variable defined in a controller before rendering this page.
The above code shows how to create list of links using cake php. When click on each link, viewPost() function will be called and relavent post id will be passed into that function. The viewPost() function will take the responsibilities to make the AJAX request to cake php action and subsequently update the page contents.

The javascript code for the viewPost() function as fallows.
function viewPost(postId){
var data = "id="+ postId;
$.ajax({
type: "post", // Request method: post, get
url: "/cake/index.php/posts/view/", // URL to request
data: data, // post data
success: function(response) {
document.getElementById("post-view").innerHTML = response;
},
error:function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
return false;
}
$.ajax() is a jquery function that suports to make AJAX request to the server. You have to specify the path to your cake php action for url option. "success" and "error" are tow callback functions suporting by $.ajax() function. "success" callback function will be fired with the succesfull response from the server. If some error occured at the server "error" callback function will be fired. The code for updating client side with successfull response should take place inside the "success" callback function.

The "success" callback function will be passed the contents of the response page if the server response type is 'text/html'. In this case, response will be the contents of /app/views/posts/view.ctp file. In this case the server response type will be 'text/html'. "success" callback function will receive the reponse data and set as the inner html of the DIV tag with id "post-view". You can do what ever with the response data. Keep in mind, this response data is just text or html. Using data option, we can pass some data into the server as http post data.
Next, we will explore the server side code for the view action in cake php controller. The code as fallows.
function view($id = null) {
Configure::write('debug', 0);
if($id == null){
$id = $_POST["id"];
}
$this->pageTitle = 'View Post';
if($this->RequestHandler->isAjax()) {
$post = $this->Post->findById($id);
$this->set('post', $post);
$this->layout = 'ajax';
$this->render('view');
} else {
$post = $this->Post->findById($id);
$this->set('post', $post);
$this->layout = 'post_layout';
$this->render('view');
}
}



This function supports for both behaviours. The cake php default behaviours and also this function responses for AJAX request. Function checks the request type for ajax or normal request and then proceed based on that. Cake php's RequestHandler component helps to check the request type.If the request is AJAX request, the layout is being changed to 'ajax' to support for a ajax response.
When the request is ajax, /app/views/posts/view.ctp file's content will be passed to the client as normal text/html. Then from the client side, "success" callback function will get this response and update the client page. If the request is normal request,/app/views/posts/view.ctp file will render in default cake php's behaviour, where you have placed <?php echo $content_for_layout; ?>.

Next we will consider,how to get the response as JSON object from cake php's controller action. Some time you may need to get JSON object as the response from the php controller's action. With slight changes to above code, we can have a JSON response. The javascript function making the AJAX request as fallows.

function viewPost(postId){
var data = "id="+ postId;
$.ajax({
type: "post", // Request method: post, get
url: "/cake/index.php/posts/view/", // URL to request
data: data, // Form variables
dataType: "json", // Expected response type
success: function(response) {
var sb = [];
sb[sb.length] = "Title :" + response.data.title + "";
sb[sb.length] = "Body :" + response.data.body;
document.getElementById("post-view").innerHTML = sb.join("");
},
error:function (XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
}
});
return false;
}
Note that, I have specified the dataType option as "json". This is important, if we want to get JSON response from the server. JSON object from the server will automatically converted into a javascript object. Then we can traverse the object and update the page with some dynamic html.

Next we will see the server side code, which makes the json response.
function view($id = null) {     
if($id == null){
$id = $_POST["id"];
}
$post = $this->Post->findById($id);
$this->set('post', $post);
$this->pageTitle = 'View Post';
if($this->RequestHandler->isAjax()) {
Configure::write('debug', 0);
$post = $this->Post->findById($id);
$this->layout = 'ajax';
$this->autoLayout = false;
$this->autoRender = false;
$this->header('Content-Type: application/json');

$data = array();
$result = array();
$data = array('title' => $post['Post']['title'],
'body' => $post['Post']['body']);
$result['status'] = "success";
$result['data'] = $data;
echo json_encode($result);
return;

} else {
$this->layout = 'post_layout';
$this->render('view');
}
}

This function also supports for both the behaviours, ie: cake php's default behaviour which renders /app/views/posts/view.ctp file after executing the action method. If the request is ajax request, it creates a JSON object and send it back to the browser as the response. PHP json_encode() function makes PHP array into JSON object. Note that, I have set change the layout to 'ajax' and autoRender to false. Content type should be set to 'application/json' for JSON response. Like this you can response any of data as JSON objects back to the browser. In the client side, use some javascript to create the dynamic html from these data. You can do nice ajax with JSON. Personally, I like JSON. I hope you too. Go ahead guys ...

Sunday, February 08, 2009

Standard injection into Servlet/Filter - Spring

I understood that spring dependency injection for beans which
implements standard servlet 2.3 Filter innterface, declared in applicationContext.xml is not working in usual manner. For this one, I had to involve DelegatingFilterProxy class which is delegating to a Spring-managed bean that implements the Filter interface. I have a filter class called 'PDAFilter' which implements standard Filter interface and have a bean entry in the applicationContext.xml file fallows.


<bean id="pdaFilter" class="com.axiohelix.pda.main.PDAFilter">
<property name="hibernateUtil" ref="hibernateUtil" />
</bean>
PDAFilter implements Filter.init(), Filter.doFilter() and Filter.destroy() methods. While creating PDAFilter instance,I needed to inject instance of HibernateUtil class into it. Usual injection always results in null HibernateUtil object. Spring's DelegatingFilterProxy class can be used for this kind of injection. We have to add the fallowing entry into web.xml file.

<filter>
<filter-name>pdaFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>

All calls to the filter proxy will then be delegated to that bean in the Spring context, which is required to implement the standard Servlet 2.3 Filter interface. The lifecycle methods defined by Filter interface will not be invoked by this kind of implementation. The servlet api expects in from spring context. But spring context is not handling licecyle methods. To force servlet api to invoke the licecyle methods, I have set 'targetFilterLifecyle' to 'true'.

Labels:

Monday, December 22, 2008

Creating custom result type - struts 2

Creating a custom result type comes to the field when your are trying
to develop a Ajax base development with struts 2. By default, struts 2 uses RequestDispatcher originated from servlets API, as the default result type. That is, after returning the control string from your action, this default result type will show the way for final page rendering.

Suppose you want to have some data response to the client, in stead of despatching to a JSP page after returning from your action class. Ajax request to the server often needs this kind of data.
Here, I am going to explain you, how to make a Ajax request to a struts 2 action class and how to use a custom result type to send some data back to the client, instead of rendering a another JSP page as usual. I am not going to give you a detailed description on Ajax related things. My major concern is how to create a custom result type and use it with Ajax development.

I have a list of User's names in a struts select box component and onchange event will make a request to a action class to get full details about the user to the client and update some segment of current rendered page of the browser as usual Ajax application does. I am passing userId as query string parameter to the action class.
Here is the javascript code the responsible to make the request to the server. I have encpsulated all related functions needed to make a request to the server, into one javascript object.


var PDA = {};
PDA.common = {};

PDA.common.ContentLoader = function(httpMethod,url,params,callBack){
this.init(httpMethod,url,params,callBack);
}
PDA.common.ContentLoader.prototype = {
READY_STATE_UNINITIALIZED :0,
READY_STATE_LOADING :1,
READY_STATE_LOADED :2,
READY_STATE_INTERACTIVE :3,
READY_STATE_COMPLETE :4,
httpMethod : null,
url : null,
params : null,
callBack : null,
error : null,
request : null,

defaultError:function(){
alert("error fetching data!"
+"\n\nreadyState:"+this.request.readyState
+"\nstatus: "+this.request.status
+"\nheaders: "+this.request.getAllResponseHeaders());
},

getXMLHttpRequest: function(){
if(window.XMLHttpRequest){
this.request = new XMLHttpRequest();
}else if(typeof ActiveXObject != "undefined"){
try {
this.request = new ActiveXObject("Msxml2.XMLHTTP");
}catch(e) {
try{
this.request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
this.request = null;
}
}
}
},

onReadyState: function(){
var req = this.request;
var ready = req.readyState;
if (ready == this.READY_STATE_COMPLETE){
var httpStatus = req.status;
if (httpStatus == 200 || httpStatus == 0){
this.callBack.call(this);
}else{
this.error.call(this);
}
}
},

sendRequest: function(){
this.getXMLHttpRequest();
if(this.request){
try{
var loader = this;
this.request.onreadystatechange = function(){
loader.onReadyState.call(loader);
}
this.request.open(this.httpMethod,this.url,true);
this.request.send(this.params);
}catch(e){
this.error.call(this);
}
}else{
this.error.call(this);
}
},

init: function(httpMethod,url,params,callBack){
this.httpMethod = httpMethod;
this.url = url;
this.params = params;
this.callBack = callBack;
this.error = this.defaultError;
this.sendRequest();
}
};
The above object almost contains all the function to make a Ajax request to the server. I will simply explain the constructor parameters need to initiate object from this class.
  • httpMethod : eidther "POST" or "GET"
  • url : request url (in my case, it is action name)
  • params : url parameter needed to pass to the server
  • callBack : javascrip function name which handles the response data and update the client browser content
If you want to make a request to the server, you can simply initiate an object from this class. Easy isn't it? Now the time to call the action.
My user list select box' onchange event will fire the fallowing javascrip function which get the user id form the select box and make a request to the "ViewUsers" class.

function fetchUser(){
var selectBox = document.getElementById('userId');
var selectedIndex = selectBox.selectedIndex;
var selectedValue = selectBox.options[selectedIndex].value;
new PDA.common.ContentLoader("POST",
"ViewUsers.action?userId=" + selectedValue,
null,
showUsers);
}
"showUsers" function is the callback function in my case which is responsible for client side updates. Let's see the source code for ViewUsers action class next.

package com.axiohelix.pda.actions;
import org.hibernate.Transaction;
import com.opensymphony.xwork2.ActionSupport;

public class ViewUsers extends ActionSupport{
private static final long serialVersionUID = 1L;
private int userId;
private User user;
private Object jsonModel;

public String execute(){
String result = null;
try{
//do some thing to get the user object from userId
setUserType(user);
setJsonModel(user);
result = SUCCESS;
}catch(Exception e){
e.printStackTrace();
result = ERROR;
}finally{
}
return result;
}

public int getUserId() {
return userId;
}

public void setUserId(int userId) {
this.userId = userId;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public Object getJsonModel() {
return jsonModel;
}

public void setJsonModel(Object jsonModel) {
this.jsonModel = jsonModel;
}
}

As you can see, I have created User object from the userId and exposed it into the ValueStack under the property of "jsonModel". After returning from this action class, it should hit our custom result type and should send the user informations back to the client. Here, I am going to use JSON which provides a succinct text-based serialization of data objects. JSON can be a powerfully succinct and lightweight means of communication between a web application server and an Ajax client.
Next, we will see the core of the post.We are going to create the custom result which sends JSON data string back to the client. The only requirement needs to create a struts2 custom result is, implementing Result interface of struts 2. Here is the source code to create a custom result type and serialize the object in to JSON.


package com.axiohelix.pda.jsonresult;

import java.io.PrintWriter;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.Result;
import com.opensymphony.xwork2.util.ValueStack;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;

public class JSONResult implements Result{
private static final long serialVersionUID = 1L;
public static final String DEFAULT_PARAM = "classAlias";
String classAlias;

public String getClassAlias() {
return classAlias;
}
public void setClassAlias(String classAlias) {
this.classAlias = classAlias;
}
@Override
public void execute(ActionInvocation invocation) throws Exception {
ServletActionContext.getResponse().setContentType("text/plain");
ServletActionContext.getResponse().addHeader("Cache-Control", "no-cache");
PrintWriter responseStream = ServletActionContext.getResponse().getWriter();
ValueStack valueStack = invocation.getStack();
Object jsonModel = valueStack.findValue("jsonModel");
XStream xstream = new XStream(new JettisonMappedXmlDriver());
if ( classAlias == null ){
classAlias = "object";
}
xstream.alias(classAlias, jsonModel.getClass() );
responseStream.println(xstream.toXML( jsonModel ));
}
}

Here, I have used two open source libraries, XStream and Jettison. XStream helps to serialize java objects into XML format while Jettison helps to create JSON from the XML.As you can see,I am getting "jsonModel" property exposed into the ValueStack from our ViewUsers action calss,progrmmetically and serialize it into JSON.
Now the time to tell ViewUsers action class about our custom result type. We have to add fallowing entries into our declarative architecture, ie struts.xml file. To declare our custom result type, you have to add fallowing into struts.xml file.

<result-types>
<result-type name="customJSON" class="com.axiohelix.pda.jsonresult.JSONResult">
</result-type>
</result-types>
To use our custom result type, we need to add the fallowing to struts.xml file.
<action name="ViewUsers" class="com.axiohelix.pda.actions.ViewUsers">
<result type="customJSON">user</result>
</action&gt;
Here, you can see, I am passing in a parameter to the custom result type. It will be set to the "classAlias" field and finally, it will be the name of the root JSON object return to the client. Fallowing is the response JSON string representation of JSON

{"user":{"username":"Mary",
"password":"max",
"age":"28",
"firstName":"Mary",
"lastName":"Greene",
"receiveJunkMail":"false"}}

Labels:

Saturday, December 06, 2008

Handling custom error pages - struts 2

When you work with struts 2, you may need to handle exception and
forward it to a custom error pages as you wish. The exception interceptor of struts 2 will catch exception and map them to user-defined error pages by type. For example, suppose you want to map all java.lang.Exception to a custom error page defined by yourself, you have to involve the exception interceptor.
You can add the fallowing entry to your struts.xml file just after the package element.

<global-results>
<result name="error">/WEB-INF/jsp/errorPage.jsp
</result>
</global-results>
<global-exception-mappings>
<exception-mapping exception="java.lang.Exception"
result="error" />
</global-exception-mappings>

With this declaration, each java.lang.Exception exception will end up with your error page. Also you can display more details on error in your custom page.Before yielding control to the result, the exception interceptor will create an ExceptionHolder object and place it on top of the ValueStack. The Exception-Holder is a wrapper around an exception that exposes the stack trace and the exception as JavaBeans properties that you can access from a tag in your error page. The following snippet shows our error JSP page:

Exception Name: <s:property value="exception" />

What you did wrong:<s:property value="exceptionStack" />

We can use the
exception and exceptionStack property which are already exposed to ValueStack by ExceptionHolder object to get the details about the exception into the error page.

Labels:

Tuesday, December 02, 2008

Object backed-components – Struts 2

The automatic data transfer of Struts 2 needs only to provide
JavaBeans properties on actions, using the same names as the form
fields being submitted. Object backed – components of Struts 2 allows us to transfer data as objects. Suppose you want
to create a new user.

We have User class as fallows,

public class User{
private String name;
private String email;
// getter and setter methods
}

If you use Struts 2 default data transferring process, you must give the same field names as input JSP’s field’s name, must write JavaBeans properties with the same name in you action class and you have to create User object by yourself to create a new user. If you use, object – backed components of Struts 2 for data transferring, We don’t even have to create the User object that backs the property because the framework’s data transfer will handle this for us when it starts trying to move the data over. You can have more cleaner action class as fallows.

public String execute(){
createUser(user);
return SUCCESS;
}

private User user;
public void setUser(User user){
this.user = user;
}

public User getUser(){
return this.user;
}

You also need to do slight changes in your input JSP’s and the resulting JSP’s when addressing appropriate fields. For example, in the input JSP for creating a new user,

Similarly, when rendering the result,

Welcome <s:property value="user.name" />

The minor consequences are that we have to go a dot deeper when we reference our data from the JSP pages.