Binding lifecycle called for the removed views on navigating to any route : Durandal - durandal

In my Durandal application, I have configured several parent routers and for some of it , there are child routers configured. The problem is, when the user navigates between different parent pages or the child pages, then binding lifecycle of the previous route is also getting triggered along with the binding lifecycle of the existing route.
The child routers are configured inside the parent view models and the parent routers are configured in shell.ts as mentioned in Durandal docs.
Provided, in my shell.ts, in the router data bind property, i have given the attribute cacheViews as false and alwaysTriggerAttach as true.
<div data-bind="router:{cacheViews: false, alwaysTriggerAttach: true}"></div>
The expected result is, the binding lifecycle of the previous route should not be loaded when the current route is loading. For example if we are navigating to #P1/C1 from #P2, then the binding lifecycle of #P2 should not get loaded.

Without getting enough details, it's hard to answer this question correctly.
But looks like domElements which are removed from DOM still has eventHandlers active on them. Have you looked into http://durandaljs.com/documentation/api#class/Router/method/reset and tried using it?
Default behaviour in knockout is even if DOM elements are removed from dom, it still leaves eventHandlers attached, in active state and doesn't remove them by default.
In your router bindingHandler, inside init method, try below and see if it fixes
ko.utils.domeNodeDisposal.addDisposeCallback(element,()=>{
//do whatever you want here. like cleanup //reset(), etc
})
https://knockoutjs.com/documentation/custom-bindings-disposal.html

Related

How to determine and respond to different way the route can changes in vue

I need help with checking for router changes under three different circumstances:
When the user enters a url in a brand new page. In this case, the route is for setting initial state. (In this case, on some controls, I check for an initialised flag to determine whether to set state)
When a user performs an action on the page when the page is loaded and that action changes the route.
When a user enters a url whilst the page is loaded. The intuitive behaviour should be to set page state. However, because I'm not sure how to distinguish this type of event from the second type, these events are ignored.
Is there a way to differentiate between the 2nd and 3rd types?
You can put a initial variable in the data section of your root Vue instance and initialize it as true - it will show your components that this is the first route since the page has been loaded in the browser. Then in the beforeEach hook of your router you will set this variable to false - but only when the from argument of the hook has a non-empty matched array (or if its name key is not null - considering all your routes have a name) so that you can skip the entering into the first route / and only clear the initial variable when you leave the initial route. Or you can use the beforeRouteLeave hook in the relevant component for / route which will clear the variable instead of beforeEach hook.
You can put a watcher inside the relevant page to watch for changes in $route - or you can use the beforeRouteLeave hook in the relevant components (I prefer the latter)
You can install an event handler for the beforeunload event on the window object to detect when the user types a new URL. This will also be triggered when you close the tab (or browser). You may want to look at https://stackoverflow.com/a/26275621 for a non-universal solution.

keep-alive doesn't cache component

I have issue with having keep-alive actually keeping components alive.
Component that is being rendered in router-view have async fetching after component is mounted. My issue is that after the first time component shows up, when I render other component in that very same router, and then go back, then first component rerender as normal instead of keeping fetched data as it was.
I checked hooks and besides activated and deactivated also created hook fires which I suppose shouldn't be the case beyond first render. Also when I switch components destroyed hook fires which also shouldn't happen.
.container-fluid
.row.wrapper
aside.col-12.col-sm-2.p-0
nav.navbar.navbar-light.navbar-expand-sm.align-items-start.flex-sm-column.flex-row.text-uppercase#navbar1
a.navbar-toggler(href='', data-toggle='collapse', data-target='.sidebar')
span.navbar-toggler-icon
.collapse.navbar-collapse.sidebar
ul.flex-column.navbar-nav.w-100.justify-content-between
li.nav-item
router-link.nav-link.pl-0(to='candidates' data-toggle="collapse" data-target=".navbar-collapse.show")
font-awesome-icon.fa-fw.mr-2(:icon="iconTachometer")
| Dashboard
main.col.bg-faded.py-3
.card
.card-body
keep-alive
router-view(:key="$route.fullPath")
Okay, I found the answer - and my apologies because turned out my question wasn't fully informed.
First thing - the component in question was already nested within another router-view so what I was actually doing was nesting one in another.
Therefore, to keep alive that nested/child router-view parent router-view also has to be wrapped with keep-alive.
Based on answer here: https://forum.vuejs.org/t/how-to-use-keep-alive-with-nested-router-component/46813/4
See Special Attributes - key:
It can also be used to force replacement of an element/component
instead of reusing it. This can be useful when you want to:
Properly trigger lifecycle hooks of a component;
Trigger transitions.
If you bind key to $route.fullPath, it will always force a replacement of the <router-view> element / component every time a navigation event occurs. So just remove :key.

Vue Single page app Router, what happens with the components when we change route?

Let's suppose I have a component called FirstPage, which is my default route, now FirstPage triggers an asynchronous call, with the help of an action of the vuex store, to be made each minute to a backend Api (it's triggered when the component is loaded as a route), now let's say I go to an about route that goes to an About component, is FirstPage still making the calls?
Edit:
I'm not developing an app with that yet, so I can't provide examples.
It's on my interest to know the behavior in these cases of the router, because whenever I change the route I would want to stop making the constant calls (as they won't be necessary).
The reason is that Depending on this I'd have to switch tooling for a project I have in mind.
In general, a component's instance will be destroyed when you navigate away from it. However, there are two exceptions to this ..
When you use routes with params. From the Vue Router docs
One thing to note when using routes with params is that when the user navigates from /user/foo to /user/bar, the same component instance will be reused. Since both routes render the same component, this is more efficient than destroying the old instance and then creating a new one. However, this also means that the lifecycle hooks of the component will not be called.
When you wrap your router-view component within a keep-alive element. Since the <router-view> is essentially a dynamic component.
Generally Vue does a very good job of housekeeping and cleaning up after a component's instance when it gets destroyed. But sometimes you'll have to do some manual cleanup, especially if you use some kind of external library. This is usually handled in the beforeDestroy hook of an instance's lifecycle.
In normal conditions, any logic/scripts/etc done at creation inside said component will be "purged" on the on destroy/close hooks (not only pertinent to vue but seen in lots of other tools), if there is a need to persist something then it should be in a higher scope (or other solution)
Any script written for the respective component only runs if the component is rendered in page. Once you go to about component replacing the previous component then previous script wont run.
you can make a parent component with a router-view and load in your page you always want to get loaded, so your FirstPage component, but this component should just have logic behind it, and no html because otherwise you will always see that rendered. Router-view the page you want to display the real html and stuff. I hope you get the idea, if not i could make an example for you. Goodluck.

Does $destroy function removes the Vue Custom component from cache

I construct deep nested tree of parent and children Vue custom components using my top level component dynamically and then I am updating the data from which all tree is constructed. Which has an effect of rendering the entire tree (its a form with various custom components). I refresh/rebuild the whole form after fetching the data (which is what vue do for reactive data) that itself tell me how to regenerate the view (its like a JSON Schema from which I render the entire view).
This is related to my other issue here.
I am observing a very weird behavior in my Vue Application. When I destroy all my children components and rebuild the data to force rendering the form, it appears that even after I have called $destroy on every child component...Vue is not entirely removing them from cache?
Does vue remove the component from cache if a $destroy is called ?
Because I do not see multiple components of the same type in the Vue component list in the Chrome Vue DevTool extension panel. But I see that the same custom event is handled twice by the same component. Same function that handle the events is getting called twice even though there is only one component visible in Vue DevTools of this type.
This only happens after I render the form. When the page is loaded for the first time every thing works. Then after I reset the form by destroying the child component and resetting the data to re-render the form, magically this child component start handling the event twice.. and in 3rd render it handle the events thrice. But I see only one component in google chrome VueJS DevTool extension panel. So my guess is that vue is still keeping the previously destroyed component in cache. I am trying to figure out how should I destroy those components in the cache.
If anyone has observed something similar and found a solution please let me know.
At the moment I am going to dig little bit more on my component keys (this particular component does not have explicit key set by me).
First and foremost, the vue documentation states:
vm.$destroy
In normal use cases you shouldn’t have to call this method yourself.
Prefer controlling the lifecycle of child components in a data-driven
fashion using v-if and v-for.
So instead of destroying and rebuilding the components manually yourself, you should really letting vue handle that via v-if and v-for. If the components aren't updating to your changes, you might be dealing with a reactivity issue.
As you mentioned that this is a deeply nested structure, the reactivity is key to keeping the components up to data with the data.
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:
Vue.set(vm.someObject, 'b', 2)
Inside of a component:
this.$set(this.someObject, 'b', 2)
Also, keep in mind that Vue should be doing the heavy lifting in regards to component management, while you should define the parameters by which a component is rendered.

How to handle route param updates in nuxt.js

If the destination is the same as the current route and only params are changing
going from one profile to another /users/1 -> /users/2.
How can I recognize this and update the component?
I'm not sure its the same for next.js but in due router even if the parameter changes the same component is reused. So you need to specifically watch the param changes.
From Vue router Documentation:
One thing to note when using routes with params is that when the user navigates from /user/foo to /user/bar, the same component instance will be reused. Since both routes render the same component, this is more efficient than destroying the old instance and then creating a new one. However, this also means that the lifecycle hooks of the component will not be called.
Take a look here: https://router.vuejs.org/en/essentials/dynamic-matching.html#reacting-to-params-changes