Can't make my Mutation work in vue apollo client [duplicate] - vue.js

I'm running into the problem, that Vue converts the value of an input field of type number into a string and I just can't figure out why. The guide I am following along does not run into this issue and get's the values as numbers, as expected.
The vue docs state, that Vue would convert the value to a number, if the type of the input is number.
The code is originated from a component, but I adjusted it to run in JSFiddle: https://jsfiddle.net/d5wLsnvp/3/
<template>
<div class="col-sm-6 col-md-4">
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">
{{ stock.name }}
<small>(Price: {{ stock.price }})</small>
</h3>
</div>
<div class="panel-body">
<div class="pull-left">
<input type="number" class="form-control" placeholder="Quantity" v-model="quantity"/>
</div>
<div class="pull-right">
<button class="btn btn-success" #click="buyStock">Buy</button>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['stock'],
data() {
return {
quantity: 0 // Init with 0 stays a number
};
},
methods: {
buyStock() {
const order = {
stockId: this.stock.id,
stockPrice: this.stock.price,
quantity: this.quantity
};
console.log(order);
this.quantity = 0; // Reset to 0 is a number
}
}
}
</script>
The quantity value is the issue.
It is initialized with 0 and when I just press the "Buy" button, the console shows:
Object { stockId: 1, stockPrice: 110, quantity: 0 }
But as soon as I change the value by either using the spinners or just type in a new value, the console will show:
Object { stockId: 1, stockPrice: 110, quantity: "1" }
Tested with Firefox 59.0.2 and Chrome 65.0.3325.181. Both state that they are up to date. I actually also tried it in Microsoft Edge, with the same result.
So what am I missing here? Why is Vue not converting the value to a number?

You need to use .number modifier for v-model, like this:
<input v-model.number="quantity" type="number">
Note: empty string ('') is not converted to a number, so you may need to handle it separately.

Change the order object to :
const order = {
stockId: this.stock.id,
stockPrice: this.stock.price,
quantity: +this.quantity
};
This will automatically parse the string to a number.
In general the data from HTML inputs are strings. The input type only checks if a valid string has been provided in the field.

Related

Vue does not correctly remove item from vfor

I have this custom component in vue callled "dm-vehicle-spec"
<dm-vehicle-spec #_handleRemoveSpec="_handleRemoveSpec" v-for="spec, index in vehicleSpecs" :key="index" :index="index" :spec="spec"></dm-vehicle-spec>
which looks like the following
<script>
export default {
props: ["spec"],
data() {
return {
specName: null,
specValue: null,
}
},
mounted() {
if (this.spec.detail_name && this.spec.detail_value) {
this.specName = this.spec.detail_name;
this.specValue = this.spec.detail_value;
}
},
computed: {
getSpecNameInputName() {
return `spec_${this.spec.id}_name`;
},
getSpecValueInputName() {
return `spec_${this.spec.id}_value`;
},
},
methods: {
_handleRemoveSpec() {
this.$emit("_handleRemoveSpec", this.spec.id);
}
},
}
</script>
<template>
<div class="specs-row flex gap-2 w-full items-center">
<div class="col-1 w-5/12">
<input placeholder="Naam" type="text" :id="getSpecNameInputName" class="w-full h-12 spec_name rounded-lg border-2 border-primary pl-2" v-model="specName">
</div>
<div class="col-2 w-5/12">
<input placeholder="Waarde" type="text" :id="getSpecValueInputName" class="w-full h-12 spec_name rounded-lg border-2 border-primary pl-2" v-model="specValue">
</div>
<div #click="_handleRemoveSpec" class="col-3 w-2/12 flex items-center justify-center">
<i class="fas fa-trash text-lg"></i>
</div>
</div>
</template>
so when i have 3 specs, 1 from the database and 2 customs i have the following array vehicleSpecs (Which i loop over)
[
{"id":23,"vehicle_id":"1","detail_name":"Type","detail_value":"Snel","created_at":"2022-11-07T19:06:26.000000Z","updated_at":"2022-11-07T19:06:26.000000Z","deleted_at":null},
{"id":24},
{"id":25}
]
so lets say i want to remove the second item from the list so the one with test1 as values, then the array looks like
[{"id":23,"vehicle_id":"1","detail_name":"Type","detail_value":"Snel","created_at":"2022-11-07T19:06:26.000000Z","updated_at":"2022-11-07T19:06:26.000000Z","deleted_at":null},{"id":25}]
So the second array item is removed and thats correct because object with id 24 no longer exsist but my html shows
that the value for object with id 24 still exists but the value for object with id 25 is removed, how is that possible?
If u need any more code or explaination, let me know
Any help or suggestions are welcome!
Index is not a good choice to use as v-for key.
Indexes are changing when you delete something from array.
Try using another property as a key.
Run the loop in reverse order. This way, deleted items do not effect the remaining item indexes
Hit: indexReverse = list.length - index

