ractive: Dynamic proxy event problem on edge

Description:

Dynamic proxy event names (see doc) bug using conditional sections on Ractive-edge.

Error thrown:

2017-01-30 08:54:58.222 ractive.js:5030 Uncaught ParseErrorcharacter: 15line: 1message: "Expected matching quote '"' at line 1 character 15:↵<h1 on-click="{{#if a.b}}func1{{else}}func2{{/if}}">hello world</h1>↵              ^----"name: "ParseError"shortMessage: "Expected matching quote '"'"stack: "Error: Expected matching quote '"' at line 1 character 15:↵<h1 on-click="{{#if a.b}}func1{{else}}func2{{/if}}">hello world</h1>↵              ^----↵    at ParseError (

Versions affected:

0.9.0-build-65

Platforms affected:

Chrome 55.0.2883 / Windows 10

Reproduction:

works on 0.8.10 https://jsfiddle.net/rsL7qhss/

broken 0.9.0-build-65

https://jsfiddle.net/pLsdv1gp/

Code:

the conditional section resulting in a dynamic proxy event name no longer works on 0.9.0-build-65

var r = new Ractive({
    el: '#target',
    template: '<h1 on-click="{{#if a.b}}func1{{else}}func2{{/if}}">hello world</h1>',
    data: {a: {b: 1}}
});

maybe worth noting: this kind of conditional section does work on 0.9.0-build-65:

var r = new Ractive({
    el: '#target',
    template: '<h1 on-click="{{#a.b}}func1{{else}}func2{{/}}">hello world</h1>',
    data: {a: {b: 1}}
});

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 15 (7 by maintainers)

Most upvoted comments

There are some normalization issues with plain fire vs proxied fire. With the proxy event, ractive knows that the argument will be an appropriate event object and will thus set the name on it. With ractive.fire, you can pass whatever args you want to, you just happen to be passing an event object. I’m actually working on a PR for edge right now that addresses that and #2810.

A. Yes. The proxy, as it stands now, is usually more useful for moving component events, though. B. and C. Yes. The name change is actually the only difference between the two.

I’m not always great at explanations, so if this doesn’t make sense, let me know and I’ll try to come up with something better…

For this message, “mustaches” largely means “mustache interpolators” e.g. {{foo}} and not block mustaches ({{#if}}, {{#each}}, etc) that are more structural and generally aren’t associated with a single specific value being output. Block mustaches absolutely do not work well with producing values, but they excel at strings and vdom.

Only event directives, decorators, and transitions were affected by the deprecation, or in other words, only Ractive constructs that specifically expect not to be passed plain strings. The issue that brought that change about was that the directive argument syntax was entirely ambiguous, especially around events that may be proxy or method calls, so what you would think would parse might not be what actually parsed. All of those directives as of edge now use the same parsing logic that doesn’t include mustaches because the directive value is already in “expression” mode.

You can think of it like <a on-click="{{name}}:{{arg1}},{{arg2}}"> was actually getting a string with whatever values happen to be in name, arg1, and arg2 and then trying to figure out what the actual values were supposed to be based on that string. There are many scenarios where the string gives you something largely unparseable, so you end up with all of the args jammed together in a single string arg. That issue was further compounded by method-call syntax. It boils down to anything with mustaches in it comes out as a string in the end, with a few special exceptions for simpler cases, like the single mustache used for binding. Heaven forbid you try to use a string literal with the same quote as the attribute value.

<a on-click="...expression..."> has the expression part turned into a function that is called with any references in the expression. Since it’s a plain JS expression, there’s no ambiguity of string vs maybe-a-value. All of those directive args as of edge are simply handled as JS expressions. The only exemption to that is what is left of proxy events that only specify an event name and are designed for use re-proxying component events.

TL;DR for that bit - mustaches turn out strings, expressions turn out anything, so for places where you generally expect anything, expressions tend to work out better. Square pegs and round holes and whatnot. Turning strings into expressions is headache-y and performance killing.

Now regular attributes generally expect either a string or boolean value, and the boolean flavor typically don’t have a value e.g. checked in <input type="checkbox" checked />. That leaves the string flavor which work out nicely with mustaches that typically produce strings. Any attribute that is not a directive will still play nicely with mustaches.

As of 0.8, in addition to plain attributes, any directive can be placed in a conditional section, so if you need a dynamically bound event (will be bound and unbound conditionally) you can throw them in a conditional section e.g. <button {{#if someCondition}}on-click="doSomething()"{{/if}}>. That also works with decorators, transitions, and special sometimes-directives like value and checked.

For the deprecation discussion, that mostly happened in the original issue #1172 and PR #2386. It’s very difficult for any extensive proxy event support to live in the same directive as method/expression events, but fired events are definitely useful and a bit more painful since expressions landed. That’s why I asked about/would like to reintroduce a proxy-specific directive.