Querying child elements of a component when useShadowDom = false - dart

I'm using useShadowDom: false with my components in an attempt to support more browsers without having to use the troublesome web_components polyfill. With the shadow DOM enabled, I would do something like this:
void onShadowRoot(ShadowRoot root) {
root.querySelector('.btn-go-back').onClick.listen((e) {
if (goBackHandler != null) {
goBackHandler();
}
});
}
onShadowRoot would run after my component's template was loaded and therefore all the components elements exist in the DOM. Without Shadow DOM enabled, I inject the component's root element in the constructor, and do something like this:
MyComponent(this._root){
_root.querySelector('.btn-go-back').onClick.listen((e) {
if (goBackHandler != null) {
goBackHandler();
}
});
}
This doesn't work because the component's template hasn't been loaded into the DOM yet, so the root element doesn't have any children to query yet.
I've tried implementing AttachAware, and querying the root element in the attach() method, and the template isn't loaded at that point either.
So, if I'm not using the shadow DOM, how can I know when the template has been loaded into the DOM so I can query elements within my component?
Edit
Attempting to use ShadowRootAware and onShadowRoot with useShadowRoot: false will result in the following error if you try to query against the provided ShadowRoot object:
Unsupported operation: Not supported
STACKTRACE:
#0 EmulatedShadowRoot._notSupported (package:angular/core_dom/emulated_shadow_root.dart:5:21)
#1 EmulatedShadowRoot.querySelector (package:angular/core_dom/emulated_shadow_root.dart:32:63)
I also tried a combination of:
Injecting the root element in the constructor and
querying against the root element within onShadowRoot which worked, kinda, but now I'm seeing this in the console output:
[WebPlatformShim] WARNING: Failed to set up Shadow DOM shim for [find-result].
InvalidCharacterError: The string contains invalid characters. '[find-result]' is not a valid attribute name.
So, for some reason, even with useShadowDom set to false on all my Components, it's still attempting to use the ShadowDom shim. I'm assuming this is because it is because I've implemented ShadowRootAware which constructs an EmulatedShadowRoot. So, I think I need a solution that avoids onShadowRoot

You can query the template of a emulated component as follows:
class Component implements ShadowRootAware {
Element el;
Component(this.el);
void onShadowRoot(_) {
this.el.querySelector('.blah');
}
}
The error message:
[WebPlatformShim] WARNING: Failed to set up Shadow DOM shim for [find-result].
is caused by the css shim. This is one of the limitations of the shim (you can use only element selectors).
You can disable the css shim. Then you will not see the error, but you won't have CSS encapsulation.
See more here:
https://github.com/angular/angular.dart/wiki/CSS-Shim

We schedule child elements querying on the next event loop iteration. I'm not aware about particular internal implementation details, but it reliably works fine for us:
MyComponent(Element root) {
// Schedule child elements querying on the next event loop iteration when
// AngularDart will render the child DOM.
new Future(() {
root.querySelector('.btn-go-back').onClick.listen((e) {
if (goBackHandler != null) {
goBackHandler();
}
});
});
}

You can also use onShadowRoot even with useShadowDom: false. However the parameter provided is not a ShadowRoot object.

Related

programatically adding an on-tap attribute referencing a dart function for a HtmlElement

I have a dart function:
_addSelection(HtmlElement ele){
ele.classes.add("selection");
}
I would either want 1 or 2 things to occur, either A) execute an on-tap and on-track function given the selection class.... OR Dynamically add the on-tap and on-track attributes referencing reflected dart functions.
I have 2 functions:
#reflectable
onTap(CustomEventWrapper cew, params){
//...
}
#reflectable
onTrack(CustomEventWrapper cew, params){
//...
}
I was looking at the HtmlElement class and documentation and I wasnt quite understanding how to do this.
Edit if I were using jQuery and Javascript, I would be doing something as simple as:
$(document).on("tap", function(){});
$(document).on("track", function(){});
Edit2 Added Angular Dart because both designs leverage Dart backend and markup front end.
You could do:
_addSelection(HtmlElement ele) {
ele.classes.add("selection");
ele.onTouchEnd.listen((TouchEvent touch) {
...
});
}
That would give you something close to the tap event. If you wanted to get really fancy you could also listen for onTouchStart and only call your onTap handler when the time between the start and end is small.

iron-ajax on-response doesnt recognize dart reflected method

My ajax is defined as:
<iron-ajax id="myAjaxId" auto
url="http://localhost:8088/test_server/v1/users/35"
handle-as="json"
on-response="{{handleResponse}}" ></iron-ajax>
and in my dart i say:
#reflectable
void handleResponse ( e, Map data ){
print("hand response fired");
f_name = data["f_name"];
l_name = data["l_name"];
id = data["id"];
}
not only does it not fire the print statement, but in the Chromium console, when it is run, it says:
[my-ajax-fetcher::_createEventHandler]: listener method `{{handleResponse}}` not defined
I was looking up some other examples and noticed #reflectable is the tag i should be applying.
I was also trying to look up what the target signature needs to look like, and didnt see anything.
You don't need {{}} for event handlers in Polymer 1.x just on-response="handleResponse"

JQuery UI spinner spin event not as expected in Scala JS

When I define a spinner in ScalaJS and handle the spin value I am not able to get the new spin value in the event as I would have expected. According to the JQuery UI documentation the second parameter to the spin event is the ui object that contains a value attribute. So I defined a trait:
trait Number extends js.Object {
val value: Int = js.native
}
And then handle my spin event thus:
jQuery("#mySpinner").spinner(js.Dynamic.literal(spin = { (e: HTMLInputElement, ui: Number) =>
log("Change: " + ui.value)
}: js.ThisFunction1[HTMLInputElement, Number, Any]))
But the "value" attribute does not seem to be a member of the ui object as I get the exception below in my log statement. Can someone tell me what I am doing wrong?
uncaught exception: scala.scalajs.runtime.UndefinedBehaviorError: An
undefined behavior was detected: undefined is not an instance of
java.lang.Integer
You say e: HTMLInputElement but it should be e: Event
I suspect the problem is a combination of the previous comments. You are correct that, since you're using ThisFunction, the first element should be an Element of some sort. (Although, is it really an HTMLInputElement? That's a slightly unusual element type to put a spinner on.)
But that Element gets prepended to the function parameters, whereas you've got it replacing one.
In other words, you have
(e: HTMLInputElement, ui: Number)
but it needs to be
(elem: HTMLInputElement, e:Event, ui: Number)
in order to match the expected signature. So in practice, the system is trying to cast the value member of an Event, which of course doesn't exist, to Integer. It finds that value is undefined, tries to cast it to Integer, and boom.
I can't say I'm 100% certain (and IMO that ui parameter is just plain weird to begin with -- I'm a little suspicious of the jQueryUI documentation there), but that's my guess. Try fixing the signature of your call, and see if the error goes away...

Provide callback for custom component

I made a custom component which basically wraps a d3 line chart. Now I want to be able to register a callback for clicks on the lines in the chart.
I gave the component a #NgCallback parameter, which I then send events to:
class NetworkSummaryComponent implements NgShadowRootAware {
#NgCallback('callback')
Function callback;
void onShadowRoot(ShadowRoot shadowRoot) {
...
chart.callMethod('listen', ['line-click', (ev) {
var name = ev.callMethod('getLineName');
print(name);
callback({'name': name});
}]);
}
}
When using the component, I specify a function of my controller as callback:
<network-summary
...
callback="ctrl.lineClicked">
</network-summary>
However, that function is never actually called, put I know the callback arrives from the JS side because the print in the first snippet is executed.
If I instead specify the attribute as callback="ctrl.lineClicked()" I get a strange exception:
Closure call with mismatched arguments: function 'call'
I could not find any official documentation on how to properly do callbacks, so I'm not exactly sure what I'm doing wrong.. Any ideas?
It turns out that I had to explicitly name the expected arguments in the attributes:
<network-summary
...
callback="ctrl.lineClicked(name)">
</network-summary>
Hope this is useful to the next person having this problem.

calling jQuery .remove() on custom UI widget causes infinite loop

I have created custom jQuery UI widget called uiPopover, very similar to UI-dialog (in fact, most of the code is copy-paste from it). This widget has a custom destroy method that hides the widget and removes it from the DOM. Again, it's pretty much copy-paste from UI-dialog.
destroy: function() {
var self = this;
if (self.overlay) {
self.overlay.destroy();
}
self.close();
self.element
.removeData('popover');
self.uiPopover.remove();
console.log('afterRemove')
return self;
},
The weird thing is that this causes an infinite loop that throws some errors:
$('#element').popover();
$('#element').remove();
As far as I can see, the problem is that when I call .remove(), it automatically calls destroy() on my widget (this is built-in in jQuery UI) and the destroy methodd tries to call remove() again on my element, and then that tries to call destroy() again and so on..
However, the weird thing is that this doesn't happen with UI dialog. So when I do this:
$('#element').dialog();
$('#element').remove();
Everything is okay... There must be something wrong with my plugin, but I cannot figure out what.
Here is the full source of my plugin: https://gist.github.com/2208569
There's not much you can do about the recursive call to destroy(), aside from modifying jQuery UI itself. You can, however, break the chain by preventing remove() from being called again:
destroy: function() {
var self = this;
if (self.overlay) {
self.overlay.destroy();
}
self.close();
if (self.element.data("popover")) {
self.element.removeData("popover");
self.uiPopover.remove();
}
return self;
}
Note in passing that you don't have to copy and paste code in order to augment existing widgets, as the widget framework supports prototype inheritance. It would be interesting to know if your problem still occurs if you have your widget derive from $.ui.dialog instead of duplicating its code base.

Resources