Assign a value in Vue.js onchange of input

I there guy's im struggling with a form that i need to complete using Vue.JS. Basically i need that the value of the field 'price_vat' its updated with some calculations evry time that input called 'price_user' is updated. Usign jquery evrything is going as well. Butt data is not passed to POST method using Vue.
<div class="col-md-6" v-show="form.active">
<div class="form-group">
<label >{{__('Price')}}</label>
<input type="number" v-model="form.price_user" class="form-control">
</div>
</div>
<div class="col-md-6" v-show="form.active">
<div class="form-group">
<label >{{__('Price with VAT')}}</label>
<input type="number" v-model="form.price_vat" class="form-control">
</div>
</div>
if i understand you correctly,, you want form.price_vat to change, every time you change form.price_user by typing inside the input.
you can do this using watch. just add the below your methods in vue:
watch:{
'form.price_user':function():{
this.form.price_vat += 1
},
}
so in this code, you update the value of form.price_vat by 1 every time the form.price_user changes. you can do anything inside the function of watch.
the complete vue part will be :
data(){
return:{
form:{
price_vat :'',
price_user : '',
}
}
},
methods:{},
watch:{
'form.price_user':function():{
this.form.price_vat += 1
},
}

how to show a suggestion list on inputting a tag in bootstrap vue?

