-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Binding twice after reconnect #20
Comments
I am running into the same problem... |
More specifically: the click binding will get executed twice (or thrice, etc.) when the connection is lost. I think the package expects the unbind function to be called when the elements are removed as a result of the data being unavailable, but we use GroundDB to ensure the data is available even when offline. It seems the bind function is called again on reconnect, but the previous bind is not removed. (unbind is not called) The strange this is that this seems to happen on some templates, but not others... |
Note: I added a custom mousedown binding and the same occurs. Note: when I console.log(this) within the viewmodel I can see the functions get executed after reconnection and a second nexus is added, but the first one is not removed. Some pointers would be greatly appreciated :) |
Thanks for the debugging effort. I'll look into it first thing, probably later today. |
@wvanooijen92 Do you use GroundDB as well? |
Basically the onReady gets executed, but the onInvalidate does not. Is it possible that in
} Tracker.currentComputation is undefined? |
I'm pretty sure it is (null, not undefined), but now on to the why... Could it be that because the datasource is GroundDB the computation is different? But then why doesn't this happen on some of our other pages (where the datasource is also GroundDB, I verified it doesn't happen)... |
I can verify that the unbind function is actually called on these elements after Meteor.reconnect() and ends up all the way at elem.removeEventListener with elem, type and listener properly defined (as far as I can tell). So maybe it's not the unbinding going wrong, but the binding executing twice? Looking into that now. |
Nope, bind gets called only once after reconnect, so I'm back to looking at the removeEventListener and why it's not working... |
Both view objects are identical and have identical contents (so no nexus is removed). Is it possible that the listener has changed, which is why removeEventListener is not working and which would also explain by .remove(this) is not working (if it removes based on matching the object in an array)? |
I also noticed that the vm-bind-id of all elements changes every time after calling meteor.reconnect() (this also happens on elements where we do not experience the multiple bindings problem). |
If you set a custom attribute on one of those elements in dev tools, does the attribute disappear after the reconnect, meaning that the element is re-rendered? |
I swapped jQuery for vanilla |
I tried downgrading to 0.9.4 indeed, the problem persisted. |
Hum... |
Let me know about the custom attributes. I definitely think you've found the correct part of the code to look at, but I'm having trouble reproducing the bug simply with Something to do with computations is a good bet... |
Yeah, the thing is it only happens on one set of elements in our code, all other sets are unaffected, but I don't see anything in those elements which is 'strange'. Would it help you to have a description of the element object? Checking the custom attribute now (also re-added jQuery as a dependency and manually change the code back to jQuery in your package to test). The IDs do change on all elements in our app, so it seems this might be caused by one 'bug' in combination with another... |
Good point, but in that case @wvanooijen92 has the same combination. |
wvanooijen92 is working on the same project... Don't know why he hasn't responded, he was also troubleshooting this. I checked: when adding a custom attribute the attribute remains (indicating the element is not rerendered), but the vm-bind-id does change. |
By the way: the vm-bind-id of ALL elements changes (not just those who depend on a GroundDB collection, also those who don't depend on a collection at all). And damn, I forgot, but we are using GroundDB at ground:[email protected] |
Apologies for the inconenience :( |
I just also tried switching back to jQuery (re-adding it as a dependency and using the .on and .off methods rather than the eventListener methods in plain Javascript but the problem persists. I think the the problem of multiple bindings is VERY specific and only happens when elements are somehow 're-evaluated' by your package, causing the vm-bind-id to change. I am however yet to identify the difference between most elements in our project and these specific elements. |
Also I'm using Meteor 1.2.1 |
No reason for apology. It's definitely caused by some detail that has to do with the computation – what you call re-evaluation. |
The hooks below should this.onRefreshed(this.unbind);
this.onDestroyed(this.unbind);
this.onInvalidate(() => this.unbind(true)); Maybe one of these hooks doesn't run. Would you mind checking If the |
I can do you one better: the onInvalidate hook actually runs, the do_unbind
|
Oh, right – you already said that... |
Maybe a new You might be able to debug that if you set a breakpoint where the listener is created (inside (I have a tendency to edit my posts right after sending them, would it be possible for you to read directly on GitHub?) |
I guess it's simple – a second call to |
Just the one I read on my phone (currently doing research for my thesis, so have to multitask a little). I guess that makes sense (not calling a second bind on an element with the same type and same listener), do you still need me to check which method calls the unbind? |
Working on that breakpoint now |
For sure, it could happen in other scenarios – thanks again for taking the time to debug this. I'll look into the reason why The Just to be sure, you are using the latest version |
I'm not really used to working with the stack directly, but the when I set a breakpoint on that line and look in the Chrome DevTools the second line says "nexus.js:111" which is this.onReady(this.bind) in the Nexus constructor. |
The false I mention there is not the do_unbind, it is the result of this.view[ViewModel.nexusesKey].remove(this); |
The |
Do you think I should focus on why this.view[ViewModel.nexusesKey].remove(this) returns false or rather on why the bindHelper is called twice. There are two differences between elements that don't have the erronous behavior and those who do. For elements that have the bug:
|
I see, if that is the case then Nexus.remove(this) a few lines earlier would probably also return false. I'll look into that. |
It's weird if it returns |
The bind helper being called twice is expected behavior and only causes problems when the previous binding isn't unbound properly before each subsequent call. |
Nexus.remove(this) also returns false. But I see your point. I do still think the afterFlush ==> bind being called twice is the culprit. Every time reconnect is called the previous binding is removed, but two new ones are added due to the afterFlush being triggered twice. afterFlush should never trigger a bind if the previous bind has not yet been removed, I guess. |
Actually I'm 100% sure this is the issue. Even though Nexus.remove(this) returns false, before that the eventListener is removed so that should be fine (in terms of functionality, not in terms of memory leaks). However because afterFlush is set twice (because it is bound in two different computations, one for the first triggering of bindHelper and another for the second) it is executed twice, but this shouldn't happen. I'm still not sure whether afterFlush is the right 'tool for the job' (but I'm not sufficiently equipped to think of a better alternative). |
Here's another idea: The first Don't pay too much attention to the multiple calls to |
I wonder whether |
Still wish I had a test case ;) |
To muddle the image, |
Have we established exactly which hook is responsible for running |
onDestroyed |
I am using these console.logs:
In the Nexus constructor. |
👍 |
I wish I could create a test case, but I have yet to be able to reproduce this in any other setting... I'm going to try to 'cut out' the code causing this, but to be honest I expect it would just start working when I isolate it (going to try to do so anyway). |
Try wrapping the // Bind element on view ready
this.onReady(() => {
// What does Meteor.checkElement mean, btw?
console.log("onReady", this.selector);
this.bind();
}); |
In other words, does |
I use the checkElement function to filter the console.logs to just one element. |
First rendering of the element: click: addProduct Destroing of the element (caused by reconnect): Second rendering of the element (caused by reconnect): click: addProduct So you are right, the unbind on 259 is called before the bind is called. |
Just as I suspected. Interesting... |
Terribly sorry, I made a mistake in the copying of the console.logs, it's not the onDestroyed callback causing this, it's the onInvalidate (which makes much, much more sense). |
Right. Still... |
Ok, I think I've written a fix. I will be cleaning up my code and verifying whether it works in multiple scenario's. |
I submitted a pull request, I had to hurry a little so it is not thoroughly tested, but the basic idea should be clear: When a view becomes ready, we set viewModelReady to true. If we invalidate the view, it is no longer ready. When we then try to invalidate it again, we move the unbinding to the onReady. |
Hi Kristian,
We are happily using your package for a while now. But since a while it seems that events are binded to HTML nodes twice after Meteor reconnects.
Have you seen this problem before?
Where is the code located which binds the event (within your package)? Maybe I can take a look ;)
The text was updated successfully, but these errors were encountered: