Common mistakes when writing Aria Templates applications (part 1)

Aria Templates framework empowers users to write modular and maintainable code, but like each technology, it has a couple of tricky parts where it’s easy to make certain classes of errors during the coding.

In this article, we’ll have a look at some of the mistakes that are likely to be done by a developer learning to use the framework, and we’ll try to explain the sources of confusion so that hopefully you won’t hit those problems. The initial part of this article is pretty basic, but the later part assumes some knowledge of the framework.

But actually, it’s sometimes best to break things deliberately, to be able to quickly recover from issues in the future. Let’s go.

 

Table of contents

  • JavaScript WAT
    Introduction to corner cases of JavaScript
  • Aria Templates: Widgets and sections used as “containers”
    The old syntax (removed in 1.5.1) of widgets which degrades performances and may lead to bugs – beware!
  • Data binding not working due to not using $json.setValue
  • Data binding not working due to misusing $json.setValue
  • Bulk update of data with $json.setValue
  • Confusing identifiers and strings
  • Loading initial template in HEAD instead of BODY
  • Relying on undeclared dependencies

 

JavaScript WAT

JavaScript, while powerful, is known to have its quirks. Chances are you’ve seen the entertaining WAT video, if not, it’s worth sacrificing 5 minutes to watch it.

Since AT is based on JavaScript, you may encounter a number of unintuitive (if you’re coming e.g. from Java background) behaviors in AT stemming from that fact. It’s highly recommended to learn about JavaScript quirks, reasons behind then, and how not to step upon them. JavaScript Garden is a very good resource for taking your JavaScript knowledge to the next level – learning about intricacies of scopes, closures, `this` and other basic language ingredients. You’ll learn why you shouldn’t define functions inside a for-loop, and why `this` is sometimes undefined in your methods.

 

Aria Templates: widgets and sections used as “containers”

For a warm up, let’s talk about the feature that was removed from framework in AT 1.5.1, but you may still hit it when you’re stuck with AT 1.4.17

In old version of Aria Templates, certain widgets, and section statement, were possible to use via so-called “container” syntax, i.e.

{widget} some code here {/widget}

An alternative was the self-closing syntax which looks more or less like this:

{widget {macro: "myMacro"} /}

The first syntax lead to many difficult to track problems, and was deprecated and removed.

For example what can go wrong, let’s have a look at this instant (it works only with AT <=1.4.x). In this example, we only declare some text links with corresponding tooltips (one using old syntax, one with the new syntax), and increment a variable in the main macro. As it turns out, in the old syntax, every time we hover over a link, we instantiate the tooltip from scratch, which reexecutes the main macro each time – clearly something that is counterintuitive and may lead to bugs.

If you want to read more about the topic, you can head to the blog article about deprecating section as a container.

Takeway: Make sure you’re not using that syntax anymore, and convert your code as soon as possible. Take time now to get rid of it now to save time and avoid painful debugging.

 

Data binding not working due to not using $json.setValue

Let’s suppose we have a data model entry this.data.counter which we want to increment on a button click in this example.

A naive attempt would be to call simply

this.data.counter++

This will indeed increase the value in the data model, but won’t refresh the widgets that are bound to (observing the changes in) that data model entry.

A solution is to use a function call:

this.$json.setValue(this.data, "counter", this.data.counter + 1);

This function call changes the data model entry, but also notifies all the listeners so they can be refreshed. Note that the first parameter is the reference to an object (holder of a property), and the second parameter is a string containing the name of the property to be changed in the holder.

In template script, this.$json is a shortcut to aria.utils.Json class which comes with a couple of other methods for dealing with data model objects and arrays.

 

Data binding not working due to misusing $json.setValue

Now you may think: I will remember to always use setValue and things will work properly. That’s a prerequisite, but it’s not enough.

To understand this example, it’s crucial to see the difference between primitive values and objects in JS. Let’s open your favorite dev tools and try some comparisons:

  1 === 1     // true, two equal primitive types
  [] === []   // false, two different array objects

When we assign an object variable to another variable, then both variables hold the same reference and point to the exactly same object:

  a = {value:7}; b = a; b.value = 42;
  a.value // 42
  b.value // 42

Finally, if we store in a variable (x) a reference to an object which is a part of other object (data), and later replace that key, the stored reference (x) still points to the old object:

  data = {myContainer : {magicValue : 7}}
  x = data.myContainer
  x                                         // { magicValue: 7 }
  data.myContainer = {magicValue : 42}
  data.myContainer                          // { magicValue: 42 }
  x                                         // { magicValue: 7 }

So, inside data, myContainer now points to some newly created object, while all the references to data.myContainer obtained earlier still point to the old object.

Knowing that, let’s consider another instant with the following tpl script methods:

  updateModelBad: function() {
      var c = this.data.myContainer;
      c.magicValue = 42;
      c.otherValue = 888;
      this.$json.setValue(this.data, "myContainer", c);
  },
  updateModelStillBad: function() {
      var c = {
          magicValue: 42,
          otherValue : 888
      };
      this.$json.setValue(this.data, "myContainer", c);
  },
  updateModelBetter: function() {
      this.$json.setValue(this.data.myContainer, "magicValue", 42);
      this.$json.setValue(this.data.myContainer, "otherValue", 888);
  }

and the widget in TPL like this:

    {@aria:Text {
        bind : {
           text : {
             inside: this.data.myContainer,
             to: "magicValue"
            }
        }
    }/}

The first two methods want to do some changes to the container in the standard JS way, and update the container using setValue but unfortunately this doesn’t work as one might expect.

