Requirejs is an open source JavaScript module loader and mostly used to improve script file loading times and to simply add external libraries. For example, normally all of your declared script objects (contribution.xml) will be loaded upfront on app start. Requirejs enables SDK developers to load libraries on demand. This short introduction shall be sufficient for now as my goal is to show how to integrate libraries into DesignStudio extensions.

When you start coding right after reading the developer guide you would most likely try to load external libraries by placing them in your SDK folder structure and referencing them in your contribution.xml. So far so good, but when you open your favorite browser’s developer tools you might notice an error message coming from DesignStudio’s built-in requirejs plugin: MISMATCHED ANONYMOUS DEFINE() MODULES (This problem doesn’t apply to all of the libraries but it would be too extensive to explain all of the possibilities right now). Until recently this was no real problem, because the errow was only showing in devel mode, if you made sure that all of you external libraries are loaded in the correct order. But IE started to expose this requirejs error as a standard DesignStudio error. That’s when I thought it is time to address this issue after all.

The following code examples are inspired by Mike Howles and Karol Kalisz, with whom I collaborate on the SCN DesignStudio SDK repository. I chose Leaflet and OpenStreetMap to show the integration.

First thing you have to do, is erase all of the libraries from the contribution.xml file which you want to load using requirejs. After that wrap your code in component.js like this:

/wp-content/uploads/2015/05/require_osm_example_new_769230.png

The first section locates the SDK folder structure during runtime (local and platform mode) to address the external library files later on (lines 22-27). The following section configures requirejs for the on the on demand loading process (lines 30-44). Please note that the file references are missing the “js” file extension (Requirejs will provide it by default). One quick note regarding the “shim” section. Leaflet-Providers is a Leaflet plugin responsible for the loading of the freely available OpenStreetMap map tiles. This plugin relies on the global Leaflet variable “L” which is exposed on library load completion. Therefore we need to define a dependency for this plugin making sure that the library loading order and global variable declaration is correct and transparent for the plugin. The JavaScript method call sdkReqs actually loads the defined libraries. Please note that I am not exposing “prv” to the app code. But you could either assign an additional variable in line 46 to the function closure or load the library into a variable like this: var prv = require(“prv”);

At last you only have to ensure that all of the function closures opened at the top are closed again (lines 53-54). Now the tedious mismatched anonymous define error should be gone for good 🙂

Have fun using requirejs and as always feel free to ask lots of follow up questions.

Find the fully functional example project and the source files on GitHub: https://github.com/MartinPankraz/DesignStudioSDK-Components

Yours

Martin

References:

•     http://requirejs.org/

•     https://github.com/org-scn-design-studio-community/sdkpackage

To report this post you need to login first.

9 Comments

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

  1. Mike Howles

    Hey Martin,

    Great post, this I hope helps remove some of the mystery of RequireJS.  I keep learning new things it can do myself!

    (0) 
  2. Reiner Hille-Doering

    Hi Martin,

    it is correct that RequireJS modules can’t be loaded by a <jsInclude> tag – because you would get anonymous defines. But instead copying all this very implementation-specific stuff in your SDK-handler you should better use the builtin sap.zen.Dispatcher.instance.require function, as described in What’s Coming in Design Studio 1.4 SDK .

    This might you to change your main code into the callback, but I’m sure that this is possible in almost all cases, e.g.

    sap.designstudio.sdk.Component.subclass(“com.sap.sample.coloredbox.ColoredBox”, function() {

      var that = this;

      this.init = function() {

      sap.zen.Dispatcher.instance.require([“leaflet”], function(l) {

        // Do you rendering here using your required modules.

       });

      });

      };

    Thanks,

    Reiner.

    (0) 
    1. Mike Howles

      Hey Reiner,

      I actually tried to use your suggested require call in sap.zen.Dispatcher but because of lack of path configuration flexibility (external CDN paths and fallback paths) and shim configuration, is why I came up with the ‘sdkReqs’ “hack/workaround” that essentially does what the sap.zen.Dispatcher’s require call does (pause, require callback, resume) so that we could have more control of the require setup.  If there’s a way to do this using the zen.Dispatcher require wrapper function, that would be great if I’ve missed something.

      (0) 
      1. Reiner Hille-Doering

        The problem is that we might need to change the internal functionality of sap.zen.Dispatcher.require, e.g. in 1.5. So cloning it will cause incompatibility.

        It is correct that you can’t use Shims – and you are stick to the fixed path configuration relative to the calling handler. But does this prevent you to successfully use require modules? E.g. Shim could be simulated with a little “window.L = L” in your callback.

        (0) 
        1. Mike Howles

          The problem is that we might need to change the internal functionality of sap.zen.Dispatcher.require, e.g. in 1.5. So cloning it will cause incompatibility.

          To be fair, that risk is already happening to us from going from 1.4 and 1.5 as you’ve described here: What’s new in Design Studio 1.5 SDK under ‘Incompatible Changes’, regardless if we use suggested workaround or non-suggested workarounds.


          Therefore, it’s a point of consideration/risk I’m willing to accept in order to fully make use of RequireJS with what we know in present and what might or might not happen in future 🙂


          Regarding use of shims, perhaps in Martin’s Leaflet example where window.L (the Leaflet global export), this is ok.  In other cases, such as loading a more recent version of D3 in order to not conflict with CVOM D3, I wouldn’t want to use the export option to attach to global scope, therefore I use shims or other require approaches.  (And I’m still learning all the options.)


          Also, I’m not sure if sap.zen.Dispatcher require allows for external/CDN URL paths or path fallbacks.


          (0) 
          1. Martin Pankraz Post author

            Thanks Reiner and Mike for sharing your thoughts on this important matter.

            I will use the sap.zen.Dispatcher.instance.require method wherever possible but leverage our sdk community’s approach if necessary.

            Regards

            Martin

            (0) 
    2. Vincent Dechandon

      Hey Reiner, this blog is quite old.
      Still, I’m migrating Galigeo extension for DS under require.

      I’ve tried to simply implement the snippet you gave here : https://archive.sap.com/documents/docs/DOC-58755
      by wrapping the call to subclass in a require callback (I’ve used sap.zen.Dispatcher.instance.require instance).

      However, this works fine in Design Mode, but once I launch the app in my browser (runtime), I never reach the callback registered in subclass, where all my app behaviour is.

      sap.zen.Dispatcher.instance.require([utilUrl + "loader.js", utilUrl + "queue-processor.js"], function(LoaderFactory, QueueFactory) {
          sap.designstudio.sdk.Component.subclass("com.galigeo.ds.extension.Map", function() {
               // this is never reached at runtime 
          });
      });

      Any idea on that?

      Wrapping the call to require in the this.init is something I want to avoid.

      Br,
      Vincent

      (0) 

Leave a Reply