dart-archive / polymer-dart

Polymer support for Dart
https://pub.dartlang.org/packages/polymer
BSD 3-Clause "New" or "Revised" License
181 stars 33 forks source link

Move `JsProxy` to a library that doesn't import `dart:html` #664

Closed zoechi closed 6 years ago

zoechi commented 8 years ago

to allow model classes to also be used server-side. See also http://stackoverflow.com/questions/34309483/dart-polymer-1-0-is-there-any-way-to-use-one-refectable-class-on-client-and-se

jakemac53 commented 8 years ago

Unfortunately it does need to import dart:html. This is actually because of the JsProxyReflectable annotation on it, which has const SuperclassQuantifyCapability(HtmlElement, excludeUpperBound: true) to make sure it doesn't pull in all of HtmlElement.

JsProxy in general should go away eventually (once the new js interop package allows passing dart objects to js).

ErikGrimes commented 8 years ago

I've run across the need to allow model classes in both places as well. Maybe we could create an issue for that and see if there's a way to achieve it through some other mechanism?

jakemac53 commented 8 years ago

We could actually look into removing the const SuperclassQuantifyCapability(HtmlElement, excludeUpperBound: true) from JsProxyReflectable now that we require annotations on all fields anyways. It would definitely increase the code size a bit, but it might be an acceptable cost.

ErikGrimes commented 8 years ago

Why not have the polymer transformer just add the mixin if it finds a @reflectable on the class? I've always found it a bit odd I had to add the mixin myself. If the user facing annotations don't include client specific imports and can be imported separately from Polymer itself, then we should be able to mark up our model classes and not run into issues.

That still leaves the problem that my model classes need to know that they're going to be used by polymer. If the model classes are generated from something else, I have to go back and mark them up every time they're generated. A mechanism to tell the transformer which classes and fields to transform external to the model source itself would be handy. For example arguments to the transformer in the pubspec or an annotated mapping in the consuming library's code. Just food for thought :)

jakemac53 commented 8 years ago

Why not have the polymer transformer just add the mixin if it finds a @reflectable on the class?

It needs to work pre-transformation (using mirrors) as well as post-transformation (using generated code).

That still leaves the problem that my model classes need to know that they're going to be used by polymer. If the model classes are generated from something else, I have to go back and mark them up every time they're generated. A mechanism to tell the transformer which classes and fields to transform external to the model source itself would be handy. For example arguments to the transformer in the pubspec or an annotated mapping in the consuming library's code. Just food for thought :)

Yes this is definitely a known issue and the new package:js should eventually solve it for good. JsProxy is really just a stopgap measure until we get proper support.

GnafGnaf commented 8 years ago

Is this an issue that you plan to fix soon? For me this limitation is something that would keep me from using Polymer.dart, since the single code base for front- and backend was a big selling point of dart for me.

jakemac53 commented 8 years ago

I just looked briefly into this again, but its not particularly feasible. There are some pretty deep assumptions all throughout that this is running in the browser.

jakemac53 commented 8 years ago

I presume that dart:js also only works in the browser? It uses that heavily as well...

GnafGnaf commented 8 years ago

Thanks for the quick response.

donny-dont commented 8 years ago

Just a heads up it works perfectly fine to make a proxy object over the original model.

class FooProxy extends JsProxy implements Foo {
  final Foo model;

  FooProxy(this.model);

  @reflectable
  int get bar => model.bar;
  set(int value) { model.bar = value}
}
johnpryan commented 8 years ago

It seems like Angular has a better user experience here, since it's component / template system supports Plain Old Dart Objects. For instance, assume a design where a Polymer component wants to use a data provider that returns PODOs:

class DataProvider {
  Thing get thing {
    ...
  }
}
  1. How would a Polymer component have a DataProvider injected? Should it just use a singleton?
  2. would each component need to convert a Thing to a ThingProxy?

EDIT:

After trying this out, it is possible to inject a Plain Old Dart Object into a Polymer component from Dart. For instance:

@PolymerRegister('parent-component')
class ParentComponent extends PolymerElement {
  DataProvider _dataProvider = new DataProvider();

  attached() {
    ChildComponent child = $$['#child-component'];
    child.dataProvider = _dataProvider;
  }

}

@PolymerRegister('child-component')
class ChildComponent extends PolymerElement {
  DataProvider dataProvider;
}

class DataProvider {
  // ...
}

but of course the dataProvider couldn't be used from a Polymer template.

jakemac53 commented 8 years ago

I agree it is not ideal, but this was a conscious tradeoff in the original design. Angular/Angular2 are all Dart, which has its own benefits and tradeoffs. Polymer Dart 1.x is basically just a js interop layer and this is one of the consequences of that design decision.

There are other approaches which could be taken as well which would not require the JsProxy mixin, but they would generally be slower and result in much larger generated js size.

For things which are not properties and are not accessed in your template, you can absolutely just use a regular Dart object.