How to submit form using Ajax request in Liferay?

2019-01-13 14:12发布

问题:

I am newbie in liferay portal. I have developed one portlet in liferay for demo. I used inter portlet communication in this example. What i am doing is:- I have one search portlet in which i am having one textfield for search. When i click on search button it fetches the data from the database and display that data using search-contained in another portlet. I used ProcessEvent and ActionEvent annotation for this project.

Now what i want is when i click on the search button then the page should not be refresh(i.e i wish to use the concept of AJAX) and data should be displayed on the other portlet.

Code Snippet

Portlet A - view.jsp

<%@include file="/html/init.jsp"%>
<portlet:defineObjects />

<!--

<portlet:actionURL var="actionURL" name="pitchBall"></portlet:actionURL>

//-->
**Change to Resource URL**
<portlet:resourceURL var="resourceURL">
</portlet:resourceURL>


<aui:form method="POST" action="<%= resourceURL%>" name="    <portlet:namespace>fm1</portlet:namespace>">
    <aui:input name="search" id="search" />
    <aui:button type="submit" name="Search" value="Search" />
</aui:form>

Portlet A - SearchPortlet Class

package com.test;

/**
 * Portlet implementation class SearchPortlet
 */
public class SearchPortlet extends GenericPortlet {

    @Override
    public void render(RenderRequest request, RenderResponse response)
            throws PortletException, IOException {
        // TODO Auto-generated method stub
        super.render(request, response);

    }

    @ProcessAction(name="pitchBall") 
    public void pitchBall(ActionRequest request, ActionResponse response) throws SystemException {
        String name = ParamUtil.getString(request, "search");       
        QName qName = new QName("http://liferay.com/events", "ipc.pitch");
        response.setEvent(qName, name);
    }

    public void init() {
        editJSP = getInitParameter("edit-jsp");
        helpJSP = getInitParameter("help-jsp");
        viewJSP = getInitParameter("view-jsp");
    }

