pass an object as props - javascript

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'"

Related

(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>

Vue.js not re-rendering when file state changes

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},
};
},

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 = "">

vuejs assign multiple input value to json object value

I'm trying to iterate through a JSON object in Vue. The panelfields variable will be assigned to a JSON object, and will change between different JSON objects with varying numbers of key/value pairs.
I can't figure out how to get the value inserted into each input, as it is created. The code is similar to the following:
data() {
return {
panelfields:
[
{
fname: "fname1",
lname: "lname1",
email: "email1"
},
{
fname: "fname2",
lname: "lname2",
email: "email2"
},
{
fname: "fname3",
lname: "lname3",
email: "email3"
}
]
}
}
<div :key="index" v-for="(value, name, index) in panelfields">
<span>{{ name }}: </span>
<input type="text" v-model="panelfields" :value="value" />
</div>
Note I am trying to avoid the following pattern, where the field props are explicitly bound by name in the template:
<input type="text" v-model="value.fname" :value="value" />
<input type="text" v-model="value.lname" :value="value" />
<input type="text" v-model="value.email" :value="value" />
...which would force the JSON object pattern to fit that model, requiring separate panels to be created for each JSON object that panelfields is set to.
Panel fields is for a popup panel that will display data for different database queries, and the JSON object will be different depending on what is queried; it may be "fname", "lname", "email" or it may be something else entirely. So the UI needs to be flexible in how it handles the data.
Use a nested v-for to iterate each field's props by key (obtained by Object.keys(field)), allowing you to dynamically bind the props with v-model="field[key]":
<div :key="index" v-for="(field, name, index) in panelfields">
<input v-for="key in Object.keys(field)" v-model="field[key]" />
</div>
new Vue({
el: '#app',
data() {
return {
panelfields:
[
{
fname: "fname1",
lname: "lname1",
email: "email1"
},
{
fname: "fname2",
lname: "lname2",
email: "email2"
},
{
fname: "fname3",
lname: "lname3",
email: "email3"
}
]
}
},
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.min.js"></script>
<div id="app">
<div :key="index" v-for="(value, name, index) in panelfields">
<span>{{name}}: </span>
<input type="text" v-for="x in Object.keys(value)" v-model="value[x]" />
</div>
<div>
<pre>panelfields = {{panelfields}}</pre>
</div>
</div>
Change your template to this
<div v-for="(value, index) in panelfields" :key="index">
<span>{{ value.fname }}: </span>
<input type="text" v-model="value.fname" :value="value" />
<span>{{ value.lname }}: </span>
<input type="text" v-model="value.lname" :value="value" />
<span>{{ value.email }}: </span>
<input type="text" v-model="value.email" :value="value" />
<hr>
</div>
You can't set an entire array of objects as a v-model. Just make the separate inputs for each v-model.
<div v-for="(item, index) of panelfields" :key="index">
<span>{{item.fname}}</span><input v-model="panelfieds[index].fname"/>
<span>{{item.lname}}</span><input v-model="panelfieds[index].lname"/>
<span>{{item.email}}</span><input v-model="panelfieds[index].email"/>
</div>

How do I push items into an array in the data object in Vuejs? Vue seems not to be watching the .push() method

I am attempting to add objects into an array I declared in Vue instance data object. I can set the values in the state's purchase object, but when I push data into the orders queue array, the empty array is not populated. The function is being triggered, but the array does not update.
Here is my form:
<form
v-on:submit.prevent="queuePurchase"
class="form-inline row"
id="order-creation-form"
method="POST"
>
#csrf
<autocomplete-field
sizing="col-xs-12 col-sm-3 col-md-3"
name="customer"
label="Customer"
:data="{{ json_encode($customers) }}"
v-on:setcustomer="setCustomer($event)"
></autocomplete-field>
<div class="col-xs-12 col-sm-3 col-md3 form-group d-flex flex-column align-items-start">
<label for="phone">Product</label>
<select
v-model="purchase.product"
class="form-control w-100"
name="product"
aria-describedby="productHelpBlock"
required
>
#foreach ($products as $product)
<option :value="{{ json_encode($product) }}">
{{ $product->name }}
</option>
#endforeach
</select>
<small id="productHelpBlock" class="form-text text-muted">
Select a product
</small>
</div>
<div class="col-xs-12 col-sm-3 col-md-3 form-group d-flex flex-column align-items-start">
<label for="phone">Quantity</label>
<input
v-model="purchase.quantity"
type="number"
min="1"
name="product"
class="form-control w-100"
aria-describedby="productHelpBlock"
required
>
<small id="productHelpBlock" class="form-text text-muted">
Product quantity
</small>
</div>
<div class="form-group">
<button type="submit" class="btn btn-success icon-button d-flex">
<i class="material-icons">add</i>
<span> Q U E U E</span>
</button>
</div>
</form>
And here is my javascript:
require("./bootstrap");
window.Vue = require("vue");
Vue.component("queue-table", require('./components/QueueTable.vue'));
Vue.component("autocomplete-field", require('./components/AutocompleteField.vue'));
const purchaseApp = new Vue({
el: "#purchase-app",
data() {
return {
queue: [],
purchase: {
product: null,
customer: null,
quantity: null
}
}
},
methods: {
setCustomer: function(customerObj) {
this.purchase.customer = customerObj;
},
queuePurchase: function() {
this.queue.push( this.purchase );
}
}
});
Could someone please explain what & why it is happening?
The push() method ought to add purchase objects to the queue array, but as #FK82 pointed out in his comment, push() is adding multiple references to the same purchase object. This means that if you change the object by increasing the quantity, every purchase's quantity property will be updated.
You can give that a try here:
const exampleComponent = Vue.component("example-component", {
name: "exampleComponent",
template: "#example-component",
data() {
return {
queue: [],
purchase: {
product: null,
customer: null,
quantity: null
}
};
},
methods: {
queuePurchase() {
this.queue.push( this.purchase );
}
}
});
const page = new Vue({
name: "page",
el: ".page",
components: {
"example-component": exampleComponent
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<template id="example-component">
<div>
<p>The Queue has {{ this.queue.length }} items.</p>
<input
v-model="purchase.quantity"
type="number"
min="1"
name="product"
placeholder="Quantity"
>
<button #click="queuePurchase">
Add to Queue
</button>
<pre>{{ JSON.stringify(this.queue, null, 2) }}</pre>
</div>
</template>
<div class="page">
<example-component></example-component>
</div>
Instead of push()ing a reference to the same purchase object, try creating a shallow copy with Object.assign({}, this.purchase) or by using the spread operator. Here's an example that uses the spread operator and then push()es the copy onto the queue:
const exampleComponent = Vue.component("example-component", {
name: "exampleComponent",
template: "#example-component",
data() {
return {
queue: [],
purchase: {
product: null,
customer: null,
quantity: null
}
};
},
methods: {
queuePurchase() {
this.queue.push({...this.purchase});
}
}
});
const page = new Vue({
name: "page",
el: ".page",
components: {
"example-component": exampleComponent
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.4/vue.min.js"></script>
<template id="example-component">
<div>
<p>The Queue has {{ this.queue.length }} items.</p>
<input
v-model="purchase.quantity"
type="number"
min="1"
name="product"
placeholder="Quantity"
>
<button #click="queuePurchase">
Add to Queue
</button>
<pre>{{ JSON.stringify(this.queue, null, 2) }}</pre>
</div>
</template>
<div class="page">
<example-component></example-component>
</div>

Categories