Change behavior of {$foo} when $foo is null or undefined?

2,261 views
Skip to first unread message

bolinfest

unread,
Jan 12, 2011, 1:33:57 PM1/12/11
to Closure Templates Discuss
I'm not sure about you, but I have ended up with unfortunate scenarios
in which "null" or "undefined" is printed in the HTML generated by my
Soy template because instead of doing:

<input value="{if $foo}{$foo}{/if}">

I'm lazy and just do:

<input value="{$foo}">

and hope I never pass a null value for $foo.

I think that there are three viable changes:

(1) When $foo is null (or undefined) and is passed to the print
function, print the empty string instead of 'null' or 'undefined', as
appropriate.
(2) Same as (1), but only perform that behavior when $foo is specified
as @param? instead of @param.
(3) Introduce a print directive named "empty" so that {$foo|empty}
will print $foo if $foo != null; otherwise, it will print the empty
string.

(1) is not backwards compatible, though I think it is still a good
change. I imagine very few users actually pass a null value for $foo
so they can print the string "null." My other concern is that it will
make the generated JS larger because instead of:

soy.$$escapeHtml(opt_data.foo)

it will need to be:

soy.$$escapeHtml(opt_data.foo == null ? '' : opt_data.foo)

though I suppose to keep code size down, a new function could be
created:

soy.$$nullSafeEscapeHtml = function(arg) {
return soy.$$escapeHtml(arg == null ? '' : arg);
};

and then the Soy print function would become the following when
translated to JS:

soy.$$nullSafeEscapeHtml(opt_data.foo)

admittedly, this would add a bunch of runtime ternary checks to the
generated JS.

(2) may be better as it will break backwards compatibility in fewer
places and introduce fewer additional runtime checks.

(3) is probably the safest, but won't fix any errors automatically and
may create new work for those using templates.

What do people think?
Michael

Mike Samuel

unread,
Jan 12, 2011, 2:05:08 PM1/12/11
to closure-temp...@googlegroups.com
2011/1/12 bolinfest <boli...@gmail.com>:

Why not change soy.$$escapeHtml and the other escaping directive so
that it returns '' given null?

bolinfest

unread,
Jan 12, 2011, 2:13:39 PM1/12/11
to Closure Templates Discuss
Changing soy.$$escapeHtml to return '' given null would also work,
though one aspect I skipped over is that whatever behavior is changed
in the JS code generation also needs to be updated in the Java object
generation so that the two still match.

On Jan 12, 2:05 pm, Mike Samuel <mikesam...@gmail.com> wrote:
> 2011/1/12 bolinfest <bolinf...@gmail.com>:

Jay Young

unread,
Jan 13, 2011, 10:49:20 AM1/13/11
to closure-temp...@googlegroups.com
Adding something so the programmer can explicitly handle null or undefined would be fine, but I'd rather not have every variable substitution result in an extra comparison, function call, and/or object allocation (empty string) considering how often templates are used to build nested or repeating data structures and tables.  Allowing this functionality for optional parameters would be fine if people can deal with the backward compatibility issues.

bolinfest

unread,
Jan 13, 2011, 11:23:00 AM1/13/11
to Closure Templates Discuss
I think that it's worth profiling first to see what the cost really
is. For example, if the fix were made in soy.$$escapeHtml() such that
it were:

soy.$$escapeHtml = function(str) {
return str != null ? goog.string.htmlEscape(String(str)) : '';
};

Then there is no extra function call. In both the old and new
implementations, soy.$$escapeHtml() yields a new string when str is
null. Further, any reasonable JS interpreter should intern the empty
string (though it may not intern the 'null' that it would produce,
otherwise).

In this case, the runtime cost seems negligible whereas no longer
inadvertently printing 'null' or 'undefined' is a huge win.

Jesse Hallett

unread,
Jan 14, 2011, 1:57:40 PM1/14/11
to closure-temp...@googlegroups.com
You can implement option (3) yourself by writing a plugin.

I am opposed to undefined values printing as an empty string.
Currently when one of my templates outputs an undefined value I get a
template error - which is very helpful for catching bugs when I did
not intend for that value to be undefined.

You can shorten the explicit undefined check on a value slightly by
using the ternary operator:

<input value="{$foo ? $foo : ''}" />

Reply all
Reply to author
Forward
0 new messages