I would like to create a tab using vue and bulma, but I'm getting the following error:
error: 'openTab' is defined but never used (no-unused-vars) at
code:
<template>
<div>
<div class="tabs is-centered">
<ul>
<li class="tab is-active" onclick="openTab(e,'adult')"><a>Adult</a></li>
<li class="tab" onclick="openTab(e,'card')"><a>Card</a></li>
<li class="tab" onclick="openTab(e,'food')"><a>Food</a></li>
</ul>
</div>
<div id="adult" class="content-tab">
<img :src="require(`../../static/dst/${parentData.file_name}`)">
<div v-bind:style="{ color: `${parentData.font_color}`}">
{{parentData.info_text}}<br>
{{parentData.rate_adult}}{{parentData.part_name}}<br>
{{parentData.rate_part}}
</div>
</div>
<div id="card" class="content-tab" style="display: none">
<div :style="{ color: `${parentData.card_color}`}">
{{parentData.card_text}}
</div>
</div>
<div id="food" class="content-tab" style="display: none">
{{parentData.class_name}}
</div>
</div>
</template>
<script>
export default {
name: 'Child',
props: ['parentData'],
openTab(e, tabName) {
var i, x, tablinks;
x = document.getElementsByClassName("content-tab");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tab");
for (i = 0; i < x.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" is-active", "");
}
document.getElementById(tabName).style.display = "block";
e.currentTarget.className += " is-active";
}
}
</script>
I want to find the answer to this problem.
Tap function does not apply.
Process:
Upload an image
Return json contents
Check by tap
Can you tell me what the error is?
Your openTabs function should be part of methods. So:
props: {},
methods: {
openTab()
}
More information here: https://v2.vuejs.org/v2/guide/events.html#Method-Event-Handlers
Related
export default {
name: 'MovieSlider',
data() {
return {
index: 0,
imgs: [
'https://7themes.su/_ph/40/303277371.jpg',
'https://7themes.su/_ph/23/730607462.jpg',
'https://7themes.su/_ph/23/730607462.jpg',
],
};
},
computed: {
sideSlide() {
return {
transform: `translateX(${-this.index * 100}vw)`,
};
},
},
methods: {},
left() {
this.index++;
if (this.index > this.imgs.length - 1) {
this.index = 0;
}
this.sideSlide;
},
right() {
this.index--;
if (this.index < 0) {
this.index = this.imgs.length - 1;
}
this.sideSlide;
},
};
<template>
<div class="carusel">
<div class="image-container imgs">
<button class="left buttons" #click="left">
<i class="fa-solid fa-angle-left"></i>
</button>
<div v-for="item in imgs" :key="item.id" :style="sideSlide">
<img :src="item" />
</div>
<button class="right buttons" #click="right">
<i class="fa-solid fa-chevron-right"></i>
</button>
</div>
<div class="popular_films">
<div class="content">
<p class="film_name">Film Name</p>
<p class="film_info">
<i class="fa-solid fa-star"></i>
<span>FIlm vote</span>
<span>Film time</span>
<span>Film year</span>
</p>
<span class="play_treiler"
>Trailer<i class="fa-solid fa-play"></i
></span>
</div>
</div>
</div>
</template>
Hi friends, I want to make slider with vue but something is going wrong, after pressing right or left i want to give a style(size) to image that slides to another image but something is going wrong, in here you can find all of the code that i used in this component, Thank you.
I tried to make a chat app using Vue CLI 3 and I have finished making a real-time chat room. Then, I tried to give a citing function to it which users can cite the message before and reply to it. So, I manage to pass the cited message to the child component by props. The cited message was NULL by default. After the user clicked some buttons, I expected the value of the "cited message" would change and the new value would be passed to the child through props (automatically updated). But, in fact, it didn't.
When I was browsing the internet, I did find several questions about updating the child component when props values change. So, I tried watch:, created(), update(), but none of them worked.
I once tried to directly add an element <p> in the child component and put {{cited_message}} in it to see what was inside the variable. Then, the Vue app crashed with a white blank page left (but the console didn't show any error).
For convenience, I think the problem is around:
<CreateMessage:name="name":cited_message="this.cited_message"#interface="handleFcAfterDateBack"/>
OR
props: ["name","cited_message"],
watch: { cited_message: function (newValue){ this.c_message = newValue; } },
You can ctrl+F search for the above codes to save your time.
Parent component:
<template>
<div class="container chat">
<h2 class="text-primary text-center">Real-time chat</h2>
<h5 class="text-secondary text-center">{{ name }}</h5>
<div class="card" style="min-height: 0.8vh">
<div class="card-body">
<p class="text-secondary nomessages" v-if="messages.length == 0">
[no messages yet!]
</p>
<div class="messages" v-chat-scroll="{ always: false, smooth: false }">
<div v-for="message in messages" :key="message.id">
<div v-if="equal_name(message)">
<div class="d-flex flex-row">
<div class="text-info">
[ {{ message.name }} ] : {{ message.message }}
</div>
<div class="btn-group dropright">
<a
class="btn btn-secondary btn-sm dropdown-toggle"
href="#"
role="button"
id="dropdownMenuLink"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
</a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<button
#click="get_cited_message(message)"
:key="message.id"
class="dropdown-item"
href="#"
>
Cite
</button>
</div>
</div>
<div class="text-secondary time">
<sub>{{ message.timestamp }}</sub>
</div>
</div>
<!--below is for cited message-->
<div v-if="message.cited_message" class="d-flex flex-row">
Cited : {{ message.cited_message }}
</div>
</div>
<div v-else>
<div class="d-flex flex-row-reverse">
<div class="text-info">
[ {{ message.name }} ] : {{ message.message }}
</div>
<div class="text-secondary time">
<sub>{{ message.timestamp }}</sub>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-action">
<CreateMessage
:name="name"
:cited_message="this.cited_message"
#interface="handleFcAfterDateBack"
/>
</div>
</div>
</div>
</template>
<script>
import CreateMessage from "#/components/CreateMessage";
import fb from "#/firebase/init.js";
import moment from "moment";
export default {
name: "Chat",
props: {
name: String,
},
components: {
CreateMessage,
},
methods: {
equal_name(message) {
if (message.name == this.name) {
return true;
} else {
return false;
}
},
get_cited_message(message) {
this.cited_message = message.message;
console.log(this.cited_message);
},
handleFcAfterDateBack(event) {
console.log("data after child handle: ", event);
},
},
data() {
return {
messages: [],
cited_message: null,
};
},
created() {
let ref = fb.collection("messages").orderBy("timestamp");
ref.onSnapshot((snapshot) => {
snapshot.docChanges().forEach((change) => {
change.type = "added";
if (change.type == "added") {
let doc = change.doc;
this.messages.push({
id: doc.id,
name: doc.data().name,
message: doc.data().message,
timestamp: moment(doc.data().timestamp).format(
"MMMM Do YYYY, h:mm:ss a"
),
cited_message: doc.data().cited_message,
});
}
});
});
},
};
</script>
<style>
.chat h2 {
font-size: 2.6em;
margin-bottom: 0px;
}
.chat h5 {
margin-top: 0px;
margin-bottom: 40px;
}
.chat span {
font-size: 1.2em;
}
.chat .time {
display: block;
font-size: 0.7em;
}
.messages {
max-height: 300px;
overflow: auto;
text-align: unset;
}
.d-flex div {
margin-left: 10px;
}
</style>
Child component:
<template>
<div class="container" style="margin-bottom: 30px">
<form #submit.prevent="createMessage()">
<div class="form-group">
<input
type="text"
name="message"
class="form-control"
placeholder="Enter your message"
v-model="newMessage"
/>
<p v-if="c_message" class="bg-secondary text-light">Cited: {{c_message}}</p>
<p class="text-danger" v-if="errorText">{{ errorText }}</p>
</div>
<button class="btn btn-primary" type="submit" name="action">
Submit
</button>
</form>
</div>
</template>
<script>
import fb from "#/firebase/init.js";
import moment from "moment";
export default {
name: "CreateMessage",
props: ["name","cited_message"],
watch: {
cited_message: function (newValue){
this.c_message = newValue;
}
},
data() {
return {
newMessage: "",
errorText: null,
c_message: null
};
},
methods: {
createMessage() {
if (this.newMessage) {
fb.collection("messages")
.add({
message: this.newMessage,
name: this.name,
timestamp: moment().format(),
cited_message: this.c_message
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch((err) => {
console.log(err);
});
this.newMessage = null;
this.errorText = null;
} else {
this.errorText = "Please enter a message!";
}
},
},
beforeMount(){
this.c_message = this.cited_message;
}
};
</script>
Side-note: In the parent component, I only made the dropdown menu for the messages on the left-hand side. If this thread solved, I would finish the right-hand side.
It is solved. I think the problem is that the child component didn't re-render when the variable in parent component updated. Only the parent component was re-rendered. So, the props' values in the child remained the initial values. In order to solve this, binding the element with v-bind:key can let the Vue app track the changes of the variable (like some kind of a reminder that reminds the app to follow the changes made on the key). When the variable(key) changes, the app will be noticed and the new value will be passed to the child.
E.g.
Original
<CreateMessage
:name="name"
:cited_message="this.cited_message"
#interface="handleFcAfterDateBack"
/>
Solved
<CreateMessage
:name="name"
:cited_message="this.cited_message"
#interface="handleFcAfterDateBack"
:key="this.cited_message"
/>
Even though the problem is solved, I don't know whether I understand the problem clearly. If I made any mistakes, please comment and let me know.
i want to bind my button only on the element that i added to the cart, it's working well when i'm not in a loop but in a loop anything happen. i'm not sure if it was the right way to add the index like that in order to bind only the item clicked, if i don't put the index every button on the loop are binded and that's not what i want in my case.
:loading="isLoading[index]"
here the vue :
<div class="container column is-9">
<div class="section">
<div class="columns is-multiline">
<div class="column is-3" v-for="(product, index) in computedProducts">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="" alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="content">
<div class="media-content">
<p class="title is-4">{{product.name}}</p>
<p class="subtitle is-6">Description</p>
<p>{{product.price}}</p>
</div>
</div>
<div class="content">
<b-button class="is-primary" #click="addToCart(product)" :loading="isLoading[index]"><i class="fas fa-shopping-cart"></i> Ajouter au panier</b-button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
here the data :
data () {
return {
products : [],
isLoading: false,
}
},
here my add to cart method where i change the state of isLoading :
addToCart(product) {
this.isLoading = true
axios.post('cart/add-to-cart/', {
data: product,
}).then(r => {
this.isLoading = false
}).catch(e => {
this.isLoading = false
});
}
You can change your isLoading to an array of booleans, and your addToCart method to also have an index argument.
Data:
return {
// ...
isLoading: []
}
Methods:
addToCart(product, index) {
// ...
}
And on your button, also include index:
#click="addToCart(product, index)"
By changing isLoading to an empty array, I don't think isLoading[index] = true will be reactive since index on isLoading doesn't exist yet. So you would use Vue.set in your addToCart(product, index) such as:
this.$set(this.isLoading, index, true)
This will ensure that changes being made to isLoading will be reactive.
Hope this works for you.
add on data productsLoading: []
on add to cart click, add loop index to productsLoading.
this.productsLoading.push(index)
after http request done, remove index from productsLoading.
this.productsLoading.splice(this.productoading.indexOf(index), 1)
and check button with :loading=productsLoading.includes(index)
You can create another component only for product card,
for better option as show below
Kindly follow this steps.
place the content of card in another vue component as shown below.
<!-- Product.vue -->
<template>
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src="" alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="content">
<div class="media-content">
<p class="title is-4">{{product.name}}</p>
<p class="subtitle is-6">Description</p>
<p>{{product.price}}</p>
</div>
</div>
<div class="content">
<b-button class="is-primary" #click="addToCart(product)" :loading="isLoading"><i class="fas fa-shopping-cart"></i> Ajouter au panier</b-button>
</div>
</div>
</div>
</templete>
<script>
export default {
name: "Product",
data() {
return {
isLoading: false
}
},
props: {
product: {
type: Object,
required: true
}
},
methods: {
addToCart(product) {
this.isLoading = true
axios.post('cart/add-to-cart/', {
data: product,
}).then(r => {
this.isLoading = false
}).catch(e => {
this.isLoading = false
});
}
}
}
</script>
Change your component content as shown below.
<template>
<div class="container column is-9">
<div class="section">
<div class="columns is-multiline">
<div class="column is-3" v-for="(product, index) in computedProducts">
<product :product="product" />
</div>
</div>
</div>
</div>
</templete>
<script>
import Product from 'path to above component'
export default {
components: {
Product
}
}
</script>
so in the above method you can reuse the component in other components as well.
Happy coding :-)
I have an input-component which has a form which collects start and finish times, job number and a select option.
This is attached to a data property with v-model.
This is then emitted with Event.$emit('addedData', this.hours)
In the display-component the Event.$on takes this data and checks an attribute and based on the check adds it to another data property (array) with this.todays_hours.push().
The template then displays this reactively using v-for in the template.
To this point all works fine. However when I then attempt to add another line of hours the hours already displayed change reactively with the input.
As my input-component also posts to a database with axios if I reload the page all is displayed correctly.
input-component
<template>
<div>
<div class="row">
<div class="col-2">
<input hidden="" v-model="hours.day">
</div>
<div class="col-2" >
<input type="time" v-model="hours.start">
</div>
<div class="col-2" >
<input type="time" v-model="hours.finish">
</div>
<div class="col-2" >
<input type="number" v-model="hours.job_number">
</div>
<div class="col-2" >
<select v-model="hours.climbing">
<option selected="selected" value="0">No</option>
<option value="1">Yes</option>
</select>
</div>
<div class="col-2" >
<button #click="onSave" class="btn-success btn-sm">Save</button>
</div>
</div>
<hr>
</div>
</template>
<script>
export default {
name: 'InputHoursComponent',
props: ['employeeId', 'dayCheck', 'weekEnding'],
data() {
return {
hours: {
start: "",
finish: "",
job_number: "",
climbing: 0,
day: this.dayCheck
},
climbing_select: ['No', 'Yes'],
}
},
methods: {
onSave()
{
axios.post('/payroll', {
employee_id: this.employeeId,
week_ending: this.weekEnding,
start: this.hours.start,
finish: this.hours.finish,
job_number: this.hours.job_number,
climbing: this.hours.climbing,
day: this.dayCheck
})
.then(response => {})
.catch(e => {this.errors.push(e)});
let data = this.normalizeProp(this.hours, s, true)
Event.$emit('onAddedEntry', data);
console.log("passed data:", this.hours);
}
}
}
</script>
display-component
<template>
<div>
<div v-for="item in todays_hours">
<div class="row">
<div class="col-2">
<div hidden="" ></div>
</div>
<div class="col-2" >
<div v-text="item.start"></div>
</div>
<div class="col-2" >
<div v-text="item.finish"></div>
</div>
<div class="col-2" >
<div v-text="item.job_number"></div>
</div>
<div class="col-2" >
<div v-text="(item.climbing)?'Yes':'No'"></div>
</div>
<div class="col-2" >
<button #click="onEdit" class="btn-warning btn-sm mb-1">Edit</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DisplayHoursComponent',
props: ['dayCheck', 'hoursWorked'],
data() {
return {
hours_list: this.hoursWorked,
todays_hours: []
}
},
mounted() {
for (var i = 0; i < this.hours_list.length; i++) {
if (this.hours_list[i].day === this.dayCheck) {
this.todays_hours.push(this.hours_list[i])
}
}
Event.$on('onAddedEntry', (check) => {
if(check.day === this.dayCheck){
this.todays_hours.push(check);
}
})
},
methods: {
onEdit()
{
}
}
}
</script>
Can someone please help me?
Try pushing a copy of check instead of check itself.
Event.$on('onAddedEntry', (check) => {
if(check.day === this.dayCheck){
this.todays_hours.push({...check});
}
})
You could also make the copy when you emit the event instead.
I'm using Vue.js v2 and I've defined a single-file component, RegionFacet.vue. It lists some regions that relate to polygons on a map (click a value, the corresponding polygon appears on the map).
Separately, I have a reset button. When that gets clicked, I call a method in RegionFacet to unselect any checkboxes displayed by RegionFacet. The model does get updated, however, the checkboxes remain checked. What am I missing?
<template>
<div class="facet">
<div class="">
<div class="panel-group" id="accordion">
<div class="panel panel-default">
<div class="panel-heading">
<a data-toggle="collapse"v-bind:href="'#facet-' + this.id"><h4 class="panel-title">Regions</h4></a>
</div>
<div v-bind:id="'facet-' + id" class="panel-collapse collapse in">
<ul class="list-group">
<li v-for="feature in content.features" class="list-group-item">
<label>
<input type="checkbox" class="rChecker"
v-on:click="selectRegion"
v-bind:value="feature.properties.name"
v-model="selected"
/>
<span>{{feature.properties.name}}</span>
</label>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['content'],
data: function() {
return {
id: -1,
selected: []
}
},
methods: {
selectRegion: function(event) {
console.log('click: ' + event.target.checked);
if (event.target.checked) {
this.selected.push(event.target.value);
} else {
var index = this.selected.indexOf(event.target.value);
this.selected.splice(index, 1);
}
this.$emit('selection', event.target.value, event.target.checked);
},
reset: function() {
this.selected.length = 0;
}
},
created: function() {
this.id = this._uid
}
}
</script>
<style>
</style>
You are directly setting the array length to be zero, which cannot be detected by Vue, as explained here: https://v2.vuejs.org/v2/guide/list.html#Caveats
Some more info: https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
To overcome this, you may instead set the value of this.selected as follows:
reset: function() {
this.selected = [];
}