    public void doEdit(
            RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {

        include(editJSP, renderRequest, renderResponse);
    }

    public void doHelp(
            RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {

        include(helpJSP, renderRequest, renderResponse);
    }

    @Override
    public void doView(
            RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {
        //super.doView(renderRequest, renderResponse);
        System.out.println("In doView code");

        renderResponse.setContentType(renderRequest.getResponseContentType());

        // file to display...
        String url = "/html/searchportlet/view.jsp";

        // read the above file and output it...
        getPortletContext().getRequestDispatcher(url).include(renderRequest, renderResponse);
        //include(viewJSP, renderRequest, renderResponse);
    }

    @Override
    public void serveResource(ResourceRequest request, ResourceResponse response)
            throws PortletException, IOException {
        // TODO Auto-generated method stub
        //super.serveResource(request, response);
         System.out.println("In serveResource code");

         response.setContentType("text/html");

         String name = request.getParameter("search");

         // this seems to be the page that was calling...?
         String resourceID = request.getResourceID();
         System.out.println("resourceId was : " + resourceID);


         System.out.println("message was : " + name);
         PrintWriter writer = response.getWriter();

         writer.print(name);
    }

    protected void include(
            String path, RenderRequest renderRequest,
            RenderResponse renderResponse)
        throws IOException, PortletException {

        PortletRequestDispatcher portletRequestDispatcher =
            getPortletContext().getRequestDispatcher(path);

        if (portletRequestDispatcher == null) {
            _log.error(path + " is not a valid include");
        }
        else {
            portletRequestDispatcher.include(renderRequest, renderResponse);
        }
    }

    protected String editJSP;
    protected String helpJSP;
    protected String viewJSP;

    private static Log _log = LogFactoryUtil.getLog(SearchPortlet.class);

}

Portlet B - view.jsp

<%@include file="/html/init.jsp"%>
<portlet:defineObjects />

<%
String name = (String)renderRequest.getParameter("name");
%>

<liferay-ui:search-container
    emptyResultsMessage="author-empty-results-message">

    <liferay-ui:search-container-results
        results="<%= KeyurAuthorLocalServiceUtil.getStudentByName(name) %>" />

    <liferay-ui:search-container-row className="com.test.model.KeyurAuthor">

        <liferay-ui:search-container-column-text name="authorId"
            property="authorId" />
        <liferay-ui:search-container-column-text name="authorName"
            property="authorName" />
        <liferay-ui:search-container-column-text name="authorEmail"
            property="authorEmail" />
    </liferay-ui:search-container-row>

    <liferay-ui:search-iterator></liferay-ui:search-iterator>


</liferay-ui:search-container>

Portlet B - SearchResultPortlet Class

/**
 * Portlet implementation class SearchResultPortlet
 */
public class SearchResultPortlet extends GenericPortlet {

    public void init() {
        editJSP = getInitParameter("edit-jsp");
        helpJSP = getInitParameter("help-jsp");
        viewJSP = getInitParameter("view-jsp");
    }

    @ProcessEvent(qname="{http://liferay.com/events}ipc.pitch")
    public void catchBall(EventRequest request, EventResponse response) {
        Event event = request.getEvent();
        String name = (String)event.getValue();
        response.setRenderParameter("name", name);
    }

    public void doEdit(
            RenderRequest renderRequest, RenderResponse renderResponse)
    throws IOException, PortletException {

        include(editJSP, renderRequest, renderResponse);
    }

    public void doHelp(
            RenderRequest renderRequest, RenderResponse renderResponse)
    throws IOException, PortletException {

        include(helpJSP, renderRequest, renderResponse);
    }

    public void doView(
            RenderRequest renderRequest, RenderResponse renderResponse)
    throws IOException, PortletException {

        include(viewJSP, renderRequest, renderResponse);
    }

    protected void include(
            String path, RenderRequest renderRequest,
            RenderResponse renderResponse)
    throws IOException, PortletException {

        PortletRequestDispatcher portletRequestDispatcher =
            getPortletContext().getRequestDispatcher(path);

        if (portletRequestDispatcher == null) {
            _log.error(path + " is not a valid include");
        }
        else {
            portletRequestDispatcher.include(renderRequest, renderResponse);
        }
    }

    protected String editJSP;
    protected String helpJSP;
    protected String viewJSP;

    private static Log _log = LogFactoryUtil.getLog(SearchResultPortlet.class);

}

回答1:

When making ajax requests on portal your portlet should implemet

javax.portlet.ResourceServingPortlet

GenericPortlet already does but you want to override it, and instead of using <portlet:actionURL /> you should use <portlet:resourceURL /> fro from action.

And in your setup you should have search form with hidden field for keywords, and on clicking submit button in search portlet you should copy keywords from that form, with IPC, to search results portlet and invoke submit on search result from (without submitting form in search portlet (A)).

Your SearchResultPortlet class should be

public class SearchResultPortlet extends GenericPortlet {
    ...
    public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, java.io.IOException {
       // do search and return result
    }
    ...
}

EDIT: complete example

SearchForm

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class SearchForm extends GenericPortlet {

    @Override
    protected void doView(RenderRequest p_request, RenderResponse p_response) throws PortletException, IOException {
        getPortletContext().getRequestDispatcher("/WEB-INF/jsp/search.jsp").include(p_request, p_response);
    }
}

SearchResult

import java.io.IOException;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;

public class SearchResult extends GenericPortlet {

    @Override
    protected void doView(RenderRequest p_request, RenderResponse p_response) throws PortletException, IOException {
        getPortletContext().getRequestDispatcher("/WEB-INF/jsp/result.jsp").include(p_request, p_response);
    }

    @Override
    public void serveResource(ResourceRequest p_request, ResourceResponse p_response) throws PortletException, IOException {
                    //do your search here and put results in 'result'
        p_request.setAttribute("result", "results for: " + p_request.getParameter("search"));

        getPortletContext().getRequestDispatcher("/WEB-INF/jsp/html.jsp").include(p_request, p_response);
    }
}

search.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>

<portlet:defineObjects />

<script type="text/javascript">

    function doSearch() {
        Liferay.fire('searchKeywords', document.getElementById("<portlet:namespace/>search").value);    
    }

</script>

<form>
    <input type="text" name="search" id="<portlet:namespace/>search" />
    <button name="Search" value="Search" onclick="doSearch()" type="button">Search</button>
</form>

result.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>

<portlet:defineObjects />
<portlet:resourceURL var="rurl" />

<script type="text/javascript">
    Liferay.on('searchKeywords', function(event, p_data){
        var A = AUI(); 
        A.use('aui-io-request', function(aui) {
            A.io.request("<%= rurl %>", { 
                method : 'POST', 
                data: {search: p_data},
                dataType : 'html', 
                on : { 
                    success : function() { 
                        AUI().one("#<portlet:namespace/>placeholder").html(this.get('responseData'));
                    } 
                } 
            });
        });
    });
</script>

Search Results:<br />
<div id="<portlet:namespace/>placeholder">
</div>

html.jsp (this is for rendering results)

<%= request.getAttribute("result") %>


回答2:

You could use Inter portlet communication on the client side. Let's call portletA the search portlet and portletB the search-contained in another portlet. I'm assuming you are using Liferay 6+.

Step 1: clicking the search button on portletA will trigger the folowing javascript function on the browser:

var A = AUI(); 
A.use('aui-io-request', 
    function(aui) {
        A.io.request(<portletA_serch_action_url>, { 
            method : 'GET', 
            dataType : 'json', 
            on : { 
                success : function() { 
                    Liferay.fire('myEvent', this.get('responseData');                    
                } 
            } 
        });
    }
);

Note the call to A.io.request(<portletA_serch_action_url>... this is the portletA server side action url. See the following Liferay Blog for more information about Liferay 6 and ajax: http://www.liferay.com/web/nathan.cavanaugh/blog/-/blogs/alloyui-working-with-ajax.

Step 2: on portletA server response, portletA client side will launch an event with the data retrieved from the server. Note the call to Liferay.fire('myEvent', this.get('responseData'); this is the way in which Liferay supports client side Inter Portlet Communication on the client side(See http://www.liferay.com/community/wiki/-/wiki/Main/Client-side+Inter-Portlet+Communication).

myEvent is the event fired, this.get('responseData'); are the data retrieved from the server.

Step 3: client side portletB listen on myEvent and render the data

portletB will listen on myEvent and then render the data:

Liferay.on(
 'myEvent',
 function(event, data){
   var portletId = data.portletId;
   var portlet = data.portlet;

   if(portletId.indexOf('YOUR_PORTLET_A_ID') > -1){
        alert(data);
   }
 }
 );

Note the check of the provenience of the data from portletA:

   if(portletId.indexOf('YOUR_PORTLET_A_ID') > -1){...

You can remove that check if you don't have others portlets firing the same event.

Hope this help.