Search code examples
dartdart-polymer

How do I access a non-@observable field via polymer bindings with dart2js in Dart?


Seth Ladd's Polymer.dart examples are awesome and really helpful. The observable_list example appends DateTime instances to an ObservableList timestamps. Although DateTime does not extend Observable, modifying my_element.html to access a field on

{{ts in timestamps}}

works when running in the Dart VM. For example, changing

<li>{{ts}}</li>

to

<li>{{ts.second}}</li>

will access the DateTime.seconds field when on the Dart VM. However, when dart2js compiles the app to javascript, access to fields in the Polymer expression is broken. An exception Uncaught Error: RangeError: value 0 is thrown in this case, or more generally NoSuchMethodError : method not found: 'Symbol(...)' for the fieldname is thrown (see example here)

If a class extends Observable then access to @observable fields works inside of Polymer expressions after compiling to Javascript (changing the class MyRow in this example to extends Observable does not throw an exception in javascript).

What can I do when I am unable to add annotations to external classes like DateTime? Is this just a current bug in dart2js generation, or will the Polymer.dart spec forbid reading fields out of non-observable classes? Previously, web_ui successfully accessed fields from our dart-protobuf generated classes after compiling to javascript, so I'm hoping this use-case will gain support in Polymer.dart too.


Solution

  • In general, dart2js tries to tree-shake and minify code, and it doesn't know that some of the code is used from a polymer expression (which uses mirrors internally to interpret the expressions). Very likely what happened here is that dart2js is either deleting the non-observable fields or minifying them in a way that they are not reflectable.

    To fix this, you can indicate that these fields need to be preserved and used via mirrors. In polymer we provide the @reflectable annotation for this purpose. (@observable also implies @reflectable, that's why it works when you have @observable). So you can use that in the MyRow case.

    Fields from types in the core libraries can work also if they become reflectable. In this case it is a bit harder to express because you can't modify the original code to add this annotation. Instead you can use the @MirrorsUsed annotation to override the default behavior on these core types, or avoid using these fields inside polymer-expressions by hiding them inside @reflectable getters or in filters that are written directly in Dart code.