Custom Message Parsing in SAPUI5
This post will walk you through creating a custom MessageParser using a sample scenario for an example in which it could be used. The entire code for the working application created in this post can be found here: pritin-tyagaraj/ui5-message-parser · GitHub
One more important thing it does, is take care of handling messages (success/error/information) returned by the server and convert them into appropriate UI5 objects which can then be used by your application (for example, by an sap.m.MessagePopover control instance). It is able to do this without you writing any application code, because it knows (thanks to the OData protocol that is being followed) how exactly the server’s response will be structured and where within it to look for messages.
If you’ve built an application that uses the ODataModel – even without code to explicitly handle messages – you can still already see the messages that the framework is automagically creating by executing the following snippet in your console.
But what happens when you are not using an OData service? What if it’s a HANA XS service or some other service that does not follow the OData protocol? Here is where things get interesting – the framework is unable to understand the server’s response because it does not follow a protocol that UI5 understands.
Enter, stage left – sap.ui.core.message.MessageParser
To solve this problem in a neat way, we must implement the MessageParser interface that the framework provides. This lets us write code to specify how to extract messages from the server’s response. Before jumping into sample code however, let’s understand the different entities that are involved in getting custom message parsing to work in our application. The following diagram gives a high level overview of how the various pieces fit together. As we continue reading about the roles that each of these boxes plays, the diagram will become easier to comprehend.
|This is a type of model (just like JSONModel, XMLModel etc. are types) which stores, by no surprise, messages. By ‘messages’ here, I mean instances of the sap.ui.core.message.Message class. These have properties like ‘id’, ‘type’, ‘description’ and ‘descriptionUrl’ that are typically used to describe each message.|
|An instance of this class gets to know (indirectly from our message parser) which messages are no longer relevant (need to be removed from the MessageModel) and which messages are new (need to be added to the MessageModel). That’s pretty much the only role that the MessageManager is playing in this picture – to make sure the messages within the MessageModel are up to date.|
A MessageParser is a static class that implements the sap.ui.core.message.MessageParser interface. The only two methods that must mandatorily be implemented are parse() and setProcessor().
The parse() method expects one argument – the server’s response. Our implementation of this method is expected to extract the messages within the response, and then decide which messages are ‘new’ and which ones are ‘old’ (The MessageManager uses this, like we saw above)
This is the class that is actually processing the server’s response (and as a result, processing (or ‘receiving’) the messages from the server). If your application uses an ODataModel, your ODataModel is playing the role of Message Processor, since it is responsible for talking to the OData service and sending/receiving information.
The ODataModel uses a standard implementation of the MessageParser interface (sap.ui.model.odata.ODataMessageParser); and this is how messages from the OData service got pushed into the core MessageModel without us writing any code.
To demonstrate how exactly our CustomMessageParser will fit into the equation though, we’ll be creating our own model. Since we will be interacting with the non-OData service using this custom model, it plays the role of MessageProcessor.
Our sample application
Defining the protocol
So we’ve decided to not use the OData protocol, but we still need to stick to some protocol! Let’s make up a protocol (as is common to do within non-OData projects) and decide that the server’s response must always have a structure similar to the following:
So our protocol requires any response from our server to be a JSON object with a ‘data’ node in the response, and a separate node called ‘messages’, which should be an array of objects – each object representing one message.
Creating the CustomMessageParser
Since we have now defined a protocol for our application to follow, we can already write our own implementation of a MessageParser.
This parse() method is called from our custom model (we’ll see this next), and gets the server’s response as an argument. Since we know that the response will have a node called ‘messages’ within it, we loop through it and create instances of sap.ui.core.message.Message (Step #2 in the overview diagram).
Once we have an array of all sap.ui.core.message.Message instances, we fire the messageChange event (Step #3) to which the standard MessageManager class will react and in response keep its MessageModel in sync. The MessageManager is instantiated in our view’s controller, and our sap.m.MessagePopover control will be bound to the MessageManager‘s model. We will see both of these towards the end of this post.
Creating a custom model
We call the parse() method of our very own MessageParser from within the ‘Message Processor’, which as we saw a while back is the entity that is actually making the network request to fetch data. Since the ODataModel already ships with a powerful message parser, let’s extend the JSONModel with a ‘read’ method similar to the ODataModel‘s to demonstrate where the call MessageParser is plugged in.
Once our method fetches data from the server (in our example we just fetch it from a file), it passes the response to the parse() method of the MessageParser (Step #1) to extract any messages that the response may contain. Then, it goes on to update its own internal structures to contain the actual data that the server returned. This can then be bound to UI controls via data binding.
In the view’s controller, we trigger the read() method of our custom model to trigger (OK, “simulate”) a network operation which retrieves a JSON object from the server. The method is expected to place the received data in the ‘/Customers’ path; to which the sap.m.List control in our view will be bound.
Apart from triggering the read, we also create an instance of the standard sap.ui.core.message.MessageManager class. Once we register our custom model (a.k.a “the message processor”) with this MessageManager instance, it will react to any messageChange events that our parser fires on the model (Step #4)
Showing messages on the UI
With everything we’ve looked at so far, we now have a MessageManager which contains a MessageModel – which in turn contains all the messages that we’ve received from our server. We now use a suitable control which can be bound (via a ListBinding) to the MessageModel. The sap.m library has a control specifically built for showing messages, so we use it – the sap.m.MessagePopover control.
What we get will this work (which is by no means “just a few lines of code”) and this exact output could have been achieved with much fewer lines of code. However, this is a neat and more importantly “scalable” way to handle messages when using protocols other than OData in your project – in the sense that even in a project which has a complex protocol being followed, this approach will work well and the developed code will be easy to maintain.
Hope you enjoyed reading this writeup! The source code for a working example can be found at pritin-tyagaraj/ui5-message-parser · GitHub. Your feedback is most welcome in the comments section below. ????