Skip to content

Instantly share code, notes, and snippets.

@Rich-Harris
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Rich-Harris/9628823 to your computer and use it in GitHub Desktop.
Save Rich-Harris/9628823 to your computer and use it in GitHub Desktop.
Evaluating expressions within events

In answer to this tweet:

Within Ractive templates, you can use normal JavaScript expressions (with a handful of exceptions, e.g. no new operators or assignments, since expressions should be side-effect free). These expressions aren't straightforwardly evaled - instead, they're parsed into an abstract syntax tree, at which point we extract the references and turn it back into a string which is later used to generate a function:

Ractive.parse('{{ a+b+1 ? "a" : "b" }}');

// results in
{
  t: 2,
  x: {
    r: ['a', 'b'],
    s: '${0}+${1}+1?"a":"b"'
  }
}

Those references might mean different things at different times:

<!-- here, `a` might mean `foo.a` or `a` - mutatis mutandis for `b` -->
{{#foo}}
  {{ a+b+1 ? "a" : "b" }}
{{/foo}}

<!-- but here, it could mean `bar.a` -->
{{#bar}}
  {{ a+b+1 ? "a" : "b" }}
{{/bar}}

In other words, a reference must be resolved to a keypath, taking account of its context. It's those keypaths that are used to set up the reactive data-binding. This can't happen at parse time, because it's dependent on the data the template is rendered with - so it's impossible to evaluate the expression outside the context of a specific point within the template.

Suppose we wanted to use the evaluate an expression anyway, and that we're in a position to say that we don't care about context (i.e. a and b are top-level properties), and that we wanted to use the value of that expression in an event handler.

We could do this:

ractive.on( 'foo', function ( event ) {
  var a, b, value;
  
  a = this.get( 'a' );
  b = this.get( 'b' );
  value = a+b+1 ? "a" : "b";
  
  doSomethingWith( value );
});

That's a perfectly valid approach, and one that is easy to understand. But if we wanted or needed to use the expression syntax instead, we could take advantage of the fact that you can pass arguments to event handlers:

<button on-click='foo:{{ a+b+1 ? "a" : "b" }}'>click me!</button>
ractive.on( 'foo', function ( event, value ) {
  doSomethingWith( value );
});
@Rich-Harris
Copy link
Author

@mkrn Sorry, I forgot you were going to reply to this gist and only just saw it!

As it happens, you have excellent timing. A new feature that will be merged soon is computed properties, which behave just like regular properties except that they are derived from other values. There's a discussion on GitHub and on the Ractive mailing list.

So basically, you could do

ractive = new Ractive({
  el: 'body',
  template: myTemplate,
  data: {
    a: 1,
    b: 2
  },
  computed: {
    prop: '${a} + ${b} + 1 ? "a" : "b"'
  }
});

ractive.on( 'event', function () {
  var val = ractive.get( 'prop' );
  doSomethingWith(prop);
});

In your example you're setting a keypath to the result of the expression - you wouldn't actually need to do that with computed properties since you could just use {{prop}} (or whatever it was called) in your template, and it would update reactively.

Hope this helps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment