Sunday 15 July 2012

Three Dynamic dropdowns in LIFERAY

1. Create a new liferay project (e.g named.combobox-portlet).
(http://michi-path.blogspot.in/2012/03/create-new-portlet-in-liferay-ide.html)
2. create service.xml (http://michi-path.blogspot.in/2012/03/using-service-builder-very-basic-to.html)
3. Add the following code to your service.xml



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.0.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_0_0.dtd">
<service-builder package-path="me.pd">
<author>priyanka.dhingra</author>
<namespace>COMBOBOX</namespace>


<entity name="Continent" local-service="true" remote-service="true">
<column name="continentCode" type="String" primary="true" />
<column name="continentName" type="String" />
</entity>


<entity name="Country" local-service="true" remote-service="true">
<column name="countryCode" type="String" primary="true" />
<column name="continentCode" type="String" />
<column name="countryName" type="String" />
<column name="fullName" type="String" />


<finder return-type="Collection" name="ContinentCode">
<finder-column name="continentCode" />
</finder>
</entity>


<entity name="States" local-service="true" remote-service="true">
<column name="stateAbbrev" type="String" primary="true" />
<column name="stateName" type="String" />
<column name="countryCode" type="String" />

<finder return-type="Collection" name="CountryCode">
<finder-column name="countryCode" />
</finder>

</entity>


</service-builder>


4. Do ANT build_service and then ANT deploy

5. Populate your tables running the queries from the following:-
http://michi-path.blogspot.in/2012/07/sql-queries-for-populating-continent.html
Note: Please edit according to the namespace_tablename. (e.g combobox_states)

6.  open your view.jsp (combobox-portlet\docroot\html\comboboxportlet\view.jsp). create a form


<form method="post" action="">
<select id="continent" name="continent"></select>
<select id="country" name="country"></select>
<select id="state" name="state"></select>
<input type="submit" value="save" /> 
</form>


7. Go to your portlet Class (e.g. ComboboxPortlet.java that extends MVCPortlet).
@override

public void doView(RenderRequest renderRequest,
RenderResponse renderResponse) throws IOException, PortletException {
List <Continent> continentList=null;
try {
continentList = ContinentLocalServiceUtil.findAll();


} catch (SystemException e) {

LOGGER.error("doView() - error listing keys", e);
}
renderRequest.setAttribute("continentList", continentList);
include("/html/comboboxportlet/view.jsp", renderRequest, renderResponse);


}
8. Open me.pd.service.ContinentLocalServiceUtil. Add the following method


public List<Continent> findAll() throws SystemException {
return continentPersistence.findAll();
}
The method we are using in doView has to be declared and defined in this class

9. update your view.jsp with the following code.

<%@page import="com.liferay.portal.kernel.util.Validator"%>
<%@page import="me.pd.model.Continent"%>
<%@page import="java.util.List"%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>


<portlet:defineObjects />
<%
List <Continent> continentListObj = (List <Continent>)request.getAttribute("continentList");
%>
<form method="post" action="">
<select id="continent" name="continent">
<%if(Validator.isNotNull(continentListObj)){
for(Continent con : continentListObj){%>
<option value="<%=con.getContinentCode()%>"><%=con.getContinentName()%></option>
<%}}%>
</select>
<select id="country" name="country"></select>
<select id="state" name="state"></select>
<input type="submit" value="save" /> 
</form>


Now, you can ANT build-service and deploy the code once and on your browser you'll be able to see the continent values. its great to see first step completed. Now, moving forward to the Country list.


we'll use jquery and ajax call with json feed to get data and serveResource method to fetch the values from the database.


Moving Forward........
10. Update your view,jsp with following code

<%@page import="com.liferay.portal.kernel.util.Validator"%>
<%@page import="me.pd.model.Continent"%>
<%@page import="java.util.List"%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>


<portlet:defineObjects />
<%
List <Continent> continentListObj = (List <Continent>)request.getAttribute("continentList");
%>
<portlet:resourceURL var="fetchValues" ></portlet:resourceURL>
<script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery.js"></script>
<script type="text/javascript" >


$(document).ready(function(){
$("#continent").change(function(){
var url="<%=fetchValues%>";
var continentCode=$("#continent").val(); 
jQuery.getJSON(url+"&continentCode=" +continentCode+"&action=countryValues" , function(data)  {
for(i=0;i<data.countryNameCode.length;i++){
countryNameCode = data.countryNameCode[i].split(":");
$("#country").append("<option value='"+ countryNameCode[0] +"'>"+countryNameCode[1]+"</option> " );
}
});
});
$("#country").change(function(){
var url="<%=fetchValues%>";
var  countryCode =$("#country").val(); 
jQuery.getJSON(url+"&countryCode=" +countryCode+"&action=stateValues" , function(data)  {
for(i=0;i<data.stateNameCode.length;i++){
stateNameCode = data.stateNameCode[i].split(":");
$("#state").append("<option value='"+ stateNameCode[0] +"'>"+stateNameCode[1]+"</option> " );
}
});
});
});
</script>
<form method="post" action="">
<select id="continent" name="continent">
<%if(Validator.isNotNull(continentListObj)){
for(Continent con : continentListObj){%>
<option value="<%=con.getContinentCode()%>"><%=con.getContinentName()%></option>
<%}}%>
</select>
<select id="country" name="country"></select>
<select id="state" name="state"></select>
<input type="submit" value="save" /> 
</form>
11. Add serveResource() to your portlet class.


@Override
public void serveResource(ResourceRequest resourceRequest,
ResourceResponse resourceResponse) throws IOException,
PortletException {
JSONObject  jsonFeed = JSONFactoryUtil.createJSONObject();
String action = ParamUtil.getString(resourceRequest, "action");

if (action.equalsIgnoreCase("countryValues")) {
String continentCode = ParamUtil.getString(resourceRequest, "continentCode");
JSONArray countryNameCode = JSONFactoryUtil.getJSONFactory()
.createJSONArray();

List<Country> countryList=null;
try {
countryList = CountryLocalServiceUtil
.getByContinentCode(continentCode);
for (Country countryObj : countryList) {
countryNameCode.put( countryObj.getCountryCode() + ":"
+ countryObj.getCountryName());
}
} catch (SystemException e) {
LOGGER.error("SystemException"+e);
}
jsonFeed.put("countryNameCode", countryNameCode );
}
if (action.equalsIgnoreCase("stateValues")) {
String countryCode = ParamUtil.getString(resourceRequest,"countryCode");
JSONArray statesNameCode = JSONFactoryUtil.getJSONFactory()
.createJSONArray();

List<States> statesList=null;
try {
statesList = StatesLocalServiceUtil
.getByCountryCode(countryCode);
for (States countryObj : statesList) {
statesNameCode.put( countryObj.getStateAbbrev()+ ":"
+ countryObj.getStateName());
}
} catch (SystemException e) {
LOGGER.error("SystemException"+e);
}
jsonFeed.put("stateNameCode", statesNameCode );
}
resourceResponse.setContentType("application/json");
resourceResponse.setCharacterEncoding("UTF-8");
resourceResponse.getWriter().write(jsonFeed.toString());
}
12. Open your CountryLocalServiceImpl class and add the method you used here i.e. CountryLocalServiceUtil.getByContinentCode(continentCode)

public List<Country> getByContinentCode(String continentCode)
throws SystemException {
return countryPersistence.findByContinentCode(continentCode);
}
 (Similar is to be done for the states)
StatesLocalServiceUtil.getByCountryCode(countryCode)
public List<States> getByCountryCode(String countryCode)
throws SystemException {
return statesPersistence.findByCountryCode(countryCode);
}


13. ANT build-service and deploy

Here it goes, Dependent combo-box is ready...It is always great to see these small functionalities working with just some simple logics...Enjoy!!!

7 comments:

  1. Nice Example & well explained ever..!!

    ReplyDelete
  2. Hi,

    Can we create web service to expose serveResource method? In the serveResource method I am returning a simple json file. This was one of the option I was trying to carry out my task. Other option was to try calling a serveResource method from a standalone java class and it did not work well. Please advice.

    Thanks!

    ReplyDelete
    Replies
    1. Hi Rav,

      The second option didn't work because you can't call the serveResource method explicitly. It comes in the portlet life cycle.

      Regarding first option, it is correct that you can use serveResource method for the webservice because ultimately we have to give and take the data to the view.

      Regards,
      Ajeet Singh

      Delete
    2. Hi Ajeet,

      can we call a serverResource() from a liferay portal scheduler? please let me know how to achieve this.

      Thanks,
      Ravi

      Delete
    3. Hey Ravi, with a very late reply,

      As per my knowledge it is not possible to call the serveResource method because it is not possible to get the resourceRequest and resourceResponse objet in the Java class.

      Regards,
      Ajeet Singh

      Delete
    4. good example

      Delete
  3. Hi,
    Nice example but it is working only for first drop down.Can you check and modify how to achieve this.

    Regards,
    karthik

    ReplyDelete