Vue.js not re-rendering when file state changes - javascript

I have the following code in Vue.js:
<div class="file has-name is-fullwidth is-light">
<label class="file-label">
<input
class="file-input"
type="file"
ref="img_input"
#change="onFileChange"
/>
<span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label">
Choose a file…
</span>
</span>
<span class="file-name">
{{ service.image ? service.image.name : "No File Chosen" }}
</span>
</label>
</div>
<script>
export default {
name: "ServiceForm",
data() {
return {
service: {},
};
},
methods: {
onFileChange() {
this.service.image = this.$refs.img_input.files[0];
},
},
};
</script>
However in the vue devtools I can see the image being uploaded to the state, however the tertiary isn't working and still returns no file chosen.

You have a reactivity caveat, try to use this.$set :
this.$set(this.service,'image' , this.$refs.img_input.files[0]);
or you should initialize image property like :
data() {
return {
service: {image:null},
};
},

Related

Checkbox validation and rerouting in VUE.js

I am trying to validate the check box and navigate to next link for ex google.com. I found the solution for checkbox validation from the below link
Vue JS - Checkbox validation error on submit from #wolfrevo
new Vue({
el: "#app",
data: {
termsState: false,
validated: false,
nextPageUrl:'www.google.com'
},
computed: {
termsError() {
return this.validated && !this.termsState
}
},
methods: {
handleTermsState() {
this.validated = false
},
handleSubmit() {
this.validated = true
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id='app'>
<label for="terms">
Terms and Privacy Policy
<input type="checkbox" id="terms" name="terms" v-model="termsState" #change="handleTermsState">
{{ termsState }}
</label>
<p style="color: red" class="for-error terms-error" v-if="termsError">You have to agree the terms and privacy condition.</p>
<router-link :to="nextPageUrl">
<div><button type="submit" #click="handleSubmit">Submit</button></div>
</router-link>
</div>
I tried adding router-link, it is redirecting even without validating the checkbox. what is that I am missing?

(Vuejs) Property or method is not defined on the instance but referenced during render

I have a simple button suppose to add an item in a list:
<label for="numit">item number:</label>
<input type="text" id="numit" :value="idxItemBuy">
<button id="buyitem" #click="buy($event.target.value)">Buy</button>
buy() {
console.log("buy fonction")
this.currentPlayer.buy(this.idxItemBuy)
}
but it's acctualy not calling the method buy
( and i don't know when i'm suppose to use $event.target.value)
You can read more about v-model => bind input with a data (https://v2.vuejs.org/v2/guide/forms.html or https://v3.vuejs.org/guide/forms.html#text)
I write you a code that works
<template>
<div>
<label>item number:</label>
<input type="text" v-model="idxItemBuy" />
<button #click="buy">Buy</button>
<ul>
<li v-for="item in items" :key="item">
{{ item }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
idxItemBuy: "",
items: [],
};
},
methods: {
buy() {
console.log("buy fonction", this.idxItemBuy);
this.items.push(this.idxItemBuy);
},
},
};
</script>

pass an object as props

I'm working on a little project using VueJS, and I would like to know how can I print my props (label) that I'm receiving
this is my code :
<div class="form-group">
<auto-complete :path="'/api/accounts/autoComplete/'" :label="'item.account_name'"></auto-complete>
</div>
this is my component HTML :
<template>
<div class="autocomplete">
<input autocomplete="off" type="text" class="form-control form-control-border" placeholder="query" v-model="query" #input="autoComplete" #focus="suggestion = true" />
<div v-if="data && suggestion">
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
<li #click="selected(item.account_name)" v-for="item in data" :key="item.id">
<a class="dropdown-item" href="#">{{ label }}</a>
</li>
</ul>
</div>
</div>
</template>
<script>
import {getAPI} from "../axios-api"
export default {
props: {
label: Object,
path: String
},
data() {
return {
suggestion: false,
query: '',
data: [],
}
},
methods: {
autoComplete(){
if(/\S/.test(this.query) && this.query.length >= 3){
getAPI.get(this.path + '?query=' + this.query).then((response) => {
this.data = response.data
})
}
},
selected(account){
this.query = account
this.suggestion = false
}
}
}
</script>
as you can see I'm sending an object in :label that i want to print here ( since i have this object in my component data )
Assuming that you want a specific key of item as the label, you can just pass that key string then use it in your dropdown item. Make sure that the label prop accepts only String.
<div class="form-group">
<auto-complete path="/api/accounts/autoComplete/" label="account_name" placeholder="My PlaceHolder"></auto-complete>
</div>
<template>
<input autocomplete="off" type="text" class="form-control form-control-border" :placeholder="placeholder" v-model="query" #input="autoComplete" #focus="suggestion = true" />
<li #click="selected(item.account_name)" v-for="item in data" :key="item.id">
<a class="dropdown-item" href="#">{{ item[label] }}</a>
</li>
</template>
<script>
export default {
props: {
label: String,
placeholder: String
}
}
</script>
You are sending string, not object.
You should do:
:label="item.account_name"
Instead of
:label="'item.account_name'"

Vuejs generating click event in next button after pressing enter input

I'm encountering a very strange case.
I've build a very simple example in order to present my problem.
I've 3 files : App.vue, todo2.vue, todoI.vue.
App.vue has 1 component (todo2.vue). todo2.vue has 1 component (todoI.vue).
You'll find under the code of todo2 and todoI.
The problem I'm facing is that when i press enter in the input text id #checkboxAdd, it triggers an event on the next button.
In the code below when pressing enter in the input text #checkboxAdd, it triggers the click event on the first iteration of my todoI button, which in my example calls the function del (#click="del()"), which console.log(this), logging the first iteration of the component todoI.
What is even stranger is that when I add a button just after the input text, add a #click to console.log the event, it is indeed called (instead of calling the button of the first iteration of todoI).
Does anyone understand why this happens ? Is this something I'm doing wrong ?
todo2.vue:
<template>
<div class="d-flex flex-column">
<div>
<form #submit.prevent="">
<div class="mb-3 input-group">
<div class="input-group-text">
<input type="checkbox" class="form-check-input" id="checkboxAdd" aria-describedby="checkboxAdd">
</div>
<input type="text" class="form-control" id="inputAdd" aria-describedby="inputAdd" v-model="tempI">
</div>
<ul class="list-group">
<todoI v-for="(it, k) in todos" v-model="it.v" :key="k" #delItem="del(it)"></todoI>
</ul>
<br>
</form>
</div>
</div>
</template>
<script>
import todoI from './todoI.vue'
export default {
name:"todo2",
components: {todoI},
data: function(){
return {
todos: [
{v:{
val: "Bread",
checked: false
}},
{v:{
val: "Fruits",
checked: false
}},
{v:{
val: "Ironing",
checked: false
}}
],
tempI: ''
}
},
methods:{
del (it){
this.todos = this.todos.filter(i => i!==it)
}
}
}
</script>
todoI.vue:
<template>
<li class="list-group-item d-flex align-items-center" #mouseover="btnShow=true" #mouseleave="btnShow=false">
<input type="checkbox" class="me-4" v-model="value.checked">
<div class="w-100" :class="value.checked ? checkedClass : '' ">{{ value.val }}</div>
<div class="flex-shrink-1">
<button class="btn btn-sm btn-close" v-show="btnShow" #click="del()"></button>
</div>
</li>
</template>
<script>
export default {
name:"todoI",
props:{
value: Object
},
data: function(){
return {
checkedClass:['text-decoration-line-through', 'text-muted'],
btnShow: false,
}
},
methods:{
del(){
console.log(this)
}
}
}
</script>
you can simple use #keypress or #keydown
<input type="text" class="form-control" id="inputAdd" v-model="tempI" #keypress.enter.prevent />
or
<input type="text" class="form-control" id="inputAdd" v-model="tempI" #keydown.enter.prevent = "">

Prevent on click on parent A tag when clicking button inside

I have problems with product cards on index page.
Inside product card I have Vue component to render form (quantity and add to cart button).
When I click on add to cart button I get expected result. Response is sent to root vue component and then I see toast notification that product is added to cart.
But when I click on plus, minus buttons or input field then href link is triggered from parent A tag.
How to disable it?
I found this Prevent on click on parent when clicking button inside div
But it works only if I put A tag inside vue component. I don't want to put too much html into vue.
#foreach ($products as $product)
<a href="{{ $category->fullpath.'/'.$product->sef }}" class="catalog__card">
<div class="catalog__card-img"><img src="/storage/img/products/{{ $product->mediumpic }}" alt="{{ $product->title }}"></div>
<div class="card__properties">
<div class="card__price"><span>{{ $product->price }}<span class="currencySymbol">₽</span></span></div>
<div class="card__stock">
#if ( $product->stock > 0 )
<i class="far fa-check-circle text-success"></i><span class="text-success"><strong> on stock</strong></span>
#else
<span><strong> on request</strong></span>
#endif
</div>
<addtocart #added_to_cart="updateCart"></addtocart>
</div>
<div class="catalog__card-title"><span>{{ $product->title }}</span></div>
</a>
#endforeach
in Vue component I have following
<template>
<div class="cart">
<form method="POST" id="add_to_cart" action="/add_to_cart" #submit.prevent="onSubmit">
<input type="hidden" name="_token" :value="csrf">
<div class="quantity">
<button type="button" class="minus_quantity" v-on:click="minus_quantity" v-long-press="300" #long-press-start="longMinusStart" #long-press-stop="longMinusStop">-</button>
<input type="number" class="input-text qty text" step="1" min="1" max="9999" name="quantity" value="1" title="Qty" v-model.number="quantity">
<button type="button" class="plus_quantity" v-on:click="plus_quantity" v-long-press="300" #long-press-start="longPlusStart" #long-press-stop="longPlusStop">+</button>
</div>
<button type="submit" name="add-to-cart" class="button-cart"><i class="fas fa-cart-plus"></i><span> order</span></button>
</form>
</div>
</template>
<script>
import LongPress from 'vue-directive-long-press';
export default {
name: "addtocart",
data: function () {
return {
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
quantity: 1,
plusInterval: null,
minusInterval: null,
success: false,
errors: []
}
},
directives: {
'long-press': LongPress,
},
props: [],
computed: {
getName(){
return this.$store.getters.Name
}
},
methods: {
// add to cart quantity plus and minus buttons
// short mouse click event
parent() { alert('you clicked the parent') },
minus_quantity() {
if (this.quantity > 0) {this.quantity -= 1}
},
plus_quantity() {this.quantity += 1},
// long press buttons
longPlusStart() {
this.plusInterval = setInterval(() => {
this.quantity += 1
}, 100)
},
longPlusStop() {
clearInterval(this.plusInterval)
},
longMinusStart() {
this.minusInterval = setInterval(() => {
if (this.quantity > 0) {this.quantity -= 1}
}, 100)
},
longMinusStop() {
clearInterval(this.minusInterval)
},
onSubmit() {
axios.post('/add_to_cart', this.$data)
.then(this.onSuccess)
.catch(error => this.errors = error.response.data);
},
onSuccess(response) {
this.success = true;
this.$emit('added_to_cart', response);
},
},
mounted() {
},
}
</script>
You can use "v-on:click.stop" directive for your plus, minus buttons instead of "v-on:click"
Read this for more information https://v2.vuejs.org/v2/guide/events.html
using v-on:click.prevent instead v-on:click I fixed this issue
With a <a href="..."> tag parent component an <button #click="someAction"> tag inside, you have to replace #click="someAction" by #click.prevent="someAction".
It works like a charm for m !

Categories