I am working on a Vue Assignment. For styling I am using BootstapVue. What I need to achieve is, whenever an user is entering a text in the input field, a filtered array that is containing the value should be displayed as dropdown. The User can either press Enter key stroke or select the value from dropdown. If the input text is not in the array, then the user should not be able to enter the value i.e. the tag might not be created. For example, whenever we are creating a question here in Stack Overflow, upon typing tags and entering or selecting from the suggestion cards. I I need similar functionality except that if the input text is not in the suggestion array, the user will not be able to create the tag
Here's what I have tried so far :
App.vue
<template>
<div>
<b-form-tags
v-model="value"
#input="resetInputValue()"
tag-variant="success"
:state="state"
>
<template v-slot="{ tags, inputId, placeholder, addTag, removeTag }">
<b-input-group>
<b-form-input
v-model="newTag"
list="my-list-id"
:id="inputId"
:placeholder="placeholder"
:formatter="formatter"
#keypress.enter="addTag(newTag)"
></b-form-input>
</b-input-group>
<b-form-invalid-feedback :state="state">
Duplicate tag value cannot be added again!
</b-form-invalid-feedback>
<ul v-if="tags.length > 0" class="mb-0">
<li
v-for="tag in tags"
:key="tag"
:title="`Tag: ${tag}`"
class="mt-2"
>
<span class="d-flex align-items-center">
<span class="mr-2">{{ tag }}</span>
<b-button
size="sm"
variant="outline-danger"
#click="removeTag(tag)"
>
remove tag
</b-button>
</span>
</li>
</ul>
<b-form-text v-else>
There are no tags specified. Add a new tag above.
</b-form-text>
</template>
</b-form-tags>
<datalist id="my-list-id">
<option>Manual Option</option>
<option v-for="(size, ind) in sizes" :key="ind">{{ size }}</option>
</datalist>
</div>
</template>
<script>
export default {
data() {
return {
newTag: "",
value: [],
sizes: ["Small", "Medium", "Large", "Extra Large"],
};
},
computed: {
state() {
// Return false (invalid) if new tag is a duplicate
return this.value.indexOf(this.newTag.trim()) > -1 ? false : null;
},
},
methods: {
resetInputValue() {
this.newTag = "";
},
formatter(value) {
if (this.sizes.includes(value)) {
return value.toUpperCase();
}
return ;
},
},
};
</script>
How can I achieve the same? Thanks
You can achieve it by using b-form-datalist with the use of <b-form-input> by adding a change event to reset the input value if there is no data in the dataList as per the search value.
Demo :
new Vue({
el: '#app',
data: {
sizes: ['Small', 'Medium', 'Large', 'Extra Large'],
tag: null
},
methods: {
checkTag(e) {
const tagsAvailable = this.sizes.filter((item) => item === e);
if (!tagsAvailable.length) {
this.tag = ''
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.21.2/bootstrap-vue.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-vue/2.21.2/bootstrap-vue.min.css"/>
<div id="app">
<b-form-input list="my-list-id" v-model="tag" v-on:change="checkTag($event)"></b-form-input>
<datalist id="my-list-id">
<option>Manual Option</option>
<option v-for="size in sizes">{{ size }}</option>
</datalist>
</div>

How can I change input value in Vue when value is set and update it when it is object of arrays?

I generate so many input boxes on loop of article array.
article is array of objects. Input boxes values are set with "value" key in each object.
<div
v-for="(option, i) in this.article"
:key="i"
class="form-row"
>
<div class="col">
<input
v-model="option.value"
:label="'label '+i"
:name="'inputValue'+i"
type="text"
required
/>
</div>
</div>
<button #click="submit"></button>
<script>
export default {
name: 'Article',
data(){
return(
article:[
{id: 'art1', value: 'artValue1'},
{id: 'art2', value: 'artValue2'},
{id: 'art3', value: 'artValue3'}
// There are about 50 objects
]
)
},
methods:{
submit(){
// How get values of inputs?
}
}
}
</script>
How can I make the value of input change and update the object in vue?
I created a sample Single File Component based on your code. Look at my modifications. The 'magic' of Vue reactivity is that the 'article' data is updated in real time as you change the input values.
<template>
<div class="input-bound-object-array">
<div class="row">
<div class="col-md-6">
<form #submit.prevent="submitForm">
<div class="form-group" v-for="(option, i) in article" :key="i">
<label>{{ option.id }}</label>
<input type="text" class="form-control" v-model="option.value"
required />
</div>
<button type="submit" class="btn btn-secondary">Submit</button>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
article: [
{ id: 'art1', value: 'artValue1' },
{ id: 'art2', value: 'artValue2' },
{ id: 'art3', value: 'artValue3' }
// There are about 50 objects
]
}
},
methods: {
submitForm() {
// 'this.article' is updated as the input values are changed, so at this point
// you have the data changes, so you can process it as needed, i.e. POST to REST endpoint
console.log(this.article);
}
}
}
</script>
You forgot to return the object from data function and you can pass option to submit handler and print the input values.
// How get values of inputs? -> Do you mean all of the inputs or input where ever the button clicked or anything else. Below code is to handle individual input and submit handlers
<template>
<div
v-for="(option, i) in article"
:key="i"
class="form-row"
>
<div class="col">
<input
v-model="option.value"
:label="'label ' + i"
:name="'inputValue' + i"
type="text"
required
/>
</div>
<!-- <button #click="submit(option)">Submit</button> to handle individual inputs -->
</div>
<!-- <button #click="submitAll()">Submit All</button> to handle all inputs -->
</template>
<script>
export default {
name: 'Article',
data() {
return { // you missed this
article:[
{id: 'art1', value: 'artValue1'},
{id: 'art2', value: 'artValue2'},
{id: 'art3', value: 'artValue3'}
]
}
},
methods: {
submit(option) { // current option
console.log(option.id, option.value)
},
submitAll() {
console.log(this.article) // which will have update input values as
you're using `v-model`, check `values`
}
}
}
</script>
Demo Link here

Vuejs - Proper way to clone an element, and append to DOM

I have a HTML input field to enter some information:
<div id="fruitForm">
<div class="inputArea">
<label for="fruit0">Enter Fruit Name</label>
<input id="fruit0"></input>
</div>
</div>
<button #click="newInputField">Add More Fruit Input Fields</button>
<button #click="submit">Submit Fruit</button>
And then I handle that click event:
export default {
data() {
return {
}
},
methods: {
newInputField() {
//Create another input area for users to input fruits
},
submit() {
//Do something here
}
}
}
When a user selects the Add More Fruit Input Fields button, it should create a new input area so that the HTML looks like this:
<div id="fruitForm">
<div class="inputArea">
<label for="fruit0">Enter Fruit Name</label>
<input id="fruit0"></input>
</div>
<div class="inputArea">
<label for="fruit1">Enter Fruit Name</label>
<input id="fruit1"></input>
</div>
</div>
<button #click="newInputField">Add More Fruit Input Fields</button>
<button #click="submit">Submit Fruit</button>
Now, I've been using traditional DOM manipulation methods via vanilla Javascript to accomplish this... stuff like this:
const inputArea = document.getElementsByClassName('inputArea');
And then I change the id's of the input field, and then I use appendChild to add the new input field to the DOM.
So my question is: how should I be cloning this element with vuejs? I feel I'm not approaching this in the vuejs way. Should I be approaching this like a list and using v-for? Or something else?
Avoid direct DOM manipulations with Vue. You can use data property as a model for your template. The answer would be yes, you can and probably should use v-for for what you call cloning:
var demo = new Vue({
el: '#demo',
data: {
counter: 0,
inputs: [{
id: 'fruit0',
label: 'Enter Fruit Name',
value: '',
}],
},
methods: {
addInput() {
this.inputs.push({
id: `fruit${++this.counter}`,
label: 'Enter Fruit Name',
value: '',
});
}
}
});
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="demo">
<div class="inputArea" v-for="input in inputs" :key="input.id">
<label :for="input.id">{{input.label}}</label>
<input :id="input.id" v-model="input.value"></input>
</div>
<button #click="addInput">Add input</button>
</div>