I'm trying to figure out how to have 2 different components on same route with Vue.
Main page or login page, depends if user is authenticated.
Maybe im missing something in documentation, but i cant and cant figure it out. Is it even possible?
Thx
So you need dynamic components.
in whichever Vue is a parent to these components use a computed property that returns the name of component you want to use, based on the authenticated state:
//- in your js
// insert into the vue instance definition, assuming you have an authencation
// property somewhere, eg session.isAuthenticated
...
components: {
MainComponent,
LoginComponent
},
computed: {
useComponent () {
return session.isAuthenticated ? 'MainComponent' : 'LoginComponent'
}
}
...
//- in your template html
<component :is='useComponent' />
http://vuejs.org/guide/components.html#Dynamic-Components
use auth param in router map:
router.map({
'/home': {
component: Home,
auth: true
},
'/login': {
component: Login
},
'/something': {
component: Something,
auth: true
},
})
and then check before each transition:
router.beforeEach(function (transition) {
if (transition.to.auth && !auth.user.authenticated) {
transition.redirect('/login')
} else {
transition.next()
}
})
Related
In vuejs 2 I was able to render directly in the router by return a render html tag. I'm trying to do the same things in vue3 with vue-router 4, but it doesn't appear to work:
{
path: 'posts',
redirect: '/posts/all',
name: 'posts',
meta: {'breadcrumb': 'Posts'},
component: {
render() {
return h('router-view');
}
},
children: [ //Lots of sub routes here that I could load off the anonymous router-view]
}
Any idea how I can get the component to render that router-view and allow me to use my children routes? I rather not load up a single component just to house "". This worked perfectly in vue-router 3, no idea what is wrong with 4. I also am importing the {h} from 'vue'.
From the Vue 3 Render Function docs:
In 3.x, with VNodes being context-free, we can no longer use a string ID to implicitly lookup registered components. Instead, we need to use an imported resolveComponent method:
You can still do h('div') because it's not a component, but for components you have to use resolveComponent:
import { h, resolveComponent } from Vue; // import `resolveComponent` too
component: {
render() {
return h(resolveComponent('router-view'))
}
},
Alternatively, if you wanted to use the runtime compiler, you could use the template option (not recommended because the compiler increases app size):
component: {
template: `<router-view></router-view>`
}
Since Vue Router 4, you can use the RouterView component directly:
import { RouterView } from 'vue-router';
//...
{
path: 'posts',
redirect: '/posts/all',
name: 'posts',
meta: {'breadcrumb': 'Posts'},
component: RouterView, // <- here
children: [ /* ... */ ]
}
Source: https://github.com/vuejs/vue-router/issues/2105#issuecomment-761854147
P.S. As per linked issue, there are allegedly some problems with <transition> when you use this technique, so use caution.
I want to test the "layout" method in a Nuxt page. The structure is as below. I've simplified the code to better serve the question.
<template>
<!-- insert any dom here -->
</template>
<script>
export default {
layout({ isDesktop }) {
return `default/${isDesktop ? 'desktop' : 'mobile'}/index`;
},
head() {
return {};
},
name: 'PageName',
meta: {
anyMetaProperty: true
},
data() {
let component = import( `~/path/to/the/child/component`);
return {
component,
};
},
};
</script>
I've successfully tested other parts of the code since they are similar to typical Vue component, but I had no luck with the "layout" method. I've tried accessing wrapper.vm.layout in the test, but it returns undefined. I also have tried mocking the method with jest.fn() mockReturnedValue function, but the method stays uncovered when I checked the coverage by running jest --coverage.
Any help would be appreciated. Thank you!
Hi I'm looking at Vue and building a website with a Facebook login. I have a Facebook login component, which works, although I'm having difficulty making my acquired fbid, fbname, whatever available to my Vues outside the component. Acknowledged this most likely this is a 101 issue, help would be appreciated.
I've tried the global "prototype" variable and didn't manage to get that working. How's this done?
Code below:
main.js
new Vue({
router,
render: h => h(App),
vuetify: new Vuetify()
}).$mount('#app')
App.vue
...
import FacebookComp from './components/FacebookComp.vue'
export default {
name: 'app',
components: {
FacebookComp
},
...
}
Component - FacebookComp.vue
<template>
<div class="facebookcomp">
<facebook-login class="button"
appId="###"
#login="checkLoginState"
#logout="onLogout"
#get-initial-status="checkLoginState">
</facebook-login>
</div>
</template>
<script>
//import facebookLogin from 'facebook-login-vuejs';
//import FB from 'fb';
export default {
name: 'facebookcomp',
data: {
fbuserid: "string"
},
methods: {
checkLoginState() {
FB.getLoginStatus(function(response) {
fbLoginState=response.status;
});
if(fbLoginState === 'connected' && !fbToken){
fbToken = FB.getAuthResponse()['accessToken'];
FB.api('/me', 'get', { access_token: fbToken, fields: 'id,name' }, function(response) {
fbuserid=response.id;
});
}
},
...
}
VIEW - view.vue
...
export default {
name: 'view',
data: function() {
return {
someData: '',
},
mounted() {
alert(this.fbloginId); //my facebook ID here
},
...
}
If you would like to have all these props from FB login available throughout your whole app, I strongly suggest using vuex. If you are not familiar with state management in nowadays SPAs, you basically have global container - state. You can change it only via functions called mutations (synchronous). However, you cannot call them directly from your component. That's what functions called actions (asynchronous) are for. The whole flow is: actions => mutations => state. Once you have saved your desired data in state, you can access it in any of your vue component via functions called getters.
Another option, in case you just need the data visible in your parent only, is to simply emit the data from FacebookComp.vue and listen for event in View.vue. Note that to make this work, FacebookComp.vue has to be a child of View.vue. For more info about implementation of second approach, please look at docs.
I have a component
<template>somecode</template>
<script>
export default {
name: 'Card',
created() {
axios.get(apiObjUrl)
somecode
})
}
</script>
this my url:
http://127.0.0.1:8080/#/card/12
But I have a problem:
when I use router-link like this:
<router-link to="/card/155"> card 155</router-link>
my url changes: http://127.0.0.1:8080/#/card/155
but the created() method doesn't get fired.
so I don't make new xhr request to api
and data not changes
what do I do?
As an alternative you can setup a key attribute on your <router-view> like this:
<router-view :key="$route.fullPath"></router-view>
As <router-view> is a component itself and unique keys force replacement of component instead of reusing it. So you can make use of life cycle hooks.
See the fiddle
That's just because your component is already created. You may try using the lifecycle hook updated() instead of created().
export default {
name: 'Card',
updated() {
axios.get(apiObjUrl)
somecode
})
}
Note: This will only work if your DOM changes. If you just want to listen on url changes and update accordingly you better $watch your route like this.
export default {
name: 'Card',
created() {
axios.get(apiObjUrl)
somecode
}),
watch: {
'$route': function() {
// do your stuff here
}
}
}
When using Vue Router with routes like /foo/:val you have to add a watcher to react for parameter changes. That results in somewhat annoying duplicate code in all views that have parameters in the URL.
This could look like the following example:
export default {
// [...]
created() {
doSomething.call(this);
},
watch: {
'$route' () {
doSomething.call(this);
}
},
}
function doSomething() {
// e.g. request API, assign view properties, ...
}
Is there any other way to overcome that? Can the handlers for created and $route changes be combined? Can the reuse of the component be disabled so that the watcher would not be necessary at all? I am using Vue 2, but this might be interesting for Vue 1, too.
One possible answer that I just found thanks to a GitHub issue is the following.
It is possible to use the key attribute that is also used for v-for to let Vue track changes in the view. For that to work, you have to add the attribute to the router-view element:
<router-view :key="$route.fullPath"></router-view>
After you add this to the view, you do not need to watch the $route anymore. Instead, Vue.js will create a completely new instance of the component and also call the created callback.
However, this is an all-or-nothing solution. It seems to work well on the small application that I am currently developing. But it might have effects on performance in another application. If you really want to disable the reuse of the view for some routes only, you can have a look at setting the key's value based on the route. But I don't really like that approach.
I used this variant without :key prop on router-view component.
routes.js:
{
path: 'url/:levels(.*)',
name: ROUTES.ANY_LEVEL,
props: true,
component: (): PromiseVue => import('./View.vue'),
},
view.vue
<template>
<MyComponent :config="config" />
</template>
---*****----
<script>
data: () => ({ config: {} }),
methods: {
onConfigurationChanged(route) {
const { params } = route
if (params && params.levels) {
this.config = // some logic
} else {
this.config = null
}
},
},
beforeRouteUpdate(to, from, next) {
this.onConfigurationChanged(to)
next()
},
}
</script>
Inside the component, I use the config as a property. In my case, reactivity is preserved and the component is updated automatically from parameter changes inside the same URL.
Works on Vue 2
vue3 and script setup:
watch(route, () => { fetch()})
in import:
import { watch } from 'vue';
import { useRoute } from 'vue-router';
const route = useRoute()