Dynamic JavaScript in MODX I

Methods for creating dynamic JavaScript in MODX


In this short series of articles, we'll look at some ways to use dynamic JavaScript in MODX. We'll start by discussing what we mean by "dynamic JavaScript, then look at a very simple method for implementing it. In the next articles, we'll see better ways to do it.

MODX logo

Dynamic JavaScript

Dynamic JavaScript, as we're using the term here, describes the situation where the JavaScript (JS) code that ends up on the page needs to change depending on conditions that are not available to the JavaScript code itself. In other words, we want to alter the JS code placed on the page dynamically based on the conditions tested in a snippet or plugin.


The Problem

The need for Dynamic JavaScript arises from the fact that the JavaScript code is executing locally on the user's browser (client-side) and the MODX snippet or plugin is executing on the server (server-side). At run-time, there's no way for the two to communicate without creating and handling an Ajax call to a processor that provides the information the JS code needs. I recently faced this problem when dealing with a bug in the MessageManager Extra. MessageManager provides the same services as the messaging system in the MODX Manager, but in the front end.

The MessageManager JavaScript code contained this line, calling a processor:

url: "mm-ajax.html",

This code is part of a JS function that launches a resource with the alias, mm-ajax, containing a snippet that serves as a connector to perform various actions by calling one of several MODX built-in processors and return their results. Depending on the action specified in the JS call, the snippet returns a list of user messages, a list of users, or a list of user groups. It also handles creating new messages, marking messages as read or unread, and deleting single messages or groups of messages.

You may have already noticed the problem with this line of code (though I didn't, until it crashed). It's the .html part. One of the users of MessageManager changed the HTML Content Type so that the "file extensions" field was / instead of .html. The URL in the JS code was no long valid and the page containing the connector snippet was not found.

Obviously, the URL in the JS function needed to change based on the file_extensions field of the HTML Content Type, but how? The JavaScript code has no direct access to the information held on the server. We could use Ajax to call another processor first that returned the correct location of the mm-ajax resource, but that's ridiculously inefficient.


One Solution

As I'm sure you know, in MODX, there are always lots of ways to perform a particular task. This solution has some serious problems, and it's wrong for this use case, but it could work in specific cases and it's very simple. It involves using two slightly different copies of the JS file and loading the correct one.

First, we create two copies of the JS file and call them mm-js-short.js and mm-js-long.js. The first one contains this line:

url: "mm-ajax/",

The second one has this line:

url: "mm-ajax.html",

There are several ways to implement this solution. One is to make the path to the correct JS file a property in a snippet that runs on the MessageManager page (not the mm-ajax page). Let's call it &messageManagerJsPath.

We'd use a tag like this on the page:

[[!MessageManagerJS? &messageManagerJsPath=`path/to/correct/JS/file.js`]]

The code in the MessageManagerJS snippet would look like this:

$jsPath = $modx->getOption('messageManagerJsPath', $scriptProperties, 'path/to/mm-js-short.js', true);
&modx->regClientStartupScript($jsPath);
return'';

Another method would be to let the snippet detect which file to load.

In a snippet that runs on the MessageManager page (not the mm-ajax page), we put this code:

/* Get the Content Type object */
$contentType = $modx->getObject('modContentType', array('mime_type' => 'text/html'));

/* Get the file extension */
$extension = $contentType->get('file_extensions');
if ($extension === '/') {
    $path = 'path/to/mm-js-short.js';
} else {
   $path = 'path/to/mm-js-long.js';
}

$modx->regClientStartupScript($path);
return '';

What's Wrong with these Methods?

Let's face it, these are pretty terrible solutions. They assume that there are only two possible extensions for the mm-ajax page. If the user decides that the extension should be .htm instead of .html, everything breaks (not to mention the case where the mm-ajax page is a container and the container_suffix setting comes into play). Worse yet, you've got multiple JS files to maintain and if you need changes, you'll have to modify (and test) both of them.

Still, this solution might work in specific cases where the JS files are significantly different and you're absolutely sure of the number of possible options. In the next article, we'll see a much more reliable way to handle this problem.


Coming Up

In the next article, we'll solve the problem by dynamically creating the JS code before injecting it into the page.


Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)



Comments (0)


Please login to comment.

  (Login)