The first method actually tries to update the reference to the container to its current value which is effectively a NOOP (we changed some properties of the object earlier, but without using setValue, so framework is not aware of them).

The second method entirely replaces the container object in the data model, and since the text widget was earlier bound to it, it continues to observe changes in the old JS object, instead of the new one. We can’t replace the holder object like that.

The last method correctly mutates the properties of the holder object, without replacing the object itself. This is the recommended solution.

 

Bulk update of data with $json.setValue

In the previous example, we updated the properties in our object one-by-one, saying that we shouldn’t replace the holder object, but mutate the properties of it. Of course, sometimes it would be a pain to do that – let’s say we retrieve a big JSON object from server and have to update the data model with its new contents.

In that scenario (see example), we may wrap our widget in a section which uses bindRefreshTo configuration.

Let’s consider the following data model:

{
  "myContainer": {
    "myData": {
      "a": 0, "b": 0, "c": 0
    }
  }
}

We receive a new JSON object (for simplicity, it’s hardcoded), and set it in the data model:

  updateModel: function() {
      var newData = {
          a: 1,
          b: 2,
          c: 3
      }
      this.$json.setValue(this.data.myContainer, "myData", newData);
  }

The text widget is bound to b, but this is not enough, as demonstrated in the previous example – myData is replaced by us, and the widget is still watching the old myData object:

    {@aria:Text {
        bind : {
           text : {
             inside: this.data.myContainer.myData,
             to: "b"
            }
        }
    }/}

The solution is to wrap the widget in a section that observes changes in this.data.myContainer, so that when myData is replaced with a new object, it can recreate the Text widget:

  {section {
      macro: "printText",
      bindRefreshTo : [{
         inside: this.data,
         to: 'myContainer'
      }]
   } /}

 

Confusing identifiers and strings

Let’s have a look at the very simple example:

{macro main()}
    magicValue is:
    {@aria:Text {
        bind : {
           text : {
             inside: this.data,
             to: magicValue
            }
        }
    }/}
{/macro}

What’s wrong here? As in signature of $json.setValue, the holder (inside) should be a reference to JS object (good), but the to field should be a JS string containing the name of the property, while it’s an identifier – reference to magicValue variable. Change this to "magicValue" string and the sample will be working.

This is probably one of the easiest mistakes to do in Aria Templates. Note that also when specifying a macro of a section, you should also pass a string, not an identifier.

As a rule of thumb:

  • when you reference an object, you have to pass the valid JS identifier / reference
  • when you reference a property or macro name, you have to pass a string

 

Loading initial template in HEAD instead of BODY

And now, something completely different – let’s step out of bindings and think a bit about loading the classes and dependencies.

In our samples we usually load framework in the HEAD of the page. It might be tempting to also start your application there, but it can lead to errors:

// html..
// head..
<script type="text/javascript" src="ariatemplates-1.6.1.js"></script>
<script>
  Aria.loadTemplate({
   classpath: "myapp.Main",
   div: "myAppDiv"
  }, function () {
    ...
  })
</script>
// /head

<body>
  <div id="myAppDiv"></div>
</body>

At the time of Aria.loadTemplate call, the framework is already loaded, and we want to load the intial template into a div with id “myAppDiv”. However, at this stage of parsing the HTML document, that DIV will not exist, and in some browsers even BODY element itself might not exist!

For that reason, you should avoid calling any AT methods from within HEAD, since some of them may require DOM interaction at the time when BODY element is not yet constructed. The best practice is to create a root DIV inside BODY, and only after that call Aria.loadTemplate.

 

Relying on undeclared dependencies

One very common issue is the reliance on undeclared dependencies in the templates. The way how Aria Templates caches depdendencies doesn’t help to avoid this trap.

Let’s consider the following TPL:

  {Template {
    $classpath: 'my.app.MyTemplate',
    $css : ['skins.'+aria.widgets.AriaSkinInterface.getSkinName()+'.foo.bar'],
  } }

We call a method of AriaSkinInterface class, but do not declare the $dependency on it.

Chances are this code will work properly inside the application. If some template loads AriaSkinInterface earlier, then it will be stored in the global cache of Aria Templates. Next time any template requires it, it will be retrieved from the cache, regardless if the other template specified that dependency.

However, if the same template is then loaded in isolation (for instance, in a test case through attester), it fails to resolve aria.widgets.AriaSkinInterface because it doesn’t know about that dependency.

Note that in this particular example, you should be good to go with aria.widgets.AriaSkin.skinName which should be exposed by the AT skin once you include it in your page – because the skin has by convention a classpath of aria.widgets.AriaSkin. However, aria.widgets.AriaSkinInterface is just a utility class, not a part of the AT core, so it’s mandatory to declare that dependency explicitly if you really want to use it.

Takeaway: always explicitly declare your dependencies to avoid surprises. It’s also a good idea to explicitly declare dependencies on core classes of the framework.

 

Stay tuned

This was a introductory article about some basic mistakes. Soon, we’ll dig into some more complex sources of bugs – stay tuned.

In the meantime, it might be a good time to open your favorite dev tools, and track down the Aria Templates warnings if you have any, and try to fix your code to ensure future-proofness of your application. Those warnings are logged for a good reason and indicate the usage of methods or syntax that is known to lead to bugs and confusion.

Finally, first things first. If your console is red of errors, try to fix them in the order of appearance. An error early in the application lifecycle may cascade and break other things later on.

Leave a Reply

Your email address will not be published. Required fields are marked *