durandaljs router:navigation:composition-complete firing too soon - durandal

My question is: what's the best way to know when a child module has fully loaded and finished composition, so that i can then test the html contents of that module?
I'm trying to write some integration tests that run after a route has fully finished composition.
So, the way I have the tests structured, is:
Right before the 'navigate()' call, i attach:
router.on('router:navigation:composition-complete', callback)
Next I call:
router.navigate('parent/child')
And my test code is in the callback for 'router:navigation:composition-complete'
The problem is, the route is calling a child route. That is, a parent route, that points to a module which loads up child routes. The routing works fine in my application, but when i listen for the 'router:navigation:composition-complete' event in my tests, the event fires immediately after the parent module loads, before the child module has even loaded.
Also, this only happens for the second test. By that I mean, if i have one test that navigates to a route, the event fires after the child module has loaded correctly. Then after that test has run, if i then, in a new test, navigate to a new route so i can test that, the event only fires immediately after the parent module has loaded.
SO, how can i properly know when the child module has fully finished composition, so that i can then test the html contents of my module?
Here's an example of how my routes are structured, to give a better idea of the problem I see:
router.map([
/*{durandal:routes}*/
{"route": 'parent*details',"moduleId":"parent/index","title":"Parent", hash: '#parent'}
]).buildNavigationModel();
And then in parent/index.js I have this:
var childRouter = router.createChildRouter()
.makeRelative({
moduleId:'',//refers to the 'parent' folder
fromParent: true
})
.map([
{ route: 'child', moduleId: 'parent/viewmodels/child', title: 'Child', type: 'intro', nav: true}
]).buildNavigationModel();
So then if I call: router.navigate('parent/child') it is firing the composition-complete event immediately after parent/index.js loads instead of when parent/viewmodels/child.js loads.

I'm not sure if this : Durandal router / lifecycle events when using a childRouter can help you here but I think the idea is the same

Related

Vue Lifecycle — which happens before page renders?

Using Vue 2, I want to run a check before any page elements are rendered, to determine whether the user is signed in and if so, redirect them to another page.
Looking at the Vue Lifecycle, it's my understanding that beforeMount is first in this cycle. However, the page still appears for half a second before redirecting (in my case, to Dashboard)
beforeMount() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.$router.push({ name: 'Dashboard'})
}
});
}
I've tried other Lifecycle options and none of the others work either. What am I doing wrong?
Looking at the Vue's lifecycle diagram:
beforeCreate and created hooks are earlier than beforeMount. You should use either one of them.
You have to use Guard for this. you have to check auth before going to route on router.js file. you can use beforeEnter into your route path.
read more about that here: https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard

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.

Nuxt.js router doesn't seem to load the correct component

We are trying out Nuxt.js for an app am having a bit of a problem getting their router to load the correct component. I have structured our directory to generate the following:
path: "/articles/:id?",
component: _241eccb7,
name: "articles-id",
children: [{
path: "edit",
component: _4bdace12,
name: "articles-id-edit"
}]
}, {
The issue is that the articles-id-edit never get invoked. For articles/123, the article-id route is invoked and associated component. For articles/123/edit, the article-id route is invoked and the same component when I'd expect the article-id-edit route to be invoked with its corresponding component.
What am I not understanding? What would be a decent way to debug this (like rake routes in Rails or something). Is there a way I can make more explicit my routes rather than automagically creating?
Is your file structure set up properly? Per the docs:
To define the parent component of a nested route, you need to create a
Vue file with the same name as the directory which contain your
children views.
https://nuxtjs.org/guide/routing#nested-routes

How to handle navigation when removing the current page's Vuex record?

I have a ClientManagePage where I display client information and allow for the removal of the displayed client.
The vue-router route configuration for that page looks like this:
{
path: '/client/:id/manage',
name: 'client',
component: ClientManagePage,
props: ({ params }) => ({ id: params.id }),
}
The client entities are stored in a vuex store. ClientManagePage gets its client entity from the store using the id prop and displays various properties of the client and a "remove" button.
The remove button listener is (inside a mapActions):
async removeClientClicked(dispatch) {
// Wait for the action to complete before navigating to the client list
// because otherwise the ClientListPage might fetch the client list before
// this client is actually deleted on the backend and display it again.
await dispatch('removeClientAction', this.id);
this.$router.push({ name: 'clientList' });
},
The vuex action that removes a client is:
async function removeClientAction({ commit }, id) {
// Remove the client from the store first (optimistic removal)
commit('removeClient', id);
// Actually remove the client on the backend
await api.remove('clients', id);
// Moving "commit('removeClient', id);" here still produces the warning mentioned below
}
My problem is how to handle navigating to the other route when removing a client. The current code produces warnings in development mode such as:
[Vue warn]: Error in render: "TypeError: Cannot read property 'name' of undefined"
found in
---> <ClientManagePage> at src/pages/ClientManagePage.vue
<Root>
This is of course caused by the reactivity system kicking in and trying to update the content of the page with the now-deleted vuex client entity. This happens before the removeClientAction is completed therefore the navigation to the ClientList page.
I've come up with some possible solutions to this, but they are not very appealing:
Have a v-if="client" at the top of the ClientManagePage that hides everything while the client does not exist in the store.
Use the computed property client in ClientManagePage to return a default "dummy" client that contains the required properties for the page. The page will still flash with "fake" content while the action is underway though.
Navigate to "clientList" right after (or even before) dispatching removeClientAction. This causes the clientList to display the removed client briefly while the action completes which is not good.
Are there other solutions to this seemingly common problem of navigating away when deleting the underlying vuex entity displayed on the current page?
I ended up doing a big v-if at the top of the ClientManagePage that hides everything while the client does not exist in the store. It's not pretty, but it works. An improvement could be to display a "please wait, operation in progress" in v-else.
One option is to externalize the deletion of the record. There are a number of ways to do that, but the simplest for me was to create a new route, /records/delete/:id, and place a route guard on that route that triggers the removal. Then redirect to the records list where you wanted to go in the first place. Something along the lines of:
import store from "wherever/your/store/is";
const routes = [{
path: "/records/delete/:id",
name: "deleteRecord",
props: true,
beforeEnter: (to, from, next) => {
store.dispatch("DELETE_RECORD", to.params.id).then(() => console.log("record deleted!"));
next({name: "some/path/you/wanted/to/go/to"});
}
}, ...];

Vue router view event propagation

I cant't figure out why the router-view does not emit the "login" event.
Here's the fiddle I'm playing with: https://jsfiddle.net/cvtwxf6h/22/
I want 2 different layouts, one for logged user and another for not logged user. The layout to display is determined by the logged property of the Index component.
When I click "Login" in the login page, a "login" event should propagate up to the Index component to update the logged property and change layout. For some reason the router-view does not emit the event, what am I doing wrong?
(I just want to understand the problem, I'm not interested in alternative ways to achieve this)
The problem seems to be the router-link navigates to a different route (via to="{name: 'index'}") before the login event is emitted, which causes the event to be lost somehow. If the target route is the same as the current route (no navigation), the event reaches the parent component without a problem.
A workaround would be to imperatively navigate with $router.push() after emitting the event:
const LoginPage = {
template: `<router-link to="" #click.native="switchToLoggedPage({ name: 'index' })">Login</router-link>`,
methods: {
switchToLoggedPage(route) {
this.$emit('login');
this.$router.push(route);
},
},
};
demo