Are listeners like <svelte:window on:keydown={handleKeydown}/> component scoped or global? - svelte-3

If I add a key listener inside a component using
<svelte:window on:keydown={handleKeydown}/>
as per https://svelte.dev/tutorial/svelte-window , is the listener removed when the component is destroyed? I want to be sure I am not causing memory leaks of any kind.

Yes, if you want, you can look at the generated JS code and you will find this:
m(target, anchor) {
dispose = listen(window, "keydown", /*handleKeydown*/ ctx[0]);
},
...
...
d(detaching) {
dispose();
}
The m() method is the mounting function and the d() is the destroy. You can see the listener is added in the m() and then it is removed in the d(). This is simplified but you can find out more by investigating the Svelte source code :)
Good luck!

Related

Possible to manual inject NgZone?

I'm trying to use Injector to inject my component/service but one of them requires NgZone as its dependency. From https://angular.io/api/core/Injector
export MyComponent{
constructor() {
const injector = Injector.create({
providers: [
{ provide: NgZone, deps: [ ] },
{ provide: MyService, deps: [ NgZone ] }
]
});
this.myService = injector.get(MyService);
}
}
Then in child class:
export MyOtherComponent extends MyComponent {
constructor() {
super();
}
public helloWorld() {
this.myService.stuff();
}
}
But I'm getting the following error:
ERROR Error: StaticInjectorError[MyService -> NgZone]:
NullInjectorError: No provider for NgZone!
at NullInjector.get (core.js:8896)
I tried with a dummy service that don't have anything in the constructor, and it worked.
Is there a way to provide NgZone manually through the deps like that?
Is there another way to get the "global" NgZone object (there should only be 1 instance of NgZone running right?)
MyService is also a downgraded service and is being used in both AngularJS and Angular7, not sure if that changes anything.
Edit: Reason I'm trying to do this, is because MyComponent is a component base class that will get extends upon and have many child class extending on that. If I could do it like this by manually injecting it internally, then I don't need to pass all those dependencies from the children. Imagine I have 6-7 dependencies and 30+ childrens, and lets say I need some new dependencies, I'd have to update every single one of them...
You could inject injector — it would be a single dependency. And all your children could then get what they need from this injector. Yes, you would need to provide that injector through your inheritance chain of super() calls, but at least it would be just one thing.
There's also this:
https://github.com/angular/angular/issues/16566#issuecomment-338188342
This comment states it is possible to use DI in abstract classes if you decorate them. As for NgZone instance — yes, I believe there must be only one and I also tried to get a hold of it once but couldn't come up with an elegant solution.
Guess I was brain dead last night. This morning after digging deeper, I think I've found a way to grab the global Zone and it seems to work (it triggered the change detection).
Since #waterplea also have the assumption that there should only be 1 instance of the NgZone, I decided to just look around in console and what do you know.
Then I tried to just pass the global Zone to it like this:
{ provide: NgZone, useValue: Zone },
And it gave me the error that this.ngZone.run is undefined. OK... digging deeper, oh hey, there is a root object in Zone and hey, look, a run function!
So I went and updated the code to this and it worked.
{ provide: NgZone, useValue: Zone.root },

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.

Querying child elements of a component when useShadowDom = false

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.

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.

Under Rails3.1.1/Coffeescript - not always getting the function safety wrapper

I have recently started using coffeescript with Rails and I am finding that sometimes the generated javascript does not get the function safety wrapper.
Here is a sample project demonstrating it.
For example, this CS code, in index.js.coffee:
class Foo
afunc: ->
alert("afunc")
Correctly becomes:
(function() {
var Foo;
Foo = (function() {
function Foo() {}
Foo.prototype.afunc = function() {
return alert("afunc");
};
return Foo;
})();
}).call(this);
But this code, from other.js.coffee:
class App.Func
ouch: ->
alert("ouch")
becomes this un-wrapped version
App.Func = (function() {
function Func() {}
Func.prototype.ouch = function() {
return alert("ouch");
};
return Func;
})();
It seems to be due to the "App." prefix - which I can see affects naming/scope - but why is coffeescript compiling it differently...
App is defined in setup.js.coffee, like this:
window.App =
Models: {}
Which also does not get wrapped, unless I add a class into that file too.
I am sure it must be my misunderstanding - so thanks in advance for the pointers to the manual :).
EDIT:
I created this question as I thought it might be behind some issues I was having with my backbone/coffeescript app, but it seems that it was not. As the class is linked to a public/global thing "App", it seems to work wrapped or not. Still would be useful to know why its happening - is it by design?
The "function safety wrapper" feature you are using works to prevent local variables from being set on the global namespace. Since setting an object property (App.Func) doesn't affect the global namespace, the declaration is not wrapped in a function.

Resources