how to implement closing a page through a function in vue js
by example
configured routing
{
path: '/example',
component: Layout,
redirect: '/example/list',
name: 'Example',
meta: {
title: 'example',
icon: 'example'
},
children: [
{
path: 'create',
component: () => import('#/views/example/create'),
name: 'CreateArticle',
meta: { title: 'createArticle', icon: 'edit' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('#/views/example/edit'),
name: 'EditArticle',
meta: { title: 'editArticle', noCache: true },
hidden: true
},
{
path: 'list',
component: () => import('#/views/example/list'),
name: 'ArticleList',
meta: { title: 'articleList', icon: 'list' }
}]
},
when I go to ArticleList.vue, I open the list, go there to the authoring / editing country -> a new member opens (the list page remains open in the tab next to it), after submitting the form in CreateArticle.vue, the CreateArticle.vue page should close
Tried through destroy () As a result, the content of the page is cleaned, and the page itself remains (but empty), you have to close the page through X
Related
I'm generating some menu buttons dynamically (not sure if that's best practice)
script
items: [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/' },
{ title: 'Register', icon: 'mdi-image', route: '/register' },
{ title: 'Login', icon: 'mdi-help-box', route: '/login' },
],
html
<v-list-item v-for="(item, i) in items" :key="i" link :to="{path: item.route}">
But what I want to do is hide the dashboard button until they have signed in. The user signed in value is kept in store.
$store.state.user.signedIn // true/false
How can I programmatically hide buttons depending on signed in value? I was trying to do this
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/', reqAuth: true }
But not sure how to get it working smoothly in the html. I'd also like to do the reverse later on the login/register buttons, if the user IS signed in then these should hide and a Logout button will come into play.
You have two options:
A) have separate menu arrays and display one or the other based on isLoggedIn. If you have any items showing in both cases, you'll need to place them in a third array and concat one of the first two with the third
B) have a boolean property on each menu item stating whether it should show when isLoggedIn or not. If you have menu items showing on both, you'll need either two props on each item (showWhenLoggedIn, showWhenLoggedOut - change them if too long) or, alternatively, you could make the show an array of booleans: show: [true, false] - first bool controlling whether it's shown when logged out, second when logged in).
Solution A) example (separate arrays):
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
loggedInMenuItems: [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/', show: [false, true] },
],
loggedOutMenuItems: [
{ title: 'Register', icon: 'mdi-image', route: '/register', show: [true, false] },
{ title: 'Login', icon: 'mdi-help-box', route: '/login', show: [true, false] },
],
permanentMenuItems: [
{ title: 'Terms and Conditions', icon: 'mdi-whatever', route: '/terms', show: [true, true] }
],
isLoggedIn: false
}),
computed: {
menuItems() {
return (this.isLoggedIn
? this.loggedInMenuItems
: this.loggedOutMenuItems
).concat(this.permanentMenuItems)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<label><input v-model="isLoggedIn" type="checkbox"> Is logged in</label>
<pre v-html="menuItems.map(m => m.title)"></pre>
</div>
Solution B) example:
Vue.config.devtools = false;
Vue.config.productionTip = false;
new Vue({
el: '#app',
data: () => ({
items: [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/', show: [false, true] },
{ title: 'Register', icon: 'mdi-image', route: '/register', show: [true, false] },
{ title: 'Login', icon: 'mdi-help-box', route: '/login', show: [true, false] },
{ title: 'Terms and Conditions', icon: 'mdi-whatever', route: '/terms', show: [true, true] }
],
isLoggedIn: false
}),
computed: {
menuItems() {
return this.items.filter(item => item.show[Number(!!this.isLoggedIn)])
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<label><input v-model="isLoggedIn" type="checkbox"> Is logged in</label>
<pre v-html="menuItems.map(m => m.title)"></pre>
</div>
I personally prefer the second one, for brevity. I also find it a tad more elegant.
However, in large teams or in projects where code complexity needs to be kept to a minimum and code readability to a maximum, the first solution is often times preferred.
Lastly, second solution allows more flexibility for menu items order, although it's not a real issue (implementing an order attribute to each item would be trivial).
Note: obviously, isLoggedIn should come from state, not from component data fn. I placed it in data so you could easily test it here.
Your items property should be defined as computed property and add shown field in each item of items and use state value as its value in dashboard link :
computed :{
items(){
const signedIn=$store.state.user.signedIn;
return [
{ title: 'Dashboard', icon: 'mdi-view-dashboard', route: '/', shown:signedIn },
{ title: 'Register', icon: 'mdi-image', route: '/register',shown:!signedIn},
{ title: 'Login', icon: 'mdi-help-box', route: '/login' , shown:!signedIn},
]
}
}
in template add
<v-list-item v-for="(item, i) in items" :key="i" link v-if="item.shown" :to="{path: item.route}">
As my project is growing, I've noticed a lot of repetitions. I'm starting with the navigations buttons, as they can appear in multiple places (side menu, navbar).
I'd like to centralize them and let the component import them as needed. So I've tried creating this file to hold all my menu items:
// menuItems.js
export default {
data() {
return {
items: [
{ title: 'Profile', icon: 'mdi-account-circle', reqAuth: true, hideOnAuth: false},
{ title: 'Dashboard', icon: 'mdi-view-dashboard', reqAuth: true, hideOnAuth: false },
{ title: 'Settings', icon: 'mdi-cog', reqAuth: true, hideOnAuth: false },
{ title: 'Signup', icon: 'mdi-account-circle-outline', reqAuth: false, hideOnAuth: true},
{ title: 'Login', icon: 'mdi-login', reqAuth: false, hideOnAuth: true },
{ title: 'Logout', icon: 'mdi-logout', reqAuth: true, hideOnAuth: false},
]
}
},
methods: {
menuItems: function(authenticated) {
if (!authenticated) {
// Gets items that do and don't require Auth, except for ones that should hide on Auth
return this.items.filter(o => o.reqAuth || !o.reqAuth && !o.hideOnAuth)
}
// Gets items that don't require Auth
return this.items.filter(o => !o.reqAuth)
}
}
}
Buttons can require authentication to be visible, and they can also be hidden once authenticated (eg. The login button).
Now lets assume I have a vue component for my nav bar, how do I import in the method that returns the filtered items?
// NavBarComponent.vue
<template>
<div>
<v-btn v-for="(item, i) in menuItems(authenticated)">
{{ item.title }}
</v-btn>
</div>
</template>
<script>
export default {
name: "NavBarComponent",
data() {
return {
authenticated: true,
}
},
methods: {
}
}
</script>
In this case, how do i make menuItems in the component reference the external file that will do the work of filtering?
You can create a mixin file and put your methods in that mixin and apply the mixin your component.
https://v2.vuejs.org/v2/guide/mixins.html
Your mixin would look like this:
// /mixins/menuItemsMixin.js
const menuItemsMixin= {
data() {
return {
items: [
{ title: 'Profile', icon: 'mdi-account-circle', reqAuth: true, hideOnAuth: false},
{ title: 'Dashboard', icon: 'mdi-view-dashboard', reqAuth: true, hideOnAuth: false },
{ title: 'Settings', icon: 'mdi-cog', reqAuth: true, hideOnAuth: false },
{ title: 'Signup', icon: 'mdi-account-circle-outline', reqAuth: false, hideOnAuth: true},
{ title: 'Login', icon: 'mdi-login', reqAuth: false, hideOnAuth: true },
{ title: 'Logout', icon: 'mdi-logout', reqAuth: true, hideOnAuth: false},
]
}
},
methods: {
menuItems: function(authenticated) {
if (!authenticated) {
// Gets items that do and don't require Auth, except for ones that should hide on Auth
return this.items.filter(o => o.reqAuth || !o.reqAuth && !o.hideOnAuth)
}
// Gets items that don't require Auth
return this.items.filter(o => !o.reqAuth)
}
}
};
export default menuItemsMixin
And in your component:
// NavBarComponent.vue
<script>
import menuItemsMixin from './mixins/menuItemsMixin.js'
export default {
mixins:[menuItemsMixin]
}
</script>
You can import this mixin in multiple components and you can also add more mixins in this component where the unique methods will be added.
I ended up creating a javascript file:
// views.js
export const views = [
{title: 'Dashboard'},
{title: 'Profile'},
{title: 'Login/Signup'},
]
then in my navbar component I imported it like so:
import {mapGetters} from "vuex";
import {views} from "../../common/views";
export default {
data: () => ({
items: views
}),
computed: {
...mapGetters(['isAuthenticated',])
menuItems: function (){
if (this.isAuthenticated) {
// do this
} else {
// do this
}
},
}
}
Then I did the same for the filtering function, but I could also just re-code it as needed if required in each component. I determined authentication state using Vuex's store, and retrieve it with mapgetters.
<componentA v-if='isAuthenticated'>
<navItem v-for='item in menuItems'>
</componentA>
When I press refresh(F5) button then my router directs me to the main page. How do I stay on the same page when the page is refreshed?
that's route, mode is 'history'
const routes = [
{ path: '*', redirect: '/' },
{
path: '/', component: template_Default, redirect: '/Login',
children: [
{ path: 'Login', component: login_, name: 'Login' },
{ path: 'SignUp', component: signUp_, name: 'SignUp' }
]
},
{
path: '/Main', component: template_Home, name: 'Main', redirect: '/Main/Overview',
children: [
{ path: 'Overview', component: overview_, name: 'Overview' },
{ path: 'Customer', component: customer_, name: 'Customer' },
{ path: 'Task', component: task_, name: 'Task' },
{ path: 'Order', component: order_, name: 'Order' },
{ path: 'Product', component: product_, name: 'Product' },
{ path: 'Setting', component: setting_, name: 'Setting' }
]
},
{
path: '/ForgotPassword', component: forgotPassword_, name: 'ForgotPassword',
// redirect: '/ForgotPassword/GetEmail',
// children: [
// { path: 'GetEmail', component: forgotPasswordGetEmail, name: 'GetEmail' },
// { path: 'ReSend', component: forgotPasswordReSend, name: 'ReSend' }
// ]
}
]
And my controller,
router.beforeEach((to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page
const publicPages = ['/Login','/SignUp','/ForgotPassword'];
const authRequired = !publicPages.includes(to.path);
const loggedIn = store.getters.isLoggedIn;
if (authRequired && !loggedIn) {
return next('/Login');
}else next();
if(loggedIn){
AuthController.IsOnline();
}
})
thanks in advance..
The issue is that you have { path: '*', redirect: '/' } as the first item in your routes array. According to the documentation:
When using asterisk routes, make sure to correctly order your routes so that asterisk ones are at the end.
This is because Vue Router searches through your list of routes in order and returns the first matching route. An asterisk is going to match any and all routes so, when you refresh the page, it returns the first route and redirects you to '/'.
Same problem here, but my route have no { path: '*', redirect: '/' }
Here is my code.
export const routes = [
{
path:'/login',
component: login,
name: 'home'
},
{
path:'/',
component: mainapp,
name: 'mainapp',
meta: {
requiresAuth: true
},
children: [
{
path:'/dash',
component: dashboard,
name: 'dashboard'
},
{
path:'/dept',
component: dept,
name: 'department'
},
{
path: '/deptDetail',
component: deptDetail,
name: 'detail'
},
{
path:'/hrd',
component: HRD
},
{
path:'/notify',
component: notify
},
{
path:'/device',
component: device
},
{
path:'/setup',
component: setup,
name: 'setup'
},
{
path:'/scanArea',
component: area,
},
{
path:'/env',
component: environment,
},
{
path:'/record', /* debug page */
component: record,
},
{
path:'/redis', /* debug page */
component: redis,
},
{
path:'/permit',
component: permit,
},
{
path:'/app',
component: app,
}
]
},
]
I tried to modify the routing configuration problem, but the breadcrumbs have two identical user lists, the error is displayed as 首页 / 系统设置 / 用户列表 / 用户列表. For example, when the left menu clicks on the user list, it should display correctly 首页 / 系统设置 / 用户列表.
The error is displayed as:
Displayed correctly as:
router.config.js
{
path: '/settings',
name: 'settings',
component: RouteView,
redirect: '/settings/user',
meta: { title: '设置系统', keepAlive: true, icon: bxAnaalyse },
children: [
{
path: '/settings/user',
name: 'user',
component: RouteView,
redirect: '/settings/user/list',
meta: { title: '用户列表', keepAlive: false },
hideChildrenInMenu: true,
children: [
{
path: '/settings/user/list',
name: 'user',
component: () => import('#/views/settings/user/index'),
meta: { title: '用户列表', keepAlive: false }
},
{
path: '/settings/user/add',
name: 'add',
component: () => import('#/views/settings/user/add'),
meta: { title: '新增用户', keepAlive: true, hidden: true }
}
]
},
]
},
components/tools/Breadcrumb.vue
<template>
<a-breadcrumb class="breadcrumb">
<a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name">
<router-link
v-if="item.name != name && index != 1"
:to="{ path: item.path === '' ? '/' : item.path }"
>{{ item.meta.title }}
</router-link>
<span v-else>{{ item.meta.title }}</span>
</a-breadcrumb-item>
</a-breadcrumb>
</template>
<script>
export default {
data () {
return {
name: '',
breadList: []
}
},
created () {
this.getBreadcrumb()
},
methods: {
getBreadcrumb () {
this.breadList = []
// this.breadList.push({name: 'index', path: '/', meta: {title: '首页'}})
this.name = this.$route.name
this.$route.matched.forEach(item => {
// item.name !== 'index' && this.breadList.push(item)
this.breadList.push(item)
})
}
},
watch: {
$route () {
this.getBreadcrumb()
}
}
}
</script>
<style scoped>
</style>
Why would you redirect '/settings/user' to '/settings/user/list'? The extra "用户列表"
in the breadcrumb comes from the title of '/settings/user/list'.
I suggest you delete the '/settings/user/list' item and the 'redirect' attribute of '/settings/user'.
I have the following:
children: [
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/basic')
},
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/Basic')
},
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/Basic')
}
]
I want to follow this structure, but programmatically create each dictionary using each record returned from an API call.
example api call
function getData() {
axios.get(Url + input.value)
.then(function (response) {
json_data = response.data;
});
}
so in python this would probably look something like:
test_list=[]
for item in json_data:
dict = {
name: item.name
path: item.path
meta: {
label: item.label,
link: item.link,
},
component: lazyLoading('testitem/basic')
},
test_list.append(dict)
children: test_list
How can I accomplish this in javascript? I'm having a real hard time learning javascript after doing python.
UPDATE:
This is the full code block that I am working with.
export default {
name: 'test',
meta: {
icon: 'fa-android',
expanded: false
},
children: [
{
name: 'test',
path: 'test',
meta: {
label: 'test',
link: 'test'
},
component: lazyLoading('test/Basic')
}
]
}
You were very close:
const children = [];
json_data.forEach(item => {
const dict = {
name: item.name,
path: item.path,
meta: {
label: item.label,
link: item.link,
},
component: lazyLoading('testitem/basic'),
}
children.push(dict);
});