Dynamic JavaScript in MODX IV

Asking MODX to generate the URL for our dynamic JavaScript


In this short series of articles, we're looking at some ways to use dynamic JavaScript in MODX. In this one, we'll see a better method for getting the file extension for the JavaScript url: setting.

MODX logo

Review

As we said in the previous articles, 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. Out use case here is fixing 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 in an Ajax call to a connector resource that calls various processors:

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 build-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.

The problem is with 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.


Asking MODX for the URL

As we did in the previous article, we put a snippet on the MessageManager page that inserts the appropriate JavaScript code into the head section of the page. We'll still use a chunk with a placeholder for the url variable. In this one, however, we're going to get the actual URL of the mm-ajax page and use that in the JS code.

This requires a slight change in our mmAjaxJS chunk. Instead of using a placeholder for the extension, we'll use it for the entire URL. We'll also change the name of the placeholder to reflect its new role. Here's the chunk:

<script type="text/javascript">
function mmAjax(id, action, dataIn) {
    dataIn = dataIn || {};
    dataIn['id'] = id;
    dataIn['action'] = action;

    /* Ajax call to action; calls MODX resource pseudo-connector */
    return $.ajax({
        type: "POST",
        url: "[[+file_url]]",
        data: dataIn,
        dataType: "json"

    }).done(function () {
        mmSpinner.stop();
    }).fail(function (jqXHR, textStatus) {
        mmSpinner.stop();
        pop.setText(action + ' failed on message ' + id + ' ' + textStatus);
        pop.load(40);
    });
}
</script>

Our snippet tag on the MessageManager page looks like it did in the previous article, but with a property to specify the name of the chunk holding the JS code:

[[!MessageManagerJS? &jsChunk=`mmAjaxJs`]]

The MessageManagerJS Snippet

Our snippet no longer has to get the Content Type object to check it's file extension. Instead, we'll get the mm-ajax resource using its alias. Then, we'll get it's ID, and ask MODX to generate its URL for us.

Here's the new snippet code:


$doc = $modx->getObject('modResource', array('alias' => 'mm-ajax'));
if (! $doc) {
   $modx->log(modX::LOG_LEVEL_ERROR, "Could not get resource with alias 'mm-ajax'");
   return '';
}

$docId = $doc->get('id');

$url = $modx->makeUrl($docId, "", "", "full");

/* Create the array of fields for the placeholder */

$fields = array(
    'file_url' => $url,
);

/* Get the name of the chunk holding the JS code */
$jsChunk = $modx->getOption('jsChunk', $scriptProperties, 'mmAjaxJs', true);

/* Get the JS code with the placeholder replaced */
$jsCode = $modx->getChunk($jsChunk, $fields);

/* Inject the JS code into the page */
$modx->regClientStartupScript($jsCode);
return '';


Wrapping Up

This solution is much better than the ones in the previous articles. It's pretty much guaranteed to use the correct URL in the JavaScript code. It still separates the PHP code and the JS code, making both of them much easier to maintain.

There is one potential issue. If the mm-ajax page is in a different context, makeUrl() will fail if the current user doesn't have at least load permission for that context in the front end. In some cases, they may also need list and view permission. If you need to make a change to give the user the permissions, be sure to flush permissions and log off all users before testing. Note that the script tags at the top and bottom of the chunk are necessary. This is not the case if you reference a JavaScript file in the head section of your template with code like this:

<script type="text/javascript" src="path/to/js/file.js"></script>

I should mention that in some cases, you might not need a snippet. If the placeholders in the JS code all refer to settings, resource content fields, or TVs, and you put the JS code directly in the resource content field or the template, surrounded by script tags, just using the placeholders in the JS code will be enough. MODX will replace them when the page is rendered. That wouldn't work in this case because there's no built-in placeholder to use.


Coming Up

In the next article, we'll look at adding the rel="noopener" attribute to link tags.


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)