sending vuejs data post request as array of ints instead of strings - vue.js

I have 2 input boxes for weights and values, and I can then click a button to automatically create more. This part is working fine.
The problem now is that the server expects a JSON formdata request containing array of ints, rather than strings.
This is how the postdata should look like:
{"stuff": {"weights": [2, 5, 3], "values": [654, 23, 3]}}
This is what it ends up looking like:
{"stuff": {"weights": ["2", "5", "3"], "values": ["654", "23", "3"]}}
I have tried to search how to set data array type to int, how to save v-model as int, etc.
I believe this is all the relevant code:
<template>
..
<div v-for="(weight, index) in weights">
<div>
<label for="w">weight:</label>
<input v-model="weights[index]">
<label for="v">value:</label>
<input v-model="values[index]">
</div>
</div>
..
</template>
<script>
export default {
data () {
return {
weights: [],
values: []
}
},
methods: {
solve: function () {
var formData = {
stuff: {
weights: this.weights,
values: this.values
}
}
axios.post('http://localhost:8000/my/api/url', formData).then(response => {
console.log(response)
}).catch(e => {
console.log(e)
})
},
...
</script>

You can use parseInt to convert them:
export default {
data () {
return {
weights: [],
values: []
}
},
computed: {
formData() {
return {
stuff: {
weights: this.weights.map((x) => parseInt(x, 10),
values: this.values.map((x) => parseInt(x, 10)
}
}
}
},
methods: {
solve: function () {
axios.post('http://localhost:8000/my/api/url', this.formData).then(response => {
console.log(response)
}).catch(e => {
console.log(e)
})
},
...
I used a computed variable to make the API call code cleaner, but it would work the same in there. The extra argument 10 is important with parseInt, it ensures the integer is base 10 which could otherwise cause odd behavior.

You can use the .number modifier:
<input v-model.number="weights[index]">
But this allows non-integer numbers and strings (it can be an empty string if the input is empty, for example). For this reason, you should explicitly convert the weights to integers at the API call:
// You should validate the data first
var formData = {
stuff: {
weights: this.weights.map(x => parseInt(x, 10)), // Convert to integer
values: this.values.map(x => parseInt(x, 10))
}
}

Related

convert vue ast back to template

I'm using vue-template-compiler to transform a text in the vue template to an expression
I have this vueJs file
<template>
<p>hi</p>
</template>
I'm trying to change the text with expression like this
<p>{{fun('hi')}}</p>
my code
const compiled = compiler.compile(
data,
{
modules: [
{
transformNode(data) {
const children = data.children.map((child) => {
if (child.type === 3 && child.text.trim()) {
return ({
type: 2,
expression: "_s(fun('hi'))",
tokens: [
{
'#binding': "fun('hi')"
}
],
text: "{{fun('hi)}}"
})
}
return child;
});
return { ...data, children };
},
},
],
},
{
whitespace: "condense",
}
);
it maps over the texts and replace it with the expression.
and it returns the final ast.
but I need to convert it back to string
like that:
<p>{{fun('hi')}}</p>
I need to convert the final ast back to the template

this.state return empty array when render

I wanted to display the array series and when I tried to console the array this.state.series in the function it does has the result and the value inside but when I render this.state.series just keep giving empty array. I wonder is it because of the componentDidMount()?
constructor(props){
super(props);
this.state={series:[]}
}
GetTransaction=()=> {
var APIURL="http://10.0.2.2:80/api/getTransaction.php";
var headers={
'Accept':'application/json',
'Content-Type':'application.json'
}
fetch(APIURL,
{
method:'POST',
headers: headers,
})
.then((response)=>response.json())
.then((response)=>
{
this.setState({results:response});
this.state.results[0].map((a) => {
this.state.series.push(a.count)
});
console.log(this.state.series)
})
.catch((error)=>
{
alert("Error"+error);
}
)
}
componentDidMount(){ // runs after the component output has been rendered to the DOM
this.GetTransaction();
}
render(){
console.log(this.state.series)
output
Array []
Array []
Array []
Array []
Array []
Array []
Array []
Array [
"1",
"2",
"1",
"1",
]
There are basically 2 errors in GetTransaction state assignment:
you can't read a state just assigned because this.setState is async. If you want to get the very last value of state you should use this.setState's callback:
this.setState({results:response}, () => {
console.log(this.state.result); //<-- here you have the very last value of result
});
state must be always setted with this.setState: this.state.series.push(a.count) is not good.
So you have to rewrite your code in this way:
...
this.setState({results:response}, () => {
let seriesAppo = [];
this.state.results[0].map((a) => {
seriesAppo.push(a.count);
});
this.setState({series: seriesAppo}, () => {
console.log(this.state.series);
})
});
...
That‘s weird
this.state.results[0].map((a) => {
this.state.series.push(a.count)
});
Don‘t manipulate state that way, only with setState.
const series = response[0];
this.setState({series: series});
Even if you wanna add elements to an array you have to recreate the array. You can achieve this as follows:
const series = response[0];
this.setState({series: […this.state.series, …series]});

How can I put a data inside the <template> in a component with Vue

I am trying to do a pagination but I can not put the dynamic total I am doing like this:
<v-pagination v-model="currentPage"
:page-count="total"
:classes="bootstrapPaginationClasses"
:labels="paginationAnchorTexts"
></v-pagination>
How you can see the total os in the :page-count, it is a dynamic total because I am getting data from database, my vue code is this one:
<script>
import vPagination from 'vue-plain-pagination';
export default {
created() {
this.getPosts();
},
methods: {
getPosts() {
fetch('/api/bank')
.then(response => response.json() )
.then(json => {
this.posts = json.data.data;
this.total = json.data.last_page;
this.current_page = json.data.current_page;
});
}
},
components: { vPagination },
data: function() {
return {
postsSelected: "",
posts: [],
currentPage: 1,
total: this.total,
bootstrapPaginationClasses: {
ul: 'pagination',
li: 'page-item',
liActive: 'active',
liDisable: 'disabled',
button: 'page-link'
},
paginationAnchorTexts: {
first: 'Primera',
prev: '«',
next: '»',
last: 'Última'
}
}
}
}
</script>
How you can see I am using fetch to get the data from database and then I am split it in different information like total and the I am using this information inside the data: function() {}.
How you can tell total it's like this: total: this.total because I want to get the total number but when I do that I am getting this error:
[Vue warn]: Invalid prop: type check failed for prop "pageCount". Expected Number with value NaN, got Undefined
and I think that it is because:
total: this.total in the data function() {} is bad or:
how can I put the dynamic variable total inside the
How could I fix it?
Thanks!
If you want to know the data retrieved from the API, you can console log the data returned like this:
getPosts() {
fetch('/api/bank')
.then(response => response.json() )
.then(json => {
console.log(json.data)
this.posts = json.data.data;
this.total = json.data.last_page;
this.current_page = json.data.current_page;
});
}
Also, you should not have data attribute and props attribute with the same name! So change the total data attribute to another name and initialize it with a value of 0 instead.
In fact, you don't need to care about passing the Prop total at all as your method getPosts is not dependent on the Prop! So you may just have total: 0 in data and that should fix your issues

Vue.js - Element UI - HTML message in MessageBox

I'm using vue-js 2.3 and element-ui. This question is more specific to the MessageBox component for which you can find the documentation here
Problem
I'd like to be able to enter html message in the MessageBox
More specifically I would like to display the data contained in dataForMessage by using a v-for loop.
Apparently, we can insert vnode in the message but I have no idea where to find some information about the syntax.
https://jsfiddle.net/7ugahcfz/
var Main = {
data:function () {
return {
dataForMessage: [
{
name:'Paul',
gender:'Male',
},
{
name:'Anna',
gender:'Female',
},
],
}
},
methods: {
open() {
const h = this.$createElement;
this.$msgbox({
title: 'Message',
message: h('p', null, [
h('span', null, 'Message can be '),
h('i', { style: 'color: teal' }, 'VNode '),
h('span', null, 'but I would like to see the data from '),
h('i', { style: 'color: teal' }, 'dataForMessage'),
])
}).then(action => {
});
},
}
}
var Ctor = Vue.extend(Main)
new Ctor().$mount('#app')
I think this is what you want.
methods: {
open() {
const h = this.$createElement;
let people = this.dataForMessage.map(p => h('li', `${p.name} ${p.gender}`))
const message = h('div', null, [
h('h1', "Model wished"),
h('div', "The data contained in dataForMessage are:"),
h('ul', people)
])
this.$msgbox({
title: 'Message',
message
}).then(action => {
});
},
}
Example.
You can also use html directly and convert to vnodes by using domProps:
const html = '<div><h1>Model wished</h1><div>The data contained in dataForMessage are:</div><ul><li>Paul Male</li><li>Anna Female</li></ul></div>'
const message = h("div", {domProps:{innerHTML: html}})
(The above is simplified without the loop. Just to get the idea)
Fiddle

vue.js $watch array of objects

mounted: function() {
this.$watch('things', function(){console.log('a thing changed')}, true);
}
things is an array of objects [{foo:1}, {foo:2}]
$watch detects when an object is added or removed, but not when values on an object are changed. How can I do that?
You should pass an object instead of boolean as options, so:
mounted: function () {
this.$watch('things', function () {
console.log('a thing changed')
}, {deep:true})
}
Or you could set the watcher into the vue instance like this:
new Vue({
...
watch: {
things: {
handler: function (val, oldVal) {
console.log('a thing changed')
},
deep: true
}
},
...
})
[demo]
There is a more simple way to watch an Array's items without having deep-watch: using computed values
{
el: "#app",
data () {
return {
list: [{a: 0}],
calls: 0,
changes: 0,
}
},
computed: {
copy () { return this.list.slice() },
},
watch: {
copy (a, b) {
this.calls ++
if (a.length !== b.length) return this.onChange()
for (let i=0; i<a.length; i++) {
if (a[i] !== b[i]) return this.onChange()
}
}
},
methods: {
onChange () {
console.log('change')
this.changes ++
},
addItem () { this.list.push({a: 0}) },
incrItem (i) { this.list[i].a ++ },
removeItem(i) { this.list.splice(i, 1) }
}
}
https://jsfiddle.net/aurelienlt89/x2kca57e/15/
The idea is to build a computed value copy that has exactly what we want to check. Computed values are magic and only put watchers on the properties that were actually read (here, the items of list read in list.slice()). The checks in the copy watcher are actually almost useless (except weird corner cases maybe) because computed values are already extremely precise.
If someone needs to get an item that was changed inside the array, please, check it:
JSFiddle Example
The post example code:
new Vue({
...
watch: {
things: {
handler: function (val, oldVal) {
var vm = this;
val.filter( function( p, idx ) {
return Object.keys(p).some( function( prop ) {
var diff = p[prop] !== vm.clonethings[idx][prop];
if(diff) {
p.changed = true;
}
})
});
},
deep: true
}
},
...
})
You can watch each element in an array or dictionary for change independently with $watch('arr.0', () => {}) or $watch('dict.keyName', () => {})
from https://v2.vuejs.org/v2/api/#vm-watch:
Note: when mutating (rather than replacing) an Object or an Array, the
old value will be the same as new value because they reference the
same Object/Array. Vue doesn’t keep a copy of the pre-mutate value.
However, you can iterate the dict/array and $watch each item independently. ie. $watch('foo.bar') - this watches changes in the property 'bar' of the object 'foo'.
In this example, we watch all items in arr_of_numbers, also 'foo' properties of all items in arr_of_objects:
mounted() {
this.arr_of_numbers.forEach( (index, val) => {
this.$watch(['arr_of_numbers', index].join('.'), (newVal, oldVal) => {
console.info("arr_of_numbers", newVal, oldVal);
});
});
for (let index in this.arr_of_objects) {
this.$watch(['arr_of_objects', index, 'foo'].join('.'), (newVal, oldVal) => {
console.info("arr_of_objects", this.arr_of_objects[index], newVal, oldVal);
});
}
},
data() {
return {
arr_of_numbers: [0, 1, 2, 3],
arr_of_objects: [{foo: 'foo'}, {foo:'bar'}]
}
}
If your intention is to render and array and watch for changes on rendered items, you can do this:
Create new Component:
const template = `<div hidden></div>`
export default {
template,
props: ['onChangeOf'],
emits: ['do'],
watch: {
onChangeOf: {
handler(changedItem) {
console.log('works')
this.$emit('do', changedItem)
},
deep: true
}
},
}
Register that component:
Vue.component('watcher', watcher)
Use it inside of your foreach rendering:
<tr v-for="food in $store.foods" :key="food.id">
<watcher :onChangeOf="food" #do="(change) => food.name = 'It works!!!'"></watcher>
</tr>