I'm using tree view for my application and I'm getting the id of each when checkbox selected as how it is here:
<blocks-tree class="border-bottom" :data="treeData" :horizontal="treeOrientation==='0'"
:collapsable="true">
<template #node="{data}">
<span>
<input type="checkbox"
:checked="selected.indexOf(data.some_id)> -1"
#change="(e)=>toggleSelect(data,e.target.checked)"/>
{{data.label}}
</span>
</template>
</blocks-tree>
<button class="btn" #click="editState(selected)">
ایجاد ساختمان
<br>
Selected: {{selected}}
</button>
the toggle select is like:
const toggleSelect = (node, isSelected) => {
isSelected ? selected.value.push(node.some_id) : selected.value.splice(selected.value.indexOf(node.some_id), 1);
if(node.children && node.children.length) {
node.children.forEach(ch=>{
toggleSelect(ch,isSelected)
})
}
}
now I want to get the id of the selected node with #click and pass it as params to another route.
can anyone help me with it please?
editState(id) {
this.$router.push({name: 'RealStateForm', params: {id: id}})
}
actually, it relates to Vue-router and in Vue 2 I did like this:
I have a route with these params:
{
path: "/natural-users/:userId?/:committeeId?",
components: {
main: Registration,
top: Top,
right: Right
},
beforeEnter: (to, _from, next) => {
if (!ability.isSelfUser(to.params.userId)) next("/");
next();
},
props: { main: true }
and when I want to redirect to this route I do like this:
this.$router.push(`/natural-users/${this.userID}/${this.commitee}`)
I hope this helps you.
I am trying to have render different components based on the route query produced by clicking tabs. I have tried a dynamic component approach but it is working only on initial load. When I switch by clicking tabs which changes the route query it does not change to the other component. Below are my code and files
Show.vue
<div>
<b-container fluid class="bg-white">
<b-row class="topTab types" v-if="$refs.chart">
<b-col
:class="{ active: currentTab === index }"
v-for="(tabDisplay, index) in $refs.chart.tabDisplays"
:key="index"
>
<router-link
:to="{ query: { period: $route.query.period, tab: index } }"
>
{{ tabDisplay }}
</router-link>
</b-col>
</b-row>
</b-container>
<component v-bind:is="currentExample" ref="chart" />
</div>
export default {
props: {
group: String,
cp: String,
name: String,
id: [Number, String]
},
computed: {
currentExample() {
return () =>
import(
`#/components/Trend/example/Charts/${this.group}/${this.id}/Base.vue`
);
},
}
}
Below is the component used above snippet
Base.vue
<template>
<component v-bind:is="currentData"> </component>
</template>
<script>
export default {
data: function() {
return {
tabDisplays: {
1: "example1",
2: "example2",
3: "example3",
4: "example4",
5: "example5",
6: "example6"
}
};
},
computed: {
currentData() {
return () =>
import(
`#/components/Trend/example/Charts/${this.$route.params.group}/${
this.$route.params.id
}/Tab${this.$route.query.tab === "6" ? "6" : "1-5"}.vue`
);
}
}
};
</script>
I am expecting that if I click the example6 link. It will show my #/components/Trend/example/Charts/${this.group}/${this.id}/Tab6.vue on the <component>
Dynamic components :is expects and identifier not a fully fledged component.
You should import the component under the components property in your parent component and then use the computed property to only get the identifier.
components: {
'comp{group}{id}{tab}': () => import(
`#/components/Trend/example/Charts/{group}/{id}/Tab{tab}.vue`)
}
Note: {group}, {id} and {tab} are only placeholders for your actual values. You need to import all of them.
And use the currentData to only get the identifier:
computed: {
currentData() {
return `comp${this.$route.params.group}${this.$route.params.id}{this.$route.query.tab === "6" ? "6" : "1-5"}`
}
}
I have fixed it by just being a simple.
Base.vue
<template>
<component v-bind:is="currentData"> </component>
</template>
<script>
import Tab from "#/components/Trend/example/Charts/abc/1/Tab1-5.vue";
import Tab6 from "#/components/Trend/example/Charts/abc/1/Tab6.vue";
export default {
computed: {
currentData() {
if (this.$route.query.tab === "6") {
return Tab6;
} else {
return Tab;
}
}
}
};
</script>
My structure allows me to be simple and straightforward on this level
I have a problem to read the passed data via props in Vue.js from parent to child. I have a list of components
components: [
{
name: "Cart Overview",
component: "CartOverview",
props: this.shopperCart
},
{
name: "Bank Account Overview",
component: "BankAccountOverview",
props: {}
},
{ name: "Verification", component: "Verification", props: {} },
{
name: "Completion Message",
component: "CompletionMessage",
props: {}
}
]
The variable "shopperCart" is set by a request from a backend.
Template of the parent component
<div class="modal-body">
<component
:is="checkoutComponents[currentStep].component"
v-bind="checkoutComponents[currentStep].props"
></component>
</div>
The user can navigate through the components with a next step button who sets the variable currentStep.
Example of one child component
<template>
<div>
<h1>Cart Oerview</h1>
{{ shopperCart }}
</div>
</template>
<script>
export default {
name: "CartOverview",
props: {
shopperCart: { type: Object, default: () => {} }
},
mounted() {
console.log("shopperCart", this.shopperCart);
}
};
</script>
The components lie on a modal. The log output only shows up displaying undefined when I refresh the page, where I can open the modal.
Can someone please help me?
Best regards,
A. Mehlen
I found myself a solution. I changed in the parent component the v-bind with :data
<div class="modal-body">
<component
:is="checkoutComponents[currentStep].component"
:data="checkoutComponents[currentStep].props"
></component>
</div>
and in the child component the name of the prop
<template>
<div>
<h1>Cart Oerview</h1>
{{ data }}
</div>
</template>
<script>
export default {
name: "CartOverview",
props: {
data: { type: Object, default: () => {} }
},
mounted() {
console.log("shopperCart", this.data);
}
};
</script>
Now it works :-)
I'm using Vue CLI 3 and it makes a few routes. One is Home.vue. In my program I am trying to programmaticaly go to different pages. I added the routes I need in router.js but kept the already created routes for Home.vue and About.vue. It works fine until I get to 'Home' and get a warning: [vue-router] Route with name 'Home' does not exist.'
Here is the code:
<template>
<div class='secondItem'>
<h4 v-for="item in menuItems"
#click="bindMe(item)" v-bind:class="{'active':(item === current)}">{{item}}</h4>
</div>
</template>
<script>
export default {
name: 'Header',
data() {
return {
current: '',
menuItems: ['Home', 'About', 'Portfolio', 'Contact'],
}
},
methods: {
bindMe(item) {
this.current = item;
this.$router.push({
path: item
})
}
}
}
<script>
Are you using named routes? In that case you need to use name instead of path:
this.$router.push({
name: item
})
Also, your example can be simplified quite a lot. Try this:
<template>
<div class="secondItem">
<router-link :to="{ name: item }" tag="h4" active-class="active" v-for="item in menuItems" v-bind:key="item">{{item}}</router-link>
</div>
</template>
<script>
export default {
name: 'Header',
data() {
return {
menuItems: ['Home', 'About', 'Portfolio', 'Contact']
}
}
}
<script>
Hopefully this is a rather simple question / answer, but I can't find much info in the docs.
Is there a way to enable or disable the anchor generated by <router-link> dependent on whether a prop is passed in or not?
<router-link class="Card__link" :to="{ name: 'Property', params: { id: id }}">
<h1 class="Card__title">{{ title }}</h1>
<p class="Card__description">{{ description }}</p>
</router-link>
If there's no id passed to this component, I'd like to disable any link being generated.
Is there a way to do this without doubling up the content into a v-if?
Thanks!
Assuming you want to disable anchor tag as in not clickable and look disabled the option is using CSS. isActive should return true by checking prop id.
<router-link class="Card__link" v-bind:class="{ disabled: isActive }" :to="{ name: 'Property', params: { id: id }}">
<h1 class="Card__title">{{ title }}</h1>
<p class="Card__description">{{ description }}</p>
</router-link>
<style>
.disabled {
pointer-events:none;
opacity:0.6;
}
<style>
If you want to just disable the navigation , you can use a route guard.
beforeEnter: (to, from, next) => {
next(false);
}
If you need to use it often, consider this:
Create new component
<template>
<router-link
v-if="!disabled"
v-bind="$attrs"
>
<slot/>
</router-link>
<span
v-else
v-bind="$attrs"
>
<slot/>
</span>
</template>
<script>
export default {
name: 'optional-router-link',
props: {
params: Object,
disabled: {
type: Boolean,
default: false,
},
},
};
</script>
Optional, register globally:
Vue.component('optional-router-link', OptionalRouterLink);
Use it as follows:
<optional-router-link
:disabled="isDisabled"
:to="whatever"
>
My link
</optional-router-link>
The problem is that router-link renders as an html anchor tag, and anchor tags do not support the disabled attribute. However you can add tag="button" to router-link:
<router-link :to="myLink" tag="button" :disabled="isDisabled" >
Vue will then render your link as a button, which naturally supports the disabled attribute. Problem solved! The downside is that you have to provide additional styling to make it look like a link. However this is the best way to achieve this functionality and does not rely on any pointer-events hack.
I sometimes do stuff like this:
<component
:is="hasSubLinks ? 'button' : 'router-link'"
:to="hasSubLinks ? undefined : href"
:some-prop="computedValue"
#click="hasSubLinks ? handleClick() : navigate"
>
<!-- arbitrary markup -->
</component>
...
computed: {
computedValue() {
if (this.hasSubLinks) return 'something';
if (this.day === 'Friday') return 'tgif';
return 'its-fine';
},
},
But I basically always wrap router-link, so you can gain control over disabled state, or pre-examine any state or props before rendering the link, with something like this:
<template>
<router-link
v-slot="{ href, route, navigate, isActive, isExactActive }"
:to="to"
>
<a
:class="['nav-link-white', {
'nav-link-white-active': isActive,
'opacity-50': isDisabled,
}]"
:href="isDisabled ? undefined : href"
#click="handler => handleClick(handler, navigate)"
>
<slot></slot>
</a>
</router-link>
</template>
<script>
export default {
name: 'top-nav-link',
props: {
isDisabled: {
type: Boolean,
required: false,
default: () => false,
},
to: {
type: Object,
required: true,
},
},
data() {
return {};
},
computed: {},
methods: {
handleClick(handler, navigate) {
if (this.isDisabled) return undefined;
return navigate(handler);
},
},
};
</script>
In my app right now, I'm noticing that some combinations of #click="handler => handleClick(handler, navigate)" suffer significantly in performance.
For example this changes routes very slow:
#click="isDisabled ? undefined : handler => navigate(handler)"
But the pattern in my full example code above works and has no performance issue.
In general, ternary operator in #click can be very dicey, so if you get issues, don't give up right away, try many different ways to bifurcate on predicates or switch over <component :is="" based on state. navigate itself is an ornery one because it requires the implicit first parameter to work.
I haven't tried, but you should be able to use something like Function.prototype.call(), Function.prototype.apply(), or Function.prototype.bind().
For example, you might be able to do:
#click="handler => setupNavigationTarget(handler, navigate)"
...
setupNavigationTarget(handler, cb) {
if (this.isDisabled) return undefined;
return this.$root.$emit('some-event', cb.bind(this, handler));
},
...
// another component
mounted() {
this.$root.$on('some-event', (navigate) => {
if (['Saturday', 'Sunday'].includes(currentDayOfTheWeek)) {
// halt the navigation event
return undefined;
}
// otherwise continue (and notice how downstream logic
// no longer has to care about the bound handler object)
return navigate();
});
},
You could also use the following:
<router-link class="Card__link" :to="id ? { name: 'Property', params: { id: id }} : {}">
<h1 class="Card__title">{{ title }}</h1>
<p class="Card__description">{{ description }}</p>
</router-link>
If id is undefined the router won't redirect the page to the link.
I've tried different solutions but only one worked for me, maybe because I'm running if from Nuxt? Although theoretically nuxt-link should work exactly the same as router-link.
Anyway, here is the solution:
<template>
<router-link
v-slot="{ navigate }"
custom
:to="to"
>
<button
role="link"
#click="onNavigation(navigate, $event)"
>
<slot></slot>
</button>
</router-link>
</template>
<script>
export default {
name: 'componentName',
props: {
to: {
type: String,
required: true,
},
},
methods: {
onNavigation(navigate, event) {
if (this.to === '#other-action') {
// do something
} else {
navigate(event);
}
return false;
},
};
</script>