Friday, October 29, 2010

Why do I see output when I call put() on a HashMap in Velocity?

Consider the following Velocity code:
#set($myMap = $contents.getEmptyMap())
$myMap.put("first", "first value")
$myMap.put("second", "second value")
$myMap.put("third", "third value")

<p>$myMap.first</p>
<p>$myMap.second</p>
<p>$myMap.third</p>
The output of that is as below.

$myMap.put("first", "first value") $myMap.put("second", "second value") $myMap.put("third", "third value")

first value

second value

third value
The problem is that put() returns something (it's return type is not void) and Velocity is trying to evaluate it somehow. The fix is easy enough though. Use a dummy variable to catch the return value.

#set($myMap = $contents.getEmptyMap())
#set($dontOutput = $myMap.put("first", "first value"))
#set($dontOutput = $myMap.put("second", "second value"))
#set($dontOutput = $myMap.put("third", "third value"))

Thursday, October 28, 2010

Don't confuse jQuery and Velocity Variable Names

Update. 6/02/2011 1:12:38 PM. Putting Javascript in a separate file is the best way to make sure jQuery and Velocity variables are not confused.

In Velocity, variables are denoted by the dollar sign ($), so when using normal notation, a velocity variable will appear as $someVar. This is tricky if you are writing jQuery code in the same file, because by convention you use the dollar sign to denote jQuery variables as well. For example, see below.

## Refers to a Velocity variable.
$emailAddress = "noreply@example.com"

// Refers to the jQuery object (that wraps a dom object).
var $email = $("#email");
// Refers to a dom object itself
var email_field = $("#email").get(0);

Most often, this is not an issue. The server (and thus Velocity) evaluates a script first and then passes the HTML (including jQuery code) to the browser. In the first stage, Velocity will pick up and evaluate all Velocity variable names i.e. things beginning with a dolllar sign, such as $email. If it finds no value (empty string or null) for that variable, it echos out the name as is, leaving the token $email intact for Javascript to evaluate.

If you do find names are clashing, you can fix this using any of these ways, listed in order of my personal preference.

  1. Put the Javascript into an external .js file. This way the Velocity engine will never even look at the file, and you get the benefit of separation of concerns (HTML being separate from Javascript) and caching (browsers caches Javascript file so it doesn't have to downloaded again every time the page is accessed).
  2. Use different names! The example above will not create an issue because the names are all different: $emailAddress vs $email vs email_field.
  3. Use different characters to denote a jQuery object, e.g. _email = $("#email") as per this excellent article on Authentic Society (The Learning Website): JavaScript Dollar Sign ($) - What is it for?
  4. Use silent notation for Velocity variables, or least those Velocity variables you know or think will be confused with jQuery variables. E.g. $!email. When using silent notation, Velocity will output nothing in place of the variable name if it finds no value (empty string or null) for that variable - leaving nothing to confuse Javascript.
  5. Use formal notation for Velocity variables, or least those Velocity variables you know or think will be confused with jQuery variables. E.g. ${email}. Just as in Bash scripting, the curly braces help the Velocity script engine to discriminate Velocity variables from other text. When using formal notation, Velocity will output tha name as is, leaving the token (e.g. ${email}) intact - which will not confuse Javascript because you will be using $email for the jQuery variable.

It is important to note that in jQuery code, the dollar sign is not just used to denote jQuery variables, but it is also used as an alias for the jQuery class itself, and that in these instances you can use the token jQuery instead. For example, var $email = $("#email") and var $email = jQuery("#email") are the same.

Wednesday, October 27, 2010

Optimising jQuery Selectors

Is your jQuery slow? Is there a noticeable delay when your jQuery effect runs? Check your selectors. jQuery has made Javascript so much easier to write, but easier to get some things wrong too - particularly through over-use of selectors whereby a piece of code spends too much time scanning the DOM. Here are some techniques to help optimise your use of selectors in your jQuery code.

  1. Select by ID wherever possible - use the speed hierarchy.
  2. Don't repeat yourself (DRY). Store results of a selector in a variable if you need to refer to it again.

Select by ID

When you select by ID ($("#some-id")) jQuery will use the "native Javascript" call getElementById() which is very fast and will stop scanning after finding the first result. [3]

If you cannot select an element by ID (perhaps because you are looking for multiple elements), can you narrow down the search by selecting a parent element by ID? jQuery will have to scan for the parent but has a smaller search to execute for the child. For example, $("#some-form-id .mandatory") will be faster than $(".mandatory"). Why? Because the $(".mandatory") selector forces jQuery to visit every single element in the DOM to find all elements with the class "password", whereas the $("#some-form-id .mandatory") narrows down that search to children of the element with ID "some-form-id".

Don't Repeat Yourself

If you find yourself referring to the same element mutliple times using the same jQuery selector, try to turn it into a variable instead. Keep in mind that each time you use a jQuery selector, you are running a search command. So, instead of having multiple instances of $("#some-id") in your code, have one $someElement = $("#some-id") and multiple instances of $someElement.

On a side note, what is the significance of the dollar sign in the variable name $someElement? Is it some supercool jQuery magic? No! $someElement and someElement are both valid JavaScript variable names, and the dollar sign isn't some special operator - it is denoting that the object being referred to was created by jQuery. This is important because the jQuery object you get back from $("#some-id") is different to the DOM element you get back from document.getElementById("some-id"): the former has a "jQuery wrapper".

The Speed Hierarchy

In general, everything revolves around the fact that Javascript/jQuery scans through the DOM every time you ask it to look for something, so you want to choose selectors that make that scanning job fast and small.

The "speed hierarchy" goes like this, in order of fastest to slowest [1]:

  1. ID: $("#some-id")
  2. Element: $("div")
  3. Class: $(".some-class")

As mentioned before, when you select by ID ($("#some-id")) jQuery will use the "native Javascript" call getElementById() which is very fast and will stop scanning after finding the first result. [3]

Selecting by element (tag name) is next fastest because it uses the native call getElementsByTagName() to narrow down the search. For example, searching for $("div .password") will limit the search for element with class "password" to all DIV elements (retrieved by getElementsByTagName()).

Selecting by class is slowest because, as mentioned above, a search like that ($(".mandatory")) forces jQuery to visit every single element in the DOM to find all elements with the class "password".

$= means "ends with". But... even faster than $("#some-id") is $("div[id$=some-id]"). It is faster because div in $("div[id$=some-id]") is telling jQuery to use ignore elements that aren't DIVs and $= in $("div[id$=some-id]") is the "single selector" operator, telling jQuery to stop looking after it's found the first match.

Resources

Sites that helped me with this page.

Friday, October 22, 2010

Multi-line strings in Velocity

Just noticed you can use multi-line strings in Velocity strings to maintain maximum readability. For example:

#getSQLResults("select distinct(t.child) as identifier, c.text1 as title, c.text_area1 as summary
      from tree t, inode i, contentlet c
      where c.live = true
          and t.child = i.identifier
          and i.inode = c.inode
          and (relation_type = 'Gen_Prod_Overview_Tab-Overview_Tab_Articles'
          or t.relation_type = 'Gen_Prod_Specif_Tab-Specif_Tab_Articles'
          or t.relation_type = 'App_Ov_Tab-Ov_Tab_Articles'
          or t.relation_type = 'App_Specif_Tab-Specif_Tab_Articles'
          or t.relation_type = 'App_Key_Cons_Tab-Key_Consideration_Articles'
          or t.relation_type = 'App_Install_Tab-Install_Tab_Articles'
          or t.relation_type = 'App_Finishes_Tab-Finishes_Tab_Articles'
          or t.relation_type = 'App_Supp_Tab-Supp_Tab_Articles')
      order by c.text1
      offset $offset
      limit $resultsPerPage;" )

My dotCMS notes.

Monday, October 18, 2010

Left side (xx) of 'y' operation has null value. Operation not possible

I have been seeing a lot of messages like this in my dotCMS logs:
[18/10/10 02:19:45:045 EST] ERROR app.VelocityEngine: Left side ($queryType) of '==' operation has null value. Operation not possible. /usr/local/dotcms/webap
ps/../dotCMS/assets/1/4/14777.vtl [line 5, column 25]

This results from code such as:
#if ($queryType == "$expectedValue" )

I thought this was strange because the macro this code belonged to wasn't even being executed - so the variable never gets a value anyway. It turns out the fix is to quote the variable, as per below.
#if ("$queryType" == "$expectedValue" )

That's fine for string values, but won't stop an error on anything involved in a numerical operation.
#if ($totalResults > 0 )

The above code results in this error:

[18/10/10 02:46:43:043 EST] ERROR app.VelocityEngine: Left side ($totalResults) of '>' operation has null value. Operation not possible. /usr/local/dotcms/webap
ps/../dotCMS/assets/1/4/14777.vtl [line 1, column 24]

The fix is to make sure it is a number under any circumstances.

#if (!$UtilMethods.isSet($totalResults))
   #set($totalResults = 0)
#end
#if ($totalResults > 0 )


It seems Velocity is a bit like bash or DOS shell scripts in this respect; unless you quote a variable name, it will see null as the value and output an error.

My dotCMS notes.

Wednesday, October 13, 2010

Remove duplicates in a list using Velocity code only

Updates: 25/10/2010. Thanks to Christopher F. Falzone in the post Compound relationship query for a better way to create an empty map.

In dotCMS, I faced a situation where I had a list in Velocity code that contained duplicates. It contained duplicates because I had to traverse a transitive relationship to build a list of objects related to some source object through two separate relationships.

Here is how I removed the duplicates - via a map.

## Remove duplicates.
#set($publishersMap = $contents.getEmptyMap()) ## Make a map to remove dupes.
#foreach($publisher in $publishers)
   #set($dontOutput = $publishersMap.put("$publisher.name", $publisher))
#end
#set($dontOutput = $publishers.clear())
#set($dontOutput = $publishers.addAll($publishersMap.values()))

## Sort.
#set($list = $sorter.sort($publishers, "name"))

Essentially, I am adding all elements from the list to a map (because a map ignores duplicates). Then I clear the original list and add all the elements (values, not keys or entries) back to the original list.

My dotCMS notes.

Friday, October 08, 2010

Simplest dotCMS Detail Page using Velocity

Update: 27/10/2010 Used $webapi.getIdentifierInode($id) to ensure that we have identifier, irrespective of whether our request parameter was an identifier or inode.

Simplest way to create a dotCMS detail page in Velocity. All you need is the item's identifier or inode. Assume identifier is stored as id in the request.

#if(!$UtilMethods.isSet($request.getParameter("id")))
   <p> Nothing to fetch </p>
#else
   #set($id = $request.getParameter("id"))
   #set($id = $webapi.getIdentifierInode($id)) ## Get identifier from inode or identifier
   #getContentMapDetailByIdentifier($id)
   #foreach($entry in $content.entrySet())
      <p><b>$entry.key</b>: $entry.value</p>
   #end
#end

This produces a non-sorted list of attribute name/value pairs. For a more complicated detail page (which is sorted), try out this code sample by Chris Falzone, which he referred me to in this dotCMS Yahoo post: loop through all fields in a node?

You can use #getContentMapDetailByIdentifier() or #getContentMapDetail() - they seem to do pretty much the same thing. It is most important to use a method that relies on identifier rather than inode (like #pullContent()) for lookup because users might bookmark the detail page and inode changes with each revision. The #getContent() and #pullContent() macros can use either the identifier or any of the inode values - they are smart enough to always look up the latest content (but they don't return a handy map though).

One more thing: right now, I can find no concrete documentation on either #getContentMapDetailByIdentifier() or #getContentMapDetail(), just a couple of posts that reference those methods. I had to dig into dotCMS vm files (dotCMS 1.7). Both of these macros are defined in $DOTCMS_HOME/dotCMS/WEB-INF/velocity/VM_global_library.vm.

My dotCMS notes.

Don't swallow IOException from OutputStream.close().

If OutputStream throws IOException on close then probably the whole write failed and left the data in a buffer i.e., nothing happened. So don't swallow this exception when you close an OutputStream.

From the Java Posse #299 Devoxx 09 - Project Lombok Interview, 18 mins 10 seconds in.

This is a good reason to support Joshua Bloch's ARM Block proposal.