Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

The following best practices are recommended when loading resources via JavaScript within Lumira extensions:

  1. JavaScript dependencies should be loaded with requirejs
  2. Resource URLs should be constructed using relative paths within the extension bundle

Following these guidelines will future-proof your extension and allow it to coexist happily with other extensions.

Using requirejs to load JavaScript dependencies

Before proceeding, it’s a good idea to have some familiarity with requirejs and/or asynchronous module definitions (AMDs), the specification that requirejs implements.

http://requirejs.org/docs/start.html

Here’s a very abridged review. When you define a module, which is a cohesive unit of code within a single JavaScript file, the basic structure is:


define([‘dependencyA’, ‘dependencyB’], function(dependencyA, dependencyB) {
   var mymodule = {}; // Define the module object or function
   return mymodule;
});

When you want to load a module without defining one, you use the “require” function instead of the “define” function.


require([‘dependencyA’, ‘dependencyB’], function(dependencyA, dependencyB) {
   // code using dependency and dependencyB
});

Using this method, different extensions can load different versions of the same library without conflicting with each other. The prerequisite is that the library must be defined as an AMD (loadable by requirejs). In some cases you might have to modify the library to satisfy this requirement, which is easy in some cases but impossible in others.

JavaScript Dependency Example

Let’s imagine that we’re building a visualization extension and we want to place our source code in multiple files. We’ll put some easing functions, for animation, in a file called “easing.js”. Then we’ll use those functions in a separate file, “render.js”, which is responsible for drawing our chart. I’ll assume at this point that you’ve previously created a “Hello World” visualization extension and are familiar with the basics, but if you want to review the procedure, click here:

http://scn.sap.com/community/lumira/blog/2014/12/10/sap-lumira-chart-extension--hello-world-from-sap...

After VizPacker generates the extension’s skeleton, our project folder structure looks like this:

Rather than copying and pasting the contents of easing.js into render.js, we’ll add easing.js to the project as a separate file and load it using require. We’ll put easing.js into the “utils” folder.

Easing.js is defined as an asynchronous module.


define([], function(){
     // http://robertpenner.com/easing/
     var Easing = {
     easeInQuad: function (x, t, b, c, d) {
          return c*(t/=d)*t + b;
     },
     easeOutQuad: function (x, t, b, c, d) {
          return -c *(t/=d)*(t-2) + b;
     }
     // etc.
     };
     return Easing;
});


Now in render.js we can load and reference the Easing object by declaring it as a dependency. Note that we must specify a relative path to the module, with the base being the root of our bundle. The root of our bundle is the folder that contains the “*-bundle.js” file. When we pass paths to require, they should be relative to that root.


define("sap_viz_ext_demorequirejs-src/js/render", ["sap_viz_ext_demorequirejs-src/js/utils/easing"],
function(Easing){
     var render = function(data, container) {
          container.selectAll("p").remove();
          container.append("text")
               .text(typeof Easing.easeInQuad); // Prints "function"
     };
     return render;
});

Using require from within the render function

If you need to require a module from within the body of your render function, you can do so by requiring the “require” module. If we were to use this method with the easing module, it would look like this:


define("sap_viz_ext_demorequirejs-src/js/render", ["require"], function(require){
     var render = function(data, container) {
          require(["sap_viz_ext_demorequirejs-src/js/utils/easing"], function(Easing) {
               container.selectAll("p").remove();
               container.append("text")
                    .text(typeof Easing.easeInQuad); // Prints "function"
          });
     };
     return render;
});

Constructing Resource URLs

Sometimes you need a URL to something in your bundle, like an image file. It’s important to build the URL from relative paths so that your extension will function in deployments other than Lumira Desktop. Requirejs again is the recommended way to do this. First you require the “require” module and then you use its “toUrl” function.

For example, let’s add a logo image to our bundle and then draw it in the render function. We’ll put the image file in the resources folder.

To draw the logo, we’ll create an SVG image node using d3. The URL for the source image is created by passing a bundle-relative path to the require.toUrl function.


define("sap_viz_ext_demorequirejs-src/js/render", ["require"], function(require){
     var render = function(data, container) {
          var imageUrl = require.toUrl("sap_viz_ext_demorequirejs-src/resources/images/SAP_logo.png");
          container.selectAll("image").remove();
          container.append("image")
               .attr("xlink:href", imageUrl)
               .attr("height", "20px")
               .attr("width", "41px");
     };
     return render;
});




1 Comment