In my initial App.vue I'm dispatching an action that checks the authentication of a user. The component looks as follows:
<template>
<v-app>
<spinner v-if="!user" :status="spinner.status" :color="spinner.color" :size="spinner.size" :depth="spinner.depth" :rotation="spinner.rotation" :speed="spinner.speed"></spinner>
<div v-if="style">
<app-header></app-header>
<v-content style="margin-left:24px;margin-right:24px;">
<!-- <v-container grid-list-md text-cs-center> -->
<router-view v-if="user"></router-view>
<!-- </v-container> -->
</v-content>
<app-footer></app-footer>
</div>
</v-app>
</template>
In this component I add the component. This will render as soon as we know the auth status.
Once the auth status has been determined (logged in/out) I dispatch another action which is responsible for fetching styles from the server. The admin user gets a generic style while a normal user would get a styling based on a dynamic value in the URL.
Eg: yoursite.com/company_one would fetch styles from the server where the company is company_one
When I try to access the router in my store.js file I receive the following output in the console.
console.log(router.history) =>
I now want to drill down to get the agency parameter so I try:
If I
console.log(router.history.current);
Which gives me the following output in the console:
All of the sudden all properties are empty. If I drill further to get to the agency, the console outputs undefined
Can somebody explain why this happens?
If you're looking for the current params, you can try this.$route.params like described here: https://router.vuejs.org/guide/essentials/dynamic-matching.html
Related
I'll try to explain my problem.
I have a VueJS component, that uses slots in its template. For example:
App.vue:
<template>
<div>
<slot name="content" :content="somecontent"></slot>
</div>
</template>
<script>
export default {
data(){
return {
somecontent: "test"
}
}
}
</script>
And now I would like to initialize it like this:
<App>
<template #content="somecontent">
<div>{{somecontent}}</div>
</template>
</App>
The idea is that the user would be able to override a part of the widget content and still have some data that the widget provides. A real-life example would be a list where the elements are being loaded remotely and the user can override the list item template.
I know now that custom elements do not support scoped slots. Is there any other way to achieve this? The template syntax does not need to be like above.
Thanks for any info that could solve my problem.
In my Vue 2.6.X app I have single-file components such as:
<template>
<div>
<div v-if="users.length">
<!-- render the users in a list -->
</div>
<div v-else>
Warning: no users were found
</div>
</div>
</template>
<script>
import userService from '#/services/user-service'
export default {
name: 'UserList',
async created () {
this.users = userService.loadUsersFromServer()
},
data () {
return {
users: []
}
}
}
</script>
A problem with this approach is that after the component renders but before the users are loaded, the warning message is briefly shown in the UI. Is there a recommended way to avoid this "flash of invalid UI state"?
One approach is to introduce a loading data prop that is initialised to true, then set to false when the AJAX request has completed, and change the template to
<template>
<div>
<div v-if="loading">
Users are loading, please wait....
</div>
<div v-else-if="users.length">
<!-- render the users in a list -->
</div>
<div v-else>
Warning: no users were found
</div>
</div>
</template>
In this simple example, this should work fine, but if we need to wait on multiple requests before showing the UI, it could become cumbersome, is there a better/simpler approach?
Use Promise.all() to wait on multiple promises before setting loading to false.
That is simplest and what I do most of the times
Or you can defer navigation until the data is loaded if you use router
I've work on a Vue project, I have a login page which redirect to the Home page when the user is logged.
The thing is that I need to update/re-render the header component when the user is on the Home page.
So I've created a global variable in the main.ts:
Main.ts
Vue.prototype.isLogin = false;
I use this global value as my key for my header:
App.vue
<template>
<div id="app" class="container">
<e-header v-bind:key="isLogin" />
<div class="alert-box">
<div class="alert-list">
<e-alert
v-for="(notif, index) in $store.state.notifications"
:key="index"
:type="notif.type"
#dismissAlert="dismissAlert(index)"
>
{{ notif.message }}
</e-alert>
</div>
</div>
<router-view />
</div>
</template>
And on the Login component, in my login() methods:
Login.vue
AdminApi.login(this.email, this.password).then(() => {
this.loaderActive = false;
this.isLogin = true;
});
The problem is when the user login successfully and redirected on the Home page, the header component doesn't update, do I need to use prop instead of a global variable in my App.vue?
Vue updates it stuff when it detects that the data it depends on changes. For Vue to detect that it changes, the data needs to be reactive.
Something on the prototype chain is not, and I think you are over-complicating things by using the prototype chain for that. To manage a global state, just use a Vuex store. (docs)
You would then use ...mapGetters(['isLoggedIn']) in your computed property, this.$store.commit('loggedIn', true) or something along those lines in your Login.vue file.
Good afternoon, I modified the group header slot to customize the group row, only I would like to set the value isOpen = false by default and I can't find a way to do it, I would appreciate your help.
<template v-if="group_by" v-slot:group.header={group,items,headers,isOpen,toggle}>
<td v-for="header in headers" #click="toggle(items[0].category)">
<template v-if="header.group_header">
<template v-if="link_row">
<strong><a :href=setInvoiceLink(group)>{{group}}</a> ({{getQuantity(group)}})</strong>
</template>
<template v-else>
<strong>{{group}} ({{getQuantity(group)}})</strong>
</template>
<strong style="color:blue" v-if="group_extra_title"> - {{getExtraTitle(group,group_extra_title)}}</strong>
</template>
<template v-if="header.sum">
<strong>{{MoneyFormat(getSuma(header.value,group))}}</strong>
</template>
<template v-if="header.value == 'data-table-select'">
<v-checkbox
:disabled="enable_if"
:input-value="check_checkbox(group)"
#change="selectAllInvoiceAction(group,$event)"
></v-checkbox>
</template>
</td>
</template>
I thought the same thing than you, that I could change the default behavior from group-by prop from v-data-table.
Looking deeper in the GitHub code I saw the Push Request that added the isOpen prop to group-header slot and an example of its usage. Here it is:
<template>
<v-container>
<v-data-table :items="items" :headers="headers" group-by="type">
<template #group.header="{ isOpen, toggle }">
<v-btn #click="toggle" icon>
<v-icon>
{{ isOpen ? '$minus' : '$plus' }}
</v-icon>
</v-btn>
</template>
</v-data-table>
</v-container>
</template>
As you can see it is just a reactive prop to inform the slot if the group header is open or closed. If you want to add a button to open or close all at the same time, this another stackoverflow question show you how:
Collapse or expand Groups in vuetify 2 data-table
The logical place to inform that you want all the groups originally closed would be at the v-data-table props, but it isn't implemented yet as you can see at the props from the source code.
v-data-table source code
****EDIT****
After thinking about how to solve this issue I came to this solution that will work on your build code.
On your chunk-vendors.[hash].jsfile from your dist/js folder remove the ! from this code before the 0 (zero) at the end.
genGroupedRows:function(t,e){var n=this;return t.map((function(t){return n.openCache.hasOwnProperty(t.name)||n.$set(n.openCache,t.name,!0)
What will make look like this:
genGroupedRows:function(t,e){var n=this;return t.map((function(t){return n.openCache.hasOwnProperty(t.name)||n.$set(n.openCache,t.name,0)
The chunk file is hard to read because of the uglify process. But you just need to find the genGroupedRows function in the middle of it, and remove the exclamation point. In other words, you are just saying to the source code from Vuetify to create groups closed by default.
You can make it work on your dev too, but in this case you would need to change the source code from vuetify module. Same function name genGroupedRows.
Since I also encountered the problem, I share my solution, which can be done within the component:
Working example: https://codepen.io/joke1/pen/bGEPdYL
...
mounted () {
let table = this.$refs.table;
let keys = Object.keys(table.$vnode.componentInstance.openCache);
keys.forEach(x => {
table.$vnode.componentInstance.openCache[x] = false;
})
}
...
<v-data-table ref="table" ...></v-data-table>
The solution provided by #jk1 worked perfectly for me.
Additionally, my requirement was, to keep the first group "open" and so I could easily achieve this by removing (popping) the last entry from the keys array.
let table = this.$refs.table;
let keys = Object.keys(table.$vnode.componentInstance.openCache);
keys.pop() //remove last element so that first group stays open
keys.forEach(x => {
table.$vnode.componentInstance.openCache[x] = false;
})
It worked like a charm.
I'm completely new to Vue.js and I think I have a bit of understanding of how a router works with things like:
<router-link to="/">
But I am not really understanding what the following line does:
<router-view :key="$route.fullPath"></router-view>
I believe router-view by itself makes sure the content is displayed but what does the key part mean?
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
$route.fullPath is defined as
The full resolved URL including query and hash.
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.
As mentioned above, this is most probably done in order to trigger a transition / animation.
In Vue-Router 4.0, this is implemented using a scoped slot. According to the docs the following is necessary to force a route to reload.
<router-view v-slot="{ Component, route }">
<component :is="Component" :key="route.path" />
</router-view>
This is necessary for integrating with Vue 3 transitions.
<router-view v-slot="{ Component, route }">
<transition name="fade">
<component :is="Component" :key="route.path" />
</transition>
</router-view>
Note: Putting the :key="route.path" directly on the <router-view> can cause subtle routing issues. Such as a route with a redirect triggering twice.