How do you handle interfacing with JS libraries that have overloaded methods?
For example Leaflet.js has both of the following defined for the Map object:
openPopup(popup); // opens the given popup
openPopup(html, LatLng, popOptions); // creates a popup with the html at the location, using the popup options.
What I've come up with is:
#JS("L.Map")
class Map {
/* code */
external Map openPopup(dynamic popup, [LatLng coords, PopupOptions opts]);
/* code */
}
Is there a better way? Note: this seems to work but the analyzer complains: The method openPopup is not defined for the class Map.
Dart: 1.17.1
package:js-0.6.0
So far I have not been able to specify a different name for an instance member/method using the JS() directive which is a big issue especially for javascript object that have method names that clash with Dart keywords (such as 'catch' in a javascript Promise). I ended up using plain dart:js. And anyway even when using package/js, I ended up adding another layer to make the api more dartish (especially with callbacks and promise) especially to enforce argument types.
What I would expect would be to be able to do (in your example)
#JS("L.Map")
class Map {
JS('openPopup')
external Map openPopupHtml(String html, [LatLng coords, PopupOptions opts]);
JS('openPopup')
external Map openPopup(Popup popup);
}
but that does not seem to work. Maybe should it be considered as a feature enhancement.
Related
Once again, a Dart/Polymer related question.
I wanted to use the Parse.com JavaScript library, but since it's not available in Dart I've written Wrapper classes which store a JsObject and delegate all calls from Dart to the corresponding JavaScript object. Basically it's like a proxy.
Guess what, it works pretty great.
However, my observables don't. To understand this, you have to take a look at the following structure of one of my "proxy"-classes.
class ParseObject extends Observable {
JsObject _jsDelegate = new JsObject(context['Parse']['ParseObject']);
void set(String key, dynamic value) {
_jsDelegate.callMethod('set', [key, jsify(value)];
}
dynamic get(String key) {
return dartify(_jsDelegate.callMethod('get', [key]));
}
}
The HTML code of my Polymer Element looks like this:
<div>Name: {{project.get('name')}}</div>
Since the data binding is only evaluate in case the parameter of the method changed, it will never be updated and thus even though the name is changed, the old one will stay in place.
The solution I came up with is to store all the values the user is setting in the ParseObject#set(String, dynamic) method into a Map which is observable. This works but I think it's quiete dirty since I have to make sure that both Maps, the one in Dart and the one in the ParseObject's JavaScript representation equal.
Thus I am looking for a better solution and I think of some kind of method to tell Polymer to reevaluate it's data bindings.
Does such a method exist or are there any other possibilities to address this problem?
Extending observable by itself does nothing yet.
You need to annotate the getters with #observable (and if you are not using Polymer, you also need to add the observable transformer to pubspec.yaml). You can't make functions observable (this works in Polymer elements but not in Observable model classes. For more details about observable see for example Implement an Observer pattern in Dart or Dart: #observable of getters
I have a Dart class (foo.dart):
class Foo {
void talk() {
print('Hello');
}
}
After compiling foo.dart to JavaScript, I'd like to be able to use Foo like this:
var foo = new Foo(); // from foo.dart.js
foo.talk() // prints "Hello"
My questions:
Is this currently possible?
If so, how?
If not, what plans, if any, are in place to make it possible?
The dart:js library documentation states:
This library does not yet make Dart objects usable from JavaScript, their methods and proeprties [sic] are not accessible, though it does allow Dart functions to be passed into and called from JavaScript.
That word "yet" offers some hope, but I've found very little on this topic anywhere else.
Edit:
I do realize it's possible to call Dart functions from JavaScript using dart2js. However, what I'm trying to do is somewhat different. I'd like to be able to access all of the functionality of a Dart class from JavaScript.
Due to tree-shaking and minification this is normally not possible. If you have a Dart application (with a main() then you can make a Dart function available to be called from JavaScript (see How to call a Dart function from Javascript? for an example).
As far as I know there are plans to support your requirement but I have no idea about progress or when such a feature might be available.
This is the related project https://github.com/dart-lang/js-interop
I've just spent 20 hours in learning about the basics of Dart language, but when I find the # prefix in an open-source Dart program such as here, which I found that most programs use, I wonder what the # directive do in those programs...
For your information, the official documentation says the following:
Metadata
Use metadata to give additional information about your code. A metadata annotation begins with the character #, followed by either a reference to a compile-time constant (such as deprecated) or a call to a constant constructor.
Three annotations are available to all Dart code: #deprecated, #override, and #proxy. For examples of using #override and #proxy, see the section called “Extending a Class”. Here’s an example of using the #deprecated annotation:
However, what "additional information" does the # directive add to the code? If you create an instance by writing the following constructor
#todo('seth', 'make this do something')
, instead of the following constructor, which is the default:
todo('seth", 'make this do something')
, what is the benefit I can get from the first constructor?
I've got that using the built-in metadata such as #deprecated and #override can give me an advantage of being warned in running the app, but what can I get from the case on the custom #todo, or the aforementioned linked sample code over Github?
Annotations can be accessed through the dart:mirrors library. You can use custom annotations whenever you want to provide additional information about a class, method, etc. For instance #MirrorsUsed is used to provide the dart2js compiler with extra information to optimize the size of the generated JavaScript.
Annotations are generally more useful to framework or library authors than application authors. For instance, if you were creating a REST server framework in Dart, you could use annotations to turn methods into web resources. For example it might look something like the following (assuming you have created the #GET annotation):
#GET('/users/')
List<User> getUsers() {
// ...
}
You could then have your framework scan your code at server startup using mirrors to find all methods with the #GET annotation and bind the method to the URL specified in the annotation.
You can do some 'reasoning' about code.
You can query for fields/methods/classes/libraries/... that have a specific annotation.
You acquire those code parts using reflection. In Dart reflection is done by the 'dart:mirrors' package.
You can find an code example here: How to retrieve metadata in Dartlang?
An example where annotations are regularly used is for serialization or database persistence where you add metatdata to the class that can be used as configuration settings by the serialization/persistence framework to know how to process a field or method.
For example you add an #Entity() annotation to indicate that this class should be persisted.
On each field that should be persisted you add another annotation like #Column().
Many persistence frameworks generate the database table automatically from this metadata.
For this they need more information so you add a #Id() on the field that should be used as primary key and #Column(name: 'first_name', type: 'varchar', length: 32) to define parameters for the database table and columns.
This is just an example. The limit is you imagination.
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.
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.
I'm working on a titanium module.
Little bit confused about the TiViewProxy.
Why we are using -(void)setColor_:(id)color such methods (_ in methods) in ViewProxy ?
If we didn't write any such methods what happens when we call:
myModule.createView({
color : 'red'
});
If I didn't passed any argument on my createView() method how my view creation code will work on my module.
Means:
I'm just creating my view in my app.js like:
myModule.createView();
I'm handling the view creation code inside:
-(void)setColor_:(id)color
{
}
If I'm not passing any argument how my view is created ? Will it work ?
I'll explain the various moving parts. It's all part of the platform, so not critical that you know how it works. Knowing that it does this for you is important, though. So...
When you call myModule.createView(), the platform looks for a child of your module that matches certain constraints. Let me expand that statement by looking at the ti.pageflip module. myModule's class is TiPageflipModule. When I call myModule.createView(), the platform will look for TiPageflipViewProxy so it can instantiate it. TiPageflip comes from the module's name, minus "Module". "View" comes from createView. "Proxy" is tagged on because that's how we get from JavaScript to native. The TiPageflipViewProxy creates a native view, TiPageflipView. The proxy handles interactions between JavaScript and the native view. Make sense so far?
Part of the naming convention for exposed properties is that they are suffixed with an _. The platform looks for these methods, and calls each of them set in the creation dictionary createView({ whatever: 'value' }) as well as the property view.whatever = 'value'; or the method view.setWhatever('value'). (Search for the word "underscore" in the iOS mod dev guide, it only occurs once, to read more: https://wiki.appcelerator.org/display/guides/iOS+Module+Development+Guide).
Because you aren't defining createView, the platform is doing it for you, and it handles createView() just the same as createView({}). It's an optional param. That's just by definition.