Liferay Objects and Remote Apps: How can we connect them?

Introduction

Liferay development has been changing throughout the years with its different versions. With 6.x we had WAR portlets. 7.0 came along and with it OSGi modules were the norm. And now, 7.4 has brought something completely new: Remote Apps. You can check this other blog and the official documentation for a thorough explanation about Remote Apps.

 

So, what are these Remote Apps? Well, as their name suggests they are applications that are not actually deployed within Liferay Portal/DXP. They use Liferay’s front-end infrastructure to register external applications with the Liferay platform and render them as widgets. So, since they use Liferay’s front-end infrastructure, these applications can be developed using a wide range of front-end frameworks/technologies, such as React, Angular or Vue. Pretty neat, huh?

 

Another feature that made its shining premiere with 7.4 is Objects. Remember Service Builder? Well, Objects is its brother. Little brother at the moment, but in the future it will become its big brother. With Objects you can create entities that are persisted in the database, just like you did with a Service Builder. The main difference is that Objects allow you to do this from the UI, totally without code and without deployments. Furthermore, each Object you create will not only add its own table to the database, they will also come with an API Rest that you can use to create, modify or delete these entities.

 

With these two things you might already know what I’m going to talk about in this blog (if you haven’t guessed from the title, of course). Traditionally, when you wanted to model an entity and work with it in Liferay you would implement a Service Builder module to create and work with that entity and then a MVC module to create its view. That’s a total of three modules (api and service modules from Service builder and MVC module) that needed to be deployed, tested, upgraded, taken care of… quite time consuming. 

 

Now, with Objects you can leave the data modeling and its maintenance to Liferay, and with Remote Apps you can use the development technology of your choice to create your view. The greatest part of this? No deployments at all. No need to stop everything to update your application. But how is all of this done? And how can we actually connect these Remote Apps to Objects’ APIs? Well, let’s find out, shall we?

 

First things first, what is the Remote App going to be? Well, in this case we are going to create a React Remote App that shows markers on a map of Spain.  As Spain's territory is divided into Autonomous Communities, and these communities are divided into Provinces, this hierarchy is a good example of how we can relate Objects and the queries we can use. Now, let’s get started!
 

Start Liferay and create the Objects needed

For this blog I’m going to use Liferay DXP 7.4 U27. And to do so, I’m going to use it with its docker image. So, in order to start our Liferay instance we can run 

docker run --name liferay-7.4-u27 -p 8080:8080 -d liferay/dxp:7.4.13-u27

 

Once our server has started we’ll log in so we can access Control Panel → Object → Objects.

 

From this interface let’s click on the blue plus button so we can add our Objects. The first one we are going to create is the Autonomous Communities object, so we are going to put “Community” in its label and “Communities” as its plural label. We click save and we’ll see that our new object is there with a “draft” status.

Now, we are going to give it some fields. To do so, we click on its name and access the “Fields” tab and then click on the plus button. Here we’ll see another form where we can add the field information. The first one, Label, is simply the label we are going to see on Liferay’s UI. The second one, Field Name, is the actual name of the field we have to use in order to access it in code. The next one, Type, is a dropdown where we can see the different types of fields we can store in Objects. Last, but not least, there is a checkbox where we can specify if this field is mandatory or not. For our Community object we are going to add a mandatory field named “Name” of the type “Text” .

With this field saved we can go back to the Details tab and change the Entry display field to use this Name field we just created to display our object. Next, we are going to change the Panel category key option to “Applications > Content” so we can see this Object in that section. Once this is done we simply click publish in order to have our object totally available. Important to keep in mind: all the fields created while the object is in draft status cannot be deleted once published, so be careful what fields you add before publishing it.

 

The next Object we are going to create is the Provinces object. We are going to follow the same procedure as we did with the Community object, using the label “Province” and the plural label “Provinces”. The rest of the options can remain the same as we did before, including fields.

 

