Blog

Demystified: Mura.js, Now and Then()

January 23, 2018 by Grant Shepert

No journey into demystifying Mura's newest and most powerful technologies would be complete without taking a look at Mura.js.

JavaScript development is becoming ever more prevalent in the modern website infrastructure, and working with frameworks like Ember, React and Vue (just to name one of the scores available) demands the presence of a robust server-side interface. While the JSON API does provide a great starting point, there are some key items that won't be found there, such as session persistence & permissions. (Note, session-based permissions are available through the JSON API via cookies, but are more complex to set up.)

Because JavaScript is the new everything

Enter Mura.js, the Javascript Library we created for the Mura ecosystem. If you've used jQuery before, you'll understand a little bit of what Mura.js is, but let's get into the bigger details of what it means for you.

We developed Mura.js for several reasons.

First, Mura often lives in the midst of complex IT infrastructures and legacy applications. In order to ease integration with these systems, we have to ensure that we don't add complexity by introducing things like third-party libraries which might not be compatible. The closer to "vanilla" we can make Mura, the better it is for our community.

Secondly, by building our own library, we are able to introduce Mura-specific functionality to the mix. Mura.js offers a familiar set of jQuery-like DOM selectors and related tools, but also integrates API communication, iterators, user actions, permissions and more.

Plug and play, anywhere

Another very powerful aspect of the Mura.js library is that it can be installed and used remotely! This makes functionality like single sign-on and remote reuse of content on different (or completely vanilla html) platforms very straightforward.

When you are developing inside of Mura, such as within a theme template or display object, Mura.js is always available. Outside of Mura, say on a static HTML web page where you want to pull content into a Vue.js application, you need to install (or "reference") the Mura.js codebase.

In these cases you can reference the minified Mura.js file from within your Mura instance like so:

  <script src="https://[domain.com]/core/vendor/mura.js/dist/mura.min.js"></script>

(For Mura 7.0 or earlier, the location differs slightly: <script src="https://[domain.com]/[siteid]/js/dist/mura.min.js"></script>.)

Further integration details can be found in the Mura.js project on Github, including instructions for packaging and usage with technologies like AMD and Node.js.

Example: invoking a Mura ORM object

Here is a simple example of how you can call a custom Mura ORM object via Mura.js:

Mura
    .getEntity(entityname)
    .new()
    .then(
        //resolve
        function(entity) {
            // fun with the response
        },
        //reject
        function(entity) {
            // entity with errors
        }
);

Then(): a bit about promises

If you have been following the latest JavaScript trends, you probably already know at least a little about JavaScript "promises", which simplify the complexity of asynchronous requests, such as requesting data from a remote source before an application can continue. A promise involves passing a function that includes both resolve and reject arguments. When the remote asynchronous action completes, it either calls the "resolve" function in a successful transaction, or "reject" when it fails.

Mura.js relies heavily on the use of promises to ensure that you can control the flow of your application synchronously even when asynchronous calls are involved. In the above example, we initiate the promise by calling ".then()" and pass it our two resolve/reject functions. These functions are not called until the remote, asynchronous request completes and it triggers one (and only one) of the passed resolve/reject functions.

The wonderful thing about this model is it allows you to chain requests in a synchronous fashion. For instance:

 Mura
   .getEntity(entityname)
   .new()
   .then(
       //resolve
       function(entity) {
             entity.get('properties').then(
            //resolve
            function(properties) {             
            },
            //reject...
       },
       //reject...
);

Putting it to use

Let's look at a practical example:

 <script src="https://[domain.com]/core/vendor/mura.js/dist/mura.min.js"></script>

<script>
Mura.init(
 {
   siteid:'default',
   rootpath:'http://[domain.com]'
 }
);

Mura(function(Mura){
 Mura.addEventHandler(
   {
     asyncObjectRendered:function(event){
       alert(this.innerHTML);
     }
   }
 );

 Mura.getEntity('content').loadBy('contentid','D3563327-BA21-CAA2-AC1E6AA4DEB1B41A')
   .then(
   // resolve
   function(item){
     console.log("The title of this blog post is: " + item.get('title'));
     },
   //reject
   function(item) {
       console.log('there was an error');
   }
 );

});

</script>

This example is pretty basic, it simply posts the title of a page in the Browser console, courtesy of Mura.js.
(Note, this syntax requires Mura 7.1, currently in release candidate status.)

Once you have downloaded and installed the mura.js package (GitHub) locally, you will be able to run requests like the one above (edited to match your domain, and with a valid contentid) even on an html page!

Maker magic

What can I make with this, you might ask yourself? Almost anything, is the answer.

Mura.js is the client-side, JavaScript-powered version of what you have been doing server-side. There are a few caveats you will have to watch out for (getting used to Promises and/or Asynchronous application development) but overall you will find these kinds of applications very rewarding to develop. You get the benefit of client-side state management, and can leverage a wider variety of programming frameworks like Ember, React or Vue.js, all the while remaining within the standard Mura permission space. Better still, because we have unhooked ourselves from the server, you can place these applications outside of Mura completely (such as in a Node application).

Build single-page, multi-panel applications that use Mura as the content repository, or completely custom applications that connect to your own Mura ORM objects. The possibilities really are endless.