I'm new in Vuejs and I want to change the selected tab of nav on click event I trying using function but console throw an error:
vue.runtime.global.js:8392 Uncaught TypeError: _ctx.changeTab is not a
function
CodePen
What I want to do, is to on #click event run a function that change selected tab and show content depending on the selected tab in a single paragraph element
Code:
<template>
<div>
<div class="sm:hidden">
<label for="tabs" class="sr-only">Select a tab</label>
<select id="tabs" name="tabs" class="block w-full focus:ring-indigo-500 focus:border-indigo-500 border-gray-300 rounded-md">
<option v-for="tab in tabs" :key="tab.name" :selected="tab.current">{{ tab.name }}</option>
</select>
</div>
<div class="hidden sm:block">
<nav class="flex space-x-4" aria-label="Tabs" :class="bg-gray-600">
<a v-for="tab in tabs" #click="changeTab(tab)" :key="tab.name" :href="tab.href" :class="[tab.current ? 'bg-purple-700 text-white' : 'text-purple-700 hover:text-gray-700', 'px-12 py-2 font-medium text-sm rounded-full font-bold text-lg']" >
{{ tab.name }}
</a>
</nav>
</div>
</div>
</template>
<script>
const tabs = [
{ id: 1 , name: 'LOREM', href: '#test1', current: false },
{ id: 2, name: 'IPSUM', href: '#test2', current: false },
{ id: 3, name: 'PDF', href: '#test3', current: true },
]
export default {
setup() {
return {
tabs,
}
function changeTab(selectedTab){
let test = this.tabs.find(selectedTab.id);
console.log(test)
}
},
}
</script>
<style>
nav {
width: max-content;
display: flex;
gap: 20px;
background: #E5E5E5;
border-radius: 20px;
}
</style>
How can I achieve this? Regards
Move your array to setup function, make it reactive with ref or reactive:
const { ref } = Vue
const app = Vue.createApp({
setup() {
const tabs = ref([
{ id: 1 , name: 'LOREM', href: '#test1', current: false },
{ id: 2, name: 'IPSUM', href: '#test2', current: false },
{ id: 3, name: 'PDF', href: '#test3', current: true },
])
const changeTab = (selectedTab) => {
tabs.value.map(t => {
t.id === selectedTab.id ? t.current = true : t.current = false
});
}
return { tabs, changeTab }
},
})
app.mount('#demo')
nav {
width: max-content;
display: flex;
gap: 20px;
background: #E5E5E5;
border-radius: 20px;
}
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/1.0.5/tailwind.min.css">
<div id="demo">
<div>
<div class="">
<nav class="flex space-x-4 bg-gray-600" aria-label="Tabs" >
<a v-for="tab in tabs" #click="changeTab(tab)" :key="tab.name" :href="tab.href" :class="[tab.current ? 'bg-purple-700 text-white' : 'text-purple-700 hover:text-gray-700', 'px-12 py-2 font-medium text-sm rounded-full font-bold text-lg']" >
{{ tab.name }}
</a>
</nav>
</div>
<div v-for="tab in tabs" #click="changeTab(tab)" :key="tab.name" :href="tab.href" class="px-12" :class="[tab.current || 'hidden']">
{{ tab.id }} - {{ tab.name }} - {{ tab.href }}
</div>
</div>
</div>
Related
i'm making a questions list and i'm trying to change the color of a question in the list with v-model and v-for for options but when change the color of one question it changes all question at once
<template>
<div class="container" id="app">
<h1>questions list</h1>
<div class="pol">
<input type="text" v-model="que" :class="inputCls" autofocus>
<span class="addBtn">
<button #click="inputCls='inputbox extend'"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
v-if="!showIcon">Add questions</button>
<button #click="addqueS"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
v-else>Add</button>
</span>
</div>
<div class="section">
<ul class="queS" v-if="queS.length > 0">
<transition-group name="list">
<li :class="bgColour" v-for="(item) in queS" :key="item">
<span>{{ item.task }}</span>
<span>
<button #click="deleteque()"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-full">Delete</button>
<select class="bg-gray-500" v-model="bgColour">
<option v-for="myClass in classes" :value="myClass">{{ myClass }}
</option>
</select>
</span>
</li>
</transition-group>
</ul>
<h3 v-else>No question to display. Add one.</h3>
</div>
</div>
</template>
the delete methode is working perfectly i tried to implemnt the same methode but it didint work out im still new to vuejs
export default {
name: "App",
data() {
return {
que: '',
queS: [],
inputCls: 'inputbox',
bgColour: 'white',
classes: [ 'Blue', 'Red', 'Green'
],
};
},
watch: {
que(value) {
if(value.length > 2) {
this.showIcon = true;
}
else {
this.showIcon = false;
}
}
},
methods: {
addqueS() {
this.inputCls = 'inputbox';
this.queS.unshift(
{
task: this.que,
completed: false
}
);
this.que = '';
setTimeout(() => {
this.showIcon = false;
}, 1000);
},
deleteque(index) {
this.queS.splice(index, 1);
},
},
};
CSS
<script>
.Blue {
background: blue;
}
.Red {
background: red;
}
.Green {
background: green;
}
</script>
You can add bgColor to queS array item, and connect v-model for select to that:
new Vue({
el: "#demo",
data() {
return {
que: '',
queS: [],
showIcon: true,
inputCls: 'inputbox',
bgColour: 'white',
classes: [ 'Blue', 'Red', 'Green'],
};
},
watch: {
que(value) {
if (value.length > 2) {
this.showIcon = true;
}
else {
this.showIcon = false;
}
}
},
methods: {
addqueS() {
this.inputCls = 'inputbox';
this.queS.unshift({ task: this.que, completed: false });
this.que = '';
setTimeout(() => {
this.showIcon = false;
}, 1000);
},
deleteque(index) {
this.queS.splice(index, 1);
},
},
})
.Blue {
background: blue;
}
.Red {
background: red;
}
.Green {
background: green;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" integrity="sha512-wnea99uKIC3TJF7v4eKk4Y+lMz2Mklv18+r4na2Gn1abDRPPOeef95xTzdwGD9e6zXJBteMIhZ1+68QC5byJZw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="container" id="demo">
<h1>questions list</h1>
<div class="pol">
<input type="text" v-model="que" :class="inputCls" autofocus>
<span class="addBtn">
<button #click="inputCls='inputbox extend'"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
v-if="!showIcon">Add questions</button>
<button #click="addqueS"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full"
v-else>Add</button>
</span>
</div>
<div class="section">
<ul class="queS" v-if="queS.length > 0">
<transition-group name="list">
<!-- 👇 here connect to -->
<li :class="item.bgColour" v-for="(item, idx) in queS" :key="idx">
<span>{{ item.task }}</span>
<span>
<button #click="deleteque()"
class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded-full">Delete</button>
<select class="bg-gray-500" v-model="item.bgColour"> <!-- 👈 here change v-model -->
<option v-for="myClass in classes" :value="myClass">{{ myClass }}</option>
</select>
</span>
</li>
</transition-group>
</ul>
<h3 v-else>No question to display. Add one.</h3>
</div>
</div>
AppsTab.vue
<script>
export default {
props: {
tabList: {
type: Array,
required: true,
},
variant: {
type: String,
required: false,
default: () => "vertical",
},
},
data() {
return {
activeTab: 1,
};
},
};
</script>
<template>
<div
:class="{
'flex space-x-4': variant === 'horizontal',
}"
>
<ul
class="list-none bg-blue-900 bg-opacity-30 p-1.5 rounded-lg text-center overflow-auto whitespace-nowrap"
:class="{
'flex items-center mb-6': variant === 'vertical',
}"
>
<li
v-for="(tab, index) in tabList"
:key="index"
class="w-full px-4 py-1.5 rounded-lg"
:class="{
'text-blue-600 bg-white shadow-xl': index + 1 === activeTab,
'text-white': index + 1 !== activeTab,
}"
>
<label
:for="`${_uid}${index}`"
v-text="tab"
class="cursor-pointer block"
/>
<input
:id="`${_uid}${index}`"
type="radio"
:name="`${_uid}-tab`"
:value="index + 1"
v-model="activeTab"
class="hidden"
/>
</li>
</ul>
<template v-for="(tab, index) in tabList">
<div
:key="index"
v-if="index + 1 === activeTab"
class="flex-grow bg-white rounded-lg shadow-xl p-4"
>
<!-- <slot :name="`tabPanel-${index + 1}`" /> --> want to get data using loop inside tab
</div>
</template>
</div>
</template>
App.vue
< script >
import AppTabs from "./components/AppTabs";
export default {
components: {
AppTabs,
},
data() {
return {
tabList: ["Tab 1", "Tab 2", "Tab 3", "Tab 4"],
};
},
}; <
/script>
<template>
<div class="bg-gradient-to-r from-blue-300 to-blue-500 min-h-screen">
<app-tabs
class="w-11/12 lg:w-10/12 mx-auto"
:tabList="tabList"
variant="horizontal"
>
{{value}} // want to bind data inside tab
</app-tabs>
</div>
</template>
Expected Output
I am working on vertical tabs. Where the functionality is working fine. Here is the complete working code with static mock data https://codesandbox.io/s/vue-js-tabs-forked-we2cx?file=/src/App.vue
Now i want to create some mockdata inside of my data like 'tabList' and then, i want to display data dynamically when user clicked on tabs(including content -->tabs)
How to remove static data, which is inside slots and then only use data dynamically
To start with that, i am not sure, Where to start the looping to display data inside tabs dynamically with mock data?
You can set dynamic slot name using :slot="slotName" where slotName is a dynamic value
This can be achieved using a v-for aswell like below.
<template v-for="content in contentList" :slot="content.slot">
{{ content.content }}
</template>
Where contentList is your array something like below.
contentList: [
{ id: 1, slot: "tabPanel-1", content: "Content 1" },
{ id: 2, slot: "tabPanel-2", content: "Content 2" },
{ id: 3, slot: "tabPanel-3", content: "Content 3" },
{ id: 4, slot: "tabPanel-4", content: "Content 4" },
]
Working Fiddle
Ok...king..
I'm trying to pass a value to child when a click event occur from other component it will change the prop value from parent. But it only shown the first mount value that pass.
topdown component that emit change
<template>
<div class="dropdown">
<button
class="btn btn-secondary dropdown-toggle h-75"
type="button"
id="dropdownMenuButton1"
data-bs-toggle="dropdown"
aria-expanded="false"
>
{{ value }}
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1" role="menu">
<li v-for="option in options" :key="option">
<a
class="dropdown-item"
#click="(value = option);dunder;dundet;"
href="javascript:void(0)"
>{{ option }}</a
>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "TopDown",
data() {
return {
options: ["Edit", "Delete"],
value: "",
};
},
computed:{
dunder(){
return this.$emit("edit-task", this.value)
},
dundet(){
return this.$emit("edit-task-index",this.value)
}
}
};
</script>
<style>
</style>
parent component
<template>
<div
v-for="(item, index) in tasker"
:items="item"
:key="index"
class="border border-dark "
>
<section class="d-flex justify-content-between">
<h4 class="w-50 font-weight-bold fs-5">
<u>{{ item.title }}</u>
</h4>
<TopDown
#edit-task="val"
#edit-task-of="show(item, index)"
:index="index"
/>
</section>
<p class="text-start">{{ item.description }}</p>
<GoTask :showVal="showVal" :bool="bool" />
</div>
</template>
<script>
import TopDown from "#/components/TopDown.vue";
import GoTask from "#/components/GoTask.vue";
export default {
inheritAttrs: false,
components: {
TopDown,
GoTask,
},
data() {
return {
takss: {
items: "sss",
index: "",
},
showVal: "",
bool: false,
};
},
name: "Taski",
props: ["tasker"],
methods: {
show(item, index) {
this.takss.items = item;
this.takss.index = index;
},
val(val) {
if (val == "Edit") {
setTimeout(() => {
console.log(this.takss);
this.showval = this.takss;
console.log(this.showval);
console.log(this.bool)
}, 1000);
this.bool = !this.bool;
}
},
},
};
</script>
<style></style>
child component
<template>
<section
v-if="bools"
class="bg-white"
style="z-index: 20"
#click="closeModal"
></section>
<section
v-if="bools"
class="position-absolute"
style="
z-index: 50;
left: 50%;
top: 50%;
height: 100vh;
margin-top: 20vh;
"
>
<form
class="mt-10"
#submit.prevent="editTask"
>
<input
class="border rounded p-2 mb-2"
v-model.lazy="newTask.title"
placeholder="Title"
type="text"
/>
<textarea
ref="texsearch"
rows="20"
class=" p-2 mb-2"
v-model.lazy="newTask.description"
placeholder="Task Details"
type="text"
></textarea>
<button
class="border rounded p-2 bg-success text-white"
type="submit"
#submit.prevent=""
>
New Task
</button>
</form>
</section>
<button #click="test">dd</button>
</template>
<script>
export default {
inheritAttrs: false,
name: "GoTask",
props: ["tasker", "showVal", "bool"],
data() {
return {
showVals: this.showVal,
bools: this.bool,
newTask: {
title: "",
description: "",
},
};
},
methods: {
test() {
console.log(this.bools);
console.log(this.showVal);
},
ModalOpen() {
this.bools = true;
},
closeModal() {
this.bools = false;
},
showModal() {
this.bools = true;
// auto focus
this.$nextTick(() => {
this.$refs.textsearch.focus();
});
},
showtheVal() {
console.log(this.showtheVal);
},
},
};
</script>
<style></style>
When I click the button form other component that emit change of #edit-task-of and #edit-task the it doesn't send the new value of bool and showval to child component the bool value still false as same as first mount and showVal = "" when click button that run test function to see this two value at child. I'm totally new to vue. Thanks
You do mistake in GoTask component , method test, first console.log(this.bools);, bools is local state which is not updated after prop bool changed, just do correct console.log(this.bool);. You don't need to set props to local state, you can use props directly.
GoTask correct example:
props: ["tasker", "showVal", "bool"],
data() {
return {
newTask: {
title: "",
description: "",
},
};
},
methods: {
test() {
console.log(this.bool);
console.log(this.showVal);
},
codesandbox
Trying to create a dynamic form with a list of each product from a db. The form has the following fields for each product: product title, product price, quantity and total price. My issue is in the fact that I'm not sure how to add a v-model field for each product quantity input, since the list is being pulled from a v-for of all of the products. Here is part of my ProductsListForm vue component template:
<div v-for="product in products" :key="product.id" class="flex form-group">
<div class="flex p-title">
<label :for="product.title">{{ product.title }}</label>
<small>{{ product.price }}$</small>
</div>
<div class="flex p-input">
<input class="input" type="number" :name="product.title" v-model="quantity">
</div>
<div class="flex p-total">
<span>Total: {{ product.price * quantity}}</span>
</div>
</div>
export default {
props: ['products'],
data() {
return {
quantity: 0,
}
},
methods: {}
}
So my question is how can I bind quantity to each individual product? Right now, it obviously changes whenever ANY of the input fields are updated...
Any help will be greatly appreciated!
Rather than using v-model, you could instead listen for the input events on each element.
new Vue({
el: '#app',
data: () => ({
cart: [],
products: [{
id: 1,
name: 'foo'
},
{
id: 2,
name: 'bar'
},
{
id: 3,
name: 'baz'
}
]
}),
methods: {
updateCart(event, product) {
const index = this.cart.findIndex(i => i.name === product.name)
const item = {
name: product.name,
quantity: event.target.value
}
if (index !== -1) {
this.cart.splice(index, 1, item)
} else {
this.cart.push(item)
}
}
}
})
ul,
li {
list-style: none;
}
#app {
display: flex;
flex-direction: row;
justify-content: space-around;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div>
<h5>products</h5>
<ul>
<li v-for="product in products" :key="product.id">
<label>{{ product.name }}</label>
<input #input="updateCart($event, product)" min="0" type="number">
</li>
</ul>
</div>
<div>
<h5>cart</h5>
<ul v-if="cart.length">
<li v-for="item in cart" :key="item.id">
<p>{{ item.name }} {{ item.quantity }}</p>
</li>
</ul>
<p v-else>no items in cart</p>
</div>
</div>
For the quantity to be related to the product (entry of products) - you will have to pass it to the products or make it an array by itself.
<div v-for="product in products" :key="product.id" class="flex form-group">
<div class="flex p-title">
<label :for="product.title">{{ product.title }}</label>
<small>{{ product.price }}$</small>
</div>
<div class="flex p-input">
<input class="input" type="number" placeholder="1" :name="product.title" v-model="quantity[product.id]">
</div>
<div class="flex p-total">
<span>Total: {{ product.price * getProductQuantity(product) }}</span>
</div>
</div>
export default {
props: ['products'],
data() {
return {
quantity: [],
}
},
methods: {
getProductQuantity(product) {
return this.quantity[product.id] || 0;
}
}
}
I'm trying to make a list item clickable. When the item is clicked the checkbox within the list item should be enabled or disabled. However it's not working the way I expect.
I've made an example:
var app = new Vue({
el: '#app',
data: {
showNav: false,
categories: [{name: 'test' }]
},
mounted() {
this.categories.forEach((category) => {
category.active = true;
});
}
})
<div id="app">
<nav class="navbar-dropdown w-full">
<ul class="list-reset">
<li class="flex justify-between items-center hover:bg-grey hover:text-white text-grey uppercase cursor-pointer p-3" v-for="category in categories" #click="category.active = !category.active">
{{ category.name }}
<input type="checkbox" class="shadow" v-model="category.active" #click="category.active = !category.active"/>
</li>
</ul>
</nav>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss/dist/tailwind.min.css" rel="stylesheet"/>
When I change this:
categories: [{name: 'test' }]
to this:
categories: [{name: 'test', active: true }]
It's working. But in my application I fetch the application with an ajax and receive the category objects without an active property.
That's why I'm doing this:
this.categories.forEach((category) => {
category.active = true;
});
But that's obviously not working. How could I fix this?
As #ohgodwhy mentioned in his comment there's an issue with the way you define a property for a category. I'm having a hard time explaining why exactly this doesn't work, although this is how you could do this:
var app = new Vue({
el: '#app',
data: {
showNav: false,
categories: [{
name: 'test'
}],
},
mounted() {
this.categories = this.categories.map((category) => {
return {
name: 'test',
active: true,
};
});
},
});
<div id="app">
<ul class="list-reset">
<li class="flex justify-between items-center hover:bg-grey hover:text-white text-grey uppercase cursor-pointer p-3" v-for="category in categories" #click="category.active = !category.active">
{{ category.name }}
<input type="checkbox" class="shadow" v-model="category.active" #click="category.active = !category.active" />
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
I would appreciate any additions or expansions on the reason why OP has this issue.