The last Object we are going to use is the Point object. This one will follow the same structure as the previous ones, with Point as label, Points as plural label and two extra fields apart from Name: Latitude and Longitude. These two fields will be of the type Precision Decimal and also mandatory. Once adding these extra fields we can publish this object as we did with the others.

 

Now, we are going to make relationships between these objects. As mentioned above, Provinces belong to Communities, so we are going to represent that. We’ll access the Community object and go to its Relationships tab. Here, we are going to create a new relationship so Communities and Provinces are related. In this case, we are going to name the relationship “Community Province”, give it a One to many relationship type (each Province belongs to a single Community but a Community can have multiple Provinces) and choose the Province object.

Now, when we access the Province object we’ll see that it has a new field: “Community Province”. This field is the ID of the Community it belongs to.

 

The next step is to add the relationship between Point and Province, since each Point will be in a Province. The steps are the same as with Community and Province: we access the Province object, go to its Relationships tab and create a new One to many relationship called “Province Point” with Point. With this done now we can know the Province a Point belongs to and the same with Provinces and Communities.

 

Before moving on to the next part we are going to add another One to many relationship between Community and Point. Why? It will make our code easier. However, technically, this is not considered a good practice and should not be done this way for all cases. I’ll get into a more detailed explanation once we reach that code part.

 

Finally, last step, we will be adding some sample objects. To do so we simply have to access the Object we want to create (from Applications > Content, as we set before in Panel category key). I’m creating the following objects:

  • Community

  1. Name: Comunidad de Madrid

  2. Name: Castilla - La Mancha

  • Province

  1. Name: Madrid  Community Province: Comunidad de Madrid

  2. Name: Toledo Community Province: Castilla - La Mancha

  3. Name: Albacete Community Province: Castilla - La Mancha

  4. Name: Cuenca Community Province: Castilla - La Mancha

  5. Name: Ciudad Real Community Province: Castilla - La Mancha

  6. Name: Guadalajara Community Province: Castilla - La Mancha

  • Point

  1. Name: Point in Madrid Latitude: 40.47628 Longitude: -3.68592

Community Point: Comunidad de Madrid Province Point: Madrid

  1. Name: Point in Toledo Latitude: 39.8602 Longitude: -4.0251

Community Point: Castilla - La Mancha Province Point: Toledo

  1. Name: Point in Albacete Latitude: 38.9957 Longitude: -1.8564

Community Point:  Castilla - La Mancha Province Point: Albacete
 

Creating the Remote App

Now that we have data we can start with our Remote App. As we saw previously, this Remote App is going to be a React Remote App, so we are going to start with a basic empty project. To do so, we are going to run the following command:

npx create-react-app show-points-app

 

This will create a basic React application from where we can start to work.

First, we will update the dependencies we are going to need in the package.json file. We are going to eliminate the @testing dependencies and also the web-vitals one and add sass, leaflet and react-leaflet. We are going to use sass to style our application and leaflet to show the map. You can see the final package.json file here. With our dependencies updated we simply have to run 

npm install

inside the created folder so the new packages are downloaded and the deleted ones are taken out of our node_modules folder.

 

The next step is to create a folder named “components” inside the src folder. This is where we will have our React components (for more info on “React components” check the official documentation. Moreover, you can always find components already done on the internet, many of them totally free). Next, we will create a “common” folder within src which will have two folders: styles and services. This last folder will have another folder called “liferay”. In styles we’ll have our css/scss files to style the application and inside “services” all the services we might need. 

 

Final directory structure

 

In our case, we are going to need some way to connect our app to Liferay Objects API, which is why we are going to create a file named api.js inside services/liferay folder. In this file we will create a function to work with the HTTP Request we can make to Liferay’s APIs. See the code needed here.

 

The next step is to create our components. In this application we are going to have two components: SearchMap and OpenMap. 

 

Let’s start with OpenMap. We are going to create a file named OpenMap.js inside the “components” folder and we will add the code to render the map and the markers needed. This map is going to be created using Leaflet, an open source js library that uses OpenStreetMap. Also, since we are creating a React application, we also use React Leaflet to abstract Leaflet layers as React components. See the code for this component here. In order for the map to be displayed correctly the component needs Leaflet’s css. We are going to import them in a file named OpenMap.scss in the common/styles path of our project. See the code needed here.

 

Our next step is creating our SearchMap component. In this component we will have two dropdowns to filter the points we want to see in the map and we are also going to add the map here. Furthermore, we’ll make our API calls from here. See the code needed here. In the getCommunities and getProvinces functions we can see how we are making an API call giving the Object API URL as the argument to the LiferayApi service we created previously. To find the created Objects API URLs you simply need to access hostname/o/api and there click on the top right dropdown “REST Applications”. Here you’ll be able to see every API Liferay offers, which includes the APIs created when we create Objects.

Since these APIs are REST APIs we can not only fetch data, but also post, update or delete data as well. Furthermore, we can also filter fetch calls so the values returned meet any requirements needed. This is exactly what is done in the updatePointsInMap function, where only the Points that meet the filter options are retrieved. In order to avoid making two calls to the API we use the two relationships that Point has with Province and Community. This way we avoid having to call the API again if we only mark Community as a filter.

 

Last, but not least, it’s time to render our components. Since SearchMap already has OpenMap in its return we just have to call the SearchMap component in the index.js. Apart from that, we also have to define our custom element with an id. All the code needed is defined here. It’s important to notice the ID defined in line 19, since we are going to need that ID to register the Remote App in our Liferay instance.
 

Adding the Remote App to Liferay

Now that we have all of our code ready, we are ready to deploy it to our Liferay instance. How? First we have to build the application. We will do this by running the following command

yarn build

 

in the application root folder (for explanation on why I am using yarn instead of npm check this blog). This will generate a “build” folder and inside it there will be a “static” folder. In this folder there will be the transpiled css and js files that we need to deploy to our Liferay instance. Specifically, we will need the files that have a name format such as main.abc123.js and main.cde456.css. We are going to take these two files and upload them to Liferay’s document library.

With our code in Liferay now we can register our Remote App. To do this, we’ll access the Remote apps menu by accessing the applications tab (clicking on the top right dotted square). From here, we’ll add a new Remote App named “Search map”. We’ll choose “custom element” as its type, use the same ID we gave our application in the code (search-map-app) as the HTML element name and add the URLs to our JS and CSS files in the URL and CSS URL fields respectively. Since we have this code in Liferay’s document library we can get this URL by clicking on the file, then on the top right information icon and copying the Latest Version URL.

However, in a real production environment these files should be hosted on a server optimized for hosting static resources (in this case you will also have to enable CORS, to do so you can check Liferay's documentation).

 

The final configuration will look like this:

With this done we can add our application to any content page we want. This is achieved the same way as with any other fragment or widget: drag and drop.

Here we have our React Remote App in Liferay! Now you can play around with it, testing the filters, adding more points or you can even step up the game and try to style the application to make it look more appealing.
 

Advantages of this new development paradigm and final conclusions

As we have seen throughout this blog, with the use of Objects you can avoid having to develop whole Service builder modules to model your data. Furthermore, Objects are integrated with Liferay’s frameworks such as Asset Framework and Permissions Framework, so it makes data modeling much quicker and easier without having to write any code. As a plus, since Objects are totally managed by DXP you won’t have any code you need to upgrade whenever you update your Liferay DXP version.

 

With the Remote Apps approach you can now build applications to use in Liferay with modern technologies,  such as React (our case here), Angular, Vue... Moreover, you can still access Liferay’s services and data thanks to their REST APIs, which are also available with Objects.

 

You can find all the code used in this blog in this repository, plus the Objects’ schemas used in this folder.

 

I hope you find this blog useful for all your future projects with Liferay DXP 7.4. If you have any tips, doubts or suggestions please leave them as a comment below. Thank you for reading this blog!

1
Blogs