RE: Define a "default render" command for a MVCPortlet?

Thomas Kellerer, modified 7 Years ago. Expert Posts: 490 Join Date: 6/9/08 Recent Posts
All examples for MVCPortlets show the portlet definition with a default view jsp.

However as I need to retrieve data to populate that view, I would like to do that in a (reusable) render command.

But I can't find a way to define a the render command in the portlet annotation instead of the view jsp:

@Component(
    immediate = true,
    property = {
        "com.liferay.portlet.display-category=MyPortlets",
        "com.liferay.portlet.instanceable=false",
        "javax.portlet.display-name=Display Portlet",
        "javax.portlet.init-param.view-template=/myportlet/list_data.jsp",
        "javax.portlet.name=my_portlet",
        "com.liferay.portlet.ajaxable=false",
    },
    service = Portlet.class
)


But instead of referencing a .jsp page in javax.portlet.init-param.view-template, I would like to tell Liferay that it should call a specific RenderCommand when the portlet is initially displayed.

I would like to avoid putting that into the Portlet's render() method, because that would duplicate the code between the RenderCommand and the render() method.

How can I do that?
Thomas Kellerer, modified 7 Years ago. Expert Posts: 490 Join Date: 6/9/08 Recent Posts
My workaround for now is the following hack in the render() method of the portlet:

public class MyDisplayPortlet
    extends MVCPortlet {

    private final DisplayCommand displayData = new DisplayCommand();
    
    @Override
    public void render(RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {

        String renderCmd = ParamUtil.getString(renderRequest, "mvcRenderCommandName", null);
        
        if (renderCmd == null) {
            String mvcPath = displayData.render(renderRequest, renderResponse);
            renderRequest.setAttribute(getMVCPathAttributeName(renderResponse.getNamespace()), mvcPath);
        }
        
        super.render(renderRequest, renderResponse); 
    }
}
Creating an additional instance of the render command feels like a hack though.
thumbnail
Minhchau Dang, modified 7 Years ago. Liferay Master Posts: 598 Join Date: 10/22/07 Recent Posts
Thomas Kellerer:
Creating an additional instance of the render command feels like a hack though.

It probably also won't work if your DisplayCommand needed to have any of its references satisfied by OSGi, since you're essentially instantiating the object yourself instead of letting OSGi do it for you.

An alternate option is to use the MVCCommandCache to grab the existing object during the render phase, if it hasn't already been set. That allows you to take advantage of Liferay storing the references.

@Override
public void render(RenderRequest renderRequest, RenderResponse renderResponse)
    throws IOException, PortletException {

    if (displayData == null) {
        displayData = getRenderMVCCommandCache().getMVCCommand("def");
    }

    // ...
}

Another option is to use @Reference to get the existing component instantiated/managed by OSGi. This allows you to take advantage of OSGi storing the references.

@Reference(target="(&(javax.portlet.name=abc)(mvc.command.name=def))")
private MVCRenderCommand displayData;
Thomas Kellerer, modified 7 Years ago. Expert Posts: 490 Join Date: 6/9/08 Recent Posts
Thanks for the answer.

There is no method getRenderMVCCommandCache() in MVCPortlet and access to the instance variable _mvcRenderCommandCache is private.
So I don't see how I could use that cache.

But, using the @Reference annotation seems to work.
thumbnail
Minhchau Dang, modified 7 Years ago. Liferay Master Posts: 598 Join Date: 10/22/07 Recent Posts
Thomas Kellerer:
There is no method getRenderMVCCommandCache() in MVCPortlet and access to the instance variable _mvcRenderCommandCache is private. So I don't see how I could use that cache.

Whether that method is available depends on the version of com.liferay.portal.kernel you are compiling against, and to some extent which version of Liferay you are running.

Based on the blame on the file, the method wasn't added until LPS-68922, which has a DE-8 label. It also has a fix version of 7.0.3 GA4, so if you're running 7.0.2 GA3 or earlier or you built from source before DE-8, the method really isn't available.

If you're using one of the later releases, checking the releases between DE-7 (2.12.x) and DE-8 (2.16.x), the earliest version of com.liferay.portal.kernel that provides that method is 2.13.0, so if your dependency version is anything lower than that (it's typical to have a dependency version that is 2.0.0 or 2.6.0 if you follow Liferay examples), the compiler would be unable to find it.
thumbnail
Adrian Rodriguez Monedero, modified 6 Years ago. New Member Posts: 15 Join Date: 10/17/10 Recent Posts

After checking the source code of MVCPortlet in Liferay 7.0, it seems as you could define a default render command declaring this component property of your display command:

 

"mvc.command.name=/"

Victor Soares, modified 6 Years ago. New Member Posts: 8 Join Date: 8/6/15 Recent Posts

Just as a note: using "mvc.command.name=/" will work to render a MVC command, however, there are two issues with this method:

 

The first one is that is an inconsistent behavior in the portal side, one cannot put any command there, just the one called "/".

The second one is that will break other portlet modes, like edit.