Skip to Content

SAPUI5 and C4C. My (un)expected journey. Part 1.

 

Finally, the last part of my journey is here. And hopefully, it’s a good timing for it.
However, each journey leads to another journey. So let’s finish this first.

As I described in my previous blogs of this series, I had a task to extend C4C UIs (both HTML client and Fiori client). The extension should be something done with SAPUI5/OpenUI5. In other words, it should embed custom (preferably, SAPUI5) control(s) into C4C. And it should talk to C4C as well.

HTML mashups were not good enough. At least, I was not satisfied with the result I managed to get. I continued to look around and noticed that there are some UI elements or controls which are not very common for C4C UI. Just to name a few:

  • “Document Flow” tab in sales and service documents;
  • “Interactions” section in service tickets where you can read related emails or even social media posts and respond to them;
  • Map integration into different areas like Visits, Accounts and etc;
  • “Barcode scanner” feature when using C4C app;
  • Geolocation feature when doing check in and check out in Visits;
  • “Increment” control when adding products;
  • Side panels in recent releases with all Machine Learning visualization;

Looking into corresponding screens in SAP UI Designer I realized that those all controls were mainly done via Custom Pane control. Hang on. Wasn’t it used for Silverlight libraries only? Yes, some of them still have Assembly Name and Type name maintained representing Silverlight’s DLLs. However, others have Assembly and Type Names empty. Apparently, Silverlight is not the only use case for Custom Panes anymore.

I checked properties of some of those nice components I mentioned above. And found that they all have interesting section populated. Properties section name I’m talking about is “HTML Custom Components”. It has three properties:
– JS Library Name
– JS Library URL
– JS Type Name

At that point in time, I had no idea what they meant.

I started to explore further. I grabbed the knowledge and tools gained and described in my previous blogs. The main one was the debug mode of SAPUI5 to load debug sources. Otherwise, the sources are loaded minimized. Just take as a precaution: it slows the load of C4C UI quite significantly. But it’s worth it. You can see from the much broader perspective and find much more interesting things (and comments).

I searched for JS Type Name value in debug sources and found that “JS Type Name” is a UI5 control name within JS library with the name as maintained in “JS Library Name” property. That sounded interesting. However, all I found was some standard libraries. No documentation. Again, I had no clue what to do with all of these.

The only thing I knew was that I needed my own library built somehow. I had an experience only in developing some SAPUI5/OpenUI5 things in WebIDE. But no libraries at all.
As usual, SAP Community was on help.
I’ve read quite a few blogs and articles about how to deal with your own JS libraries and what they are all about.
Those mentioned below were very useful personally for me to understand general principles of libraries:

Well, after reading that and “trial and error” attempts, I found that I actually needed to build a preloaded version of the library. By the way, initially, I tried a very simple thing: to put a well-known custom control (it was star rating control) into a library.

And here came some real stuff. Fortunately for me, not for a long time, but this was very early steps though. However, looking into the past I think it was a very good experience. The experience of learning a very small piece of Node.js and Grunt.
Having my library ready and composed, I followed the steps:
1. Install Node.js from https://nodejs.org/
2. Install Grunt’s command line interface (CLI) from the command line with:
npm install -g grunt-cli
3. Create package.json file
4. Create Gruntfile.js
5. Run npm install
6. Run Grunt
If you need to rebuild – simply run Grunt again

Looking now at this list it sounds very simple. When I was doing that it was actually very painful. And each step required a hell lot of work to research because everything from this list was new to me.

Fortunately, time went by and from the blog “Implementing a custom UI5 lib and a UI5 app consuming the lib using SAP Full-Stack WebIDE with Grunt” I noticed that WebIDE now has “Build” option integrated. You don’t need anymore to change your SAPUI5 thing in WebIDE, then download it, then build it with Grunt, then do some other steps.

Altogether, it’s now much easier and you can simply Build from WebIDE or even Deploy to Cloud Platform and it will build it for you as well. Check this tutorial Grunt Build in SAP Web IDE.

Having my library ready and “built” now the question was how to embed my library into C4C. All standard Custom Panes which I found had “JS Library URL” as a relative path. Means they were hosted somewhere directly in C4C. I tried to upload files in C4C tenant via SAP UI designer. Unfortunately with no luck. SDK didn’t accept either archive of the library or js-files. It only accepts:

  • UIConfiguration files
  • Dynamic Link Libraries (*.dll)
  • Silverlight Application package (*.xap)

Then I tried to put my libraries’ URL which was deployed to SAP Cloud Platform from WebIDE. And it worked! Well, not completely, to be honest. But C4C at least tried to retrieve it from SCP address I provided. It has failed actually.
And the reason was CORS. Again, the same like in HTML mashups I faced CORS issue.

I searched a lot but didn’t find any explanation how to configure Access-Control-Allow-Origin header for the resource (e.g. my library) deployed to SCP. I’m still missing this possibility. Would be much easier with it.

Since it was not there in the documentation I asked the SAP Community SAPUI5 custom library hosted in SCP and on Twitter. And got the idea (thanks Jakob Marius Kjær , Mike Doyle and John Patterson for ideas and help! ) how to achieve this which I documented in the same discussion.

The approach was:

  1. grab SAP Cloud Connectivity Proxy and change it to rewrite the header according to my needs;
  2. deploy the modified version of the proxy to SCP;
  3. configure application’s destination to point to the deployed library and voilà: same origin SCP – no CORS.

You can even deploy such a proxy to your own trial account of SCP. However, I would personally not recommend using a trial account. Because a Java app is stopped each month or so if deployed to a  trial account and you need to restart it manually. I would use trial account only for testing purposes.

Doing all this research around proxying calls from C4C to my library hosted in SCP, in parallel I used github.io to host my library. The advantage was that github.io returns this CORS header as * by default and there was no CORS issues at all. How to setup your own github.io page and commit to it – please see here https://pages.github.com/. But again it’s only for testing purposes.

After testing is finished I’d switch to some production and stable web server where you can deploy your library and where you are able to populate Access-Control-Allow-Origin header correctly or it’s already done for you. Basically, which is under your or your company or your customer control.

“JS Libray URL” should point to library.js excluding library.js from the path.
For example, if your library.js is accessible via the following link:
https://<server>/ycustom/c4c/ui5/library.js
then “JS libary URL” should be https://<server>/ycustom/c4c/ui5/ .

“JS Library Name” as it was said it’s your library name maintained in the library.js file
“JS Type Name” is your control name within this library.

But the patient reader would ask: all good, but how would I build a library or a control itself then?
Legit question.

All custom SAPUI5 controls should derive from sap.client.basecontrols.core.CustomPane control.
CustomPane control has public methods:

  • initializePane (derived from CustomPane)
  • onBeforeRendering
  • onAfterRendering
  • renderer

These methods (or some of them) should be redefined in your own control. If you have a question what they are all – nice quick answer here: How the lifecycle of UI5 Control works?

The renderer can either be as a method of your CustomPane or as a separate class. However, nowadays it’s more advisable to have a separate class to render your control. Check here for an example namespace sap.ui.core.Renderer

Even better, we can parametrize CustomPane using section “Design – Parameters” in its properties. We can maintain key-value pairs there and read them in our implementation. Check “Cheat Sheet” below.

From here I was good to go to start and build some very useful custom control and components.

My journey would end here. But it wouldn’t be “(un)expected”.

On one beautiful day, I had quite an issue. My proxy stopped and didn’t want to start again. Unaccessible custom library caused famous “Exception occurred” popup.

Switch quickly from one source of the library to another is not possible since Custom Pane parameters accessible only via SAP UI Designer for customers/partners. And in production tenant SAP UI designer can’t be opened even with “production fix” switched on. So if you want to change JS Libary URL, you need to move complete SDK/PDI solution from your development tenant to production.

What even worth right now is the following. If your JS library is not accessible or an error occurred in your Custom Pane initialization phase, C4C currently is throwing an exception asking to try again or contact Support. Like any other dump. And UI may become completely unusable. In one of messages, SAP Support representative mentioned that SAP might switch off CustomPane element completely for customers/partners development due to this.

I agree, that it always easy to follow the destructive way. Switch off. Turn off. Prohibit. Forbid. Disable.

However, I would propose a bit different approach.
Make exception during CustomPane processing less aggressive. Allow UI continue to load logging error in console and showing some temporary placeholder instead.

If you, Reader, are still here, I’m asking you please support the idea to have a nice and user-friendly handling for such cases.

Oh, if I started ideas here, one more which is relevant. In SAP UI Designer there are actually two “custom” elements. One I described in this blog and it is CustomPane. The other is CustomControl. However, currently, this CustomControl doesn’t have those magical JS-relevant properties enabled for customers/partners. SAP uses such a control, for example, to implement “Increment” control when adding products into business documents.

As a recap. I hope any reader who finished this journey with me would love to go and try himself/herself what I described briefly. It was not an end to end implementation. However, I may continue and shed light on actual end to end implementation later. For example, how to use barcode scanner within your custom implementation. Or some other cool stuff which I did already (hint: not all panes should be rendered).

Having CustomPane (and hopefully CustomControl soon) in your arsenal makes C4C custom development more flexible, highly productive and customers are very pleased with the result. It’s not hard or difficult as it might look like. Give it a try at least is my suggestion and good luck on your very own journey! 🙂


I’m asking to support my ideas:

And my “cheat sheet”:

// How to set value in data model from JS:
var oDataModel = this.getModel();
var oGTIN = oDataModel.getDataObject("/Root/Lead/ProductID");
oGTIN.setValue(sResult);

// One of the ways to get additional settings from Data Model
var vLongPath = oController.getDataContainer().getDataObject("/Root/Settings/LongitudePath");
if (vLongPath) {
	vLongPath = vLongPath.getValue();
}

// How to check that we're in extended app:
if (sap.client.getCurrentApplication().getRuntimeEnvironment().isRunningInContainer())

// How to check if it's Fiori client/extended app
sap.client.getCurrentApplication().isNewUI()

// How to get parent controller (when in pane object)
var oController = this.getController();
oController = oController.getParentController();

// How to attach to events from C4C (when in pane object)
var oController = this.getController();
oController = oController.getParentController();
oController.getEventProcessor().attachEvent(sap.client.evt.EventProcessor.EVENTS.EVENT_FIRED, this._handleGetUserLocation, this);
...
_handleGetUserLocation: function(oEvent) {
	var sEventName = oEvent.getParameter("event");
	if (sEventName === this._vEventName) {
		this._getUserLocation(true);
	}
}

// How to call C4C event handler from js library
	this.oToCustomer = new sap.ui.commons.MenuItem({
		icon: this.getImagePath("customer_16.png"),
		select: function() {
			that.oAddressContext.setValue("To");
			var oEventContext = new sap.client.evt.EventContext(this);
			that.getController().getEventProcessor().handleEvent("Select_CustomerEmail", oEventContext);
		}
	});

// How to get Custom Pane parameters:
this.getParameters(); // return all parameters
// or
this.getParameter("ParameterName"); // return named parameter

// How to check we're in debugMode
sap.client.getCurrentApplication().isDebugMode()

// How to check URL parameters:
bDebugMode = Util.getQueryParameter("debugMode") === "true";

// How to get dom ref
var outlookBox = this.oOutlookScoped.getDomRef();
jQuery(outlookBox).parent().show();
or jQuery(outlookBox).hide();

// How to listen to complete data container update
this.getModel().attachDataContainerUpdateFinished( function() {
	that._checkBtnState();
});

 

 

To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

Leave a Reply