How to handle back button after returning a promise from the activate callback in Durandal - durandal

We are developing a mobile application using Durandal 2.1
The documentation states that we can return a promise from the activate callback and it should cancel navigation on failure.
We've been trying to use that feature, but it breaks the back button in our application.
As you can see in Durandal's router.js plugin code, the cancelNavigation function is navigating towards the lastUrl:
function cancelNavigation(instance, instruction) {
system.log('Navigation Cancelled');
router.activeInstruction(currentInstruction);
router.navigate(lastUrl, false);
isProcessing(false);
rootRouter.explicitNavigation = false;
rootRouter.navigatingBack = false;
router.trigger('router:navigation:cancelled', instance, instruction, router);
}
Because of that behavior, using the back button after navigation cancellation will not provide the expected results since it will navigate back to the page that caused the error.
Are we using this feature in the wrong way?
Our goal is to try to load data in the activate function, and then navigate back to the current page if an error occurs.
Any help regarding this woule be greatly appreciated.

Related

Vue Router's next(false) clears path on global Navigation Guards

I found the reason behind my "path flashes", it's ensureURL method in the HTML5History class (vue-router v3.6.5).
// next(false) -> abort navigation, ensure current URL
this.ensureURL(true)
If we take a look at this method (that's code from vue-router source)
ensureURL (push?: boolean) {
if (getLocation(this.base) !== this.current.fullPath) {
const current = cleanPath(this.base + this.current.fullPath)
push ? pushState(current) : replaceState(current)
}
}
if I try to go straight to the restricted path, at some point, in my app I have next(false), that causes half-second absolutely clear URL.
When I logged vars in this method I noticed that it pushes to to url WITH aborting navigation.
When I commented that ensureURL(true) app worked as expected without annoying flashes.
When you try to land on the site with already some sub-path (in my case it was localhost:8080/broker/login), you expect url path to stay the same, or redirect you if path is restricted.
Instead once I try to land on the localhost:8080/broker/login, after awhile it pushed to localhost:8080/ then again localhost:8080/broker/login.
In my case, there is some logic and async operations on the first website load, that where I have next(false).
After some digging in the vue-router's source code I found ensureURL methods that does all that...
If you take a look at this method, you'll see that condition is satisfied
getLocation(this.base) !== this.current.fullPath
where:
getLocation(this.base) becomes /broker/login
this.current.fullPath becomes /
so it tries to push to undefined 🤯
Could you please explain why we need it and how we can fix it?

Is there anyway to ignore the *failure: Avoided redundant navigation to current location* error using Vue Router and just refresh the page?

I see this question has been asked a few times on here, but none of the answers have really helped me in this current situation.
I have an app I'm working on with a sidebar with tabs that link to different dashboards. Each of the SidebarLinks are a router-link with the to key being fed the route prop from the main component.
Inside one of these dashboards, the Analysis dashboard, there is another router that routes you to child routes for specific Analyses with their own ids (EX: /analysis/1).
The user clicks on a button for a specific analysis and they are routed to a page containing that information, on the same page.
The Error
When I click the Analysis SidebarLink the route in the url changes back to /analysis, but the page doesn't update/refresh.
I don't get an error in the console, but I do get the failure in the devtools.
I understand that Vue Router doesn't route back to a route you are already on, but I need it to. If you refresh the page when the url is just /analysis it routes back to it's inital state.
Is there anyway to refresh when it rereoutes to /analysis? Or a way to handle this error to work as intended?
What I've tried
I've tried changing the router-link to an <a> tag and programatically use router.push and then catch the error, but that doesn't do anything.
I've tried checking if the route.fullPath.contains("/analysis") and then just do router.back() but that doesn't seem to work either.
SidebarLink router function
function goToRoute() {
console.log(`route.fullPath → `, route.fullPath)
if (route.fullPath.match('/analysis*') as any) {
console.log('route includes /analysis')
router.back()
} else {
console.log('route doesnt inclue /analysis')
router
.push({
path: props.route,
})
.catch(() => {})
}
}
Inital /analysis Page
This is what the page looks like normally
/analysis/1 Page
This is what the route to analysis/1 looks like (url changes)
/analysis/1 Page When Issue Analysis SidebarLink Clicked
This is what the route to analysis looks like when the sidebarlink is clicked (url changes, but the page stays the same)
I suspect you are fetching your data from a backend service or data files
If yes you can refetch the data everytime the route param changed by watching it.
watch: {
'$route.params.id': function (id) {
if(id)
this.$store.dispatch('fetchOneAnalys', id)
else
this.$store.dispatch('fetchAllAnalyses')
}

Vuejs not triggering mounted after user pressed back on his browser

On my vuejs application there is a dashboard, where the user can click a button that send him to /room (router.push("/room");).
When the user arrive on the page, the "mounted" function is triggered and a simple console.log is emited. That works.
mounted() {
console.log("room mounted");
}
If the user press the "back" button of his browser and go back to the dashboard, he can click the button again to join the room, except this time, the "mounted" function is not triggered.
Is there a way to make this works ?
Thank you.
In response to a part of your response to the answer below,
what I'm looking for is when I click again on the button that trigger
the router.push("/room"), because when I'm redirected, mounted nor
updated` are called.
To solve your problem, you can watch the $route object, by doing
watch: {
'$route' () {
// this will be called any time the route changes
}
},
This is expected behavior in Vue Router according to this issue on the Vue Router GitHub repo:
This is expected behaviour, Vue re-uses components where possible.
You can use the beforeRouteUpdatehook to react to a route switch that
uses the same component.
Navigating "back" to an already-mounted component won't trigger a subsequent mounting of the component. To see which lifecycle hooks are triggered on Route Update, you can look at this blog post (scroll down to the Lifecycle Hooks diagram).
The situation you're running into is the "Client Update" column, where mounted is not called, but update is. In general, I tend to utilize parallel code in both beforeRouteEnter and beforeRouteUpdate. Sadly, it's a bit repetitive.

How to catch component unmount caused by live reload

[edit - I thought I was using Hot Reloading, but I am actually using Live Reload]
I have a native plugin that needs to do some clean up each time it is finished with. Basically I want to prevent these errors:
Calling JS function after bridge has been destroyed: RCTDeviceEventEmitter.emit(..)
componentWillUnmount() doesn't get called.
Live reloading will restart the app and load the app back to the initial route when a file changes. ComponentWillUnmount won't be called.
When you reload, what happens behind the scenes is that the react context is getting destroyed, and a new one is being created.
That error get's thrown whenever a Native Module is trying to do work, by using the old react context.
You could use something like this:
Error Boundary in react
Just wrap the error prone code inside the ErrorBoundary component, e.g.
<ErrorBoundary><childComponentCausingError></ErrorBoundary>
And in the ErrorBoundary component, you could just catch the error in componentDidCatch = (error, info) => {
}
componentDidCatch() is a lifecycle method in React.

Vue 2.0.1 and Electron - Visual Flash

I'm creating an app visually similar to Alfred to manage and search for my bookmarks. Its working well, but when I do a search and open a bookmark, I immediately hide the app and when I invoke it again, it return to the default mode doing a visual flash. The reset to default is triggered right before hiding the app.
I hide the application like this : remote.app.hide() and I added a listener on win.hide in my components to reset the vue.
It works, but the reset is processed after the application show up again.
I don't know how to do it when the application is hide or to show up it right after the vue is reloaded.
If you have any clue, it would be great.
I created a sample project on Github you can clone and test this issue.
Github Project
I'm working on macOS at the moment.
Thank you.
I found an easy way, you cannot rely on the window.on('hide') event.
So in your shortcut registration, I made your app emit a custom event that your Vue.js will listen to reset your input before hiding the app:
main.js
const retShow = globalShortcut.register('CmdOrCtrl+Alt+V', () => {
if (!win.isVisible()) {
win.show()
} else {
app.emit('hide-window'); // Let the window hide the app
}
})
In your Vue.js app, in the created hook:
app.js
app.on('hide-window', function () {
vm.reset();
setTimeout(app.hide, 10);
});
Here my pull request: https://github.com/Cronos87/electron-vue-flash-issue/pull/1/files