I do not commit and the state is updated in Vuex - vue.js

I have a bit frustrating problem, result that I have a select / option where I choose an item and then a modal is opened and just by clicking on Add / update to table, it just commits to the state "itemstabla", but result that when I give it to edit item in the table and the model is opened and I change, for example, quantity to another digit, the state "itemstabla" is updated without executing the commit, it should only be done when clicking on the modal button.
My store:
let store = {
state: {
token: localStorage.getItem('access_token') || null,
items: [],
itemstabla: [],
monedas: [],
impuestos: [],
venta: [],
estadotienda: '',
},
getters: {
loggedIn(state) {
return state.token !== null
},
},
mutations: {
EliminarItemTabla(state, item) {
var index = state.itemstabla.findIndex(c => c.id == item);
state.itemstabla.splice(index, 1);
},
retrieveToken(state, token) {
state.token = token
},
setmonedas(state, monedas) {
state.monedas = monedas
},
setventa(state, venta) {
state.venta = venta
},
setitemstabla(state, items) {
let found = state.itemstabla.find(item => item.id == items.id);
if(found) {
if(items.accion=='agregar') {
found.cantidad = parseInt(found.cantidad) + parseInt(items.cantidad)
}
else {
found.cantidad = parseInt(items.cantidad)
}
}
else {
state.itemstabla.push(items)
}
},
actualizaritemstabla(state) {
state.itemstabla.forEach(function (item) {
if ((state.venta.moneda_id == 'S/' && item.moneda == 'S/') || (state.venta.moneda_id == '$' && item.moneda == '$')) {
item.precio = parseFloat(item.precio).toFixed(2)
}
else if(state.venta.moneda_id == '$' && item.moneda == 'S/') {
item.moneda = '$'
item.precio = parseFloat(parseFloat(item.precio) / parseFloat(state.venta.tipocambio)).toFixed(2)
}
else if(state.venta.moneda_id == 'S/' && item.moneda == '$') {
item.moneda = 'S/'
item.precio = parseFloat(parseFloat(item.precio) * parseFloat(state.venta.tipocambio)).toFixed(2)
}
});
},
setimpuestos(state, impuestos) {
state.impuestos = impuestos
},
setmonedaid(state, moneda_id) {
state.venta.moneda_id = moneda_id
},
settipocambio(state, tipocambio) {
state.venta.tipocambio = tipocambio
},
destroyToken(state) {
state.token = null
}
},
actions: {
retrieveToken(context, credentials) {
return new Promise((resolve, reject) => {
axios.post('/api/login', {
username: credentials.username,
password: credentials.password,
})
.then(response => {
const token = response.data.access_token
localStorage.setItem('access_token', token)
context.commit('retrieveToken', token)
resolve(response)
})
.catch(error => {
reject(error)
})
})
},
destroyToken(context) {
if (context.getters.loggedIn){
return new Promise((resolve, reject) => {
axios.post('/api/logout', '', {
headers: { Authorization: "Bearer " + context.state.token }
})
.then(response => {
localStorage.removeItem('access_token')
context.commit('destroyToken')
resolve(response)
})
.catch(error => {
localStorage.removeItem('access_token')
context.commit('destroyToken')
reject(error)
})
})
}
}
}
}
export default store
My file Vue:
<template>
<div>
<!-- MODAL PARA EDITAR ITEM -->
<div class="modal fade" id="editarItem" tabindex="-1" role="dialog" aria-labelledby="editarItem" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Editar ítem</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="cantidadEdit" class="col-sm-4 col-form-label col-form-label-sm font-weight-bold">Cantidad</label>
<div class="col-sm-8">
<div class="input-group">
<input v-model="item.cantidad" type="number" class="form-control text-center font-weight-bold h2" min="1" tabindex="1" onfocus="this.select();">
</div>
</div>
</div>
<div class="form-group row">
<label for="itemEdit" class="col-sm-4 col-form-label col-form-label-sm">Stock</label>
<div class="col-sm-8">
<input type="text" class="form-control text-center font-weight-bold h2" v-model="item.stock" disabled>
</div>
</div>
<div class="form-group row">
<label for="precioEdit" class="col-sm-4 col-form-label col-form-label-sm">Precio unitario<small class="text-muted" id="porcentajefinalprecio">+ {{item.primer_margen}}%</small></label>
<div class="col-sm-8">
<input v-model="item.precio" type="text" class="form-control font-weight-bold" tabindex="2" onfocus="this.select();">
</div>
</div>
<div class="form-group row">
<label for="" class="col-sm-4 col-form-label col-form-label-sm">Tipo de impuesto</label>
<div class="col-sm-8">
<select v-model="item.impuesto" class="imp custom-select" id="imp">
<option v-for="imp in impuestos" v-bind:key="imp.id" v-bind:value="imp.id">{{ imp.nombre }}</option>
</select>
</div>
</div>
<div class="form-group row">
<label for="precioTotalEdit" class="col-sm-4 col-form-label col-form-label-sm">Subtotal <small id="simboloparatodos" class="text-muted">{{item.moneda_id}}</small></label>
<div class="col-sm-8">
<input type="text" v-model="subtotal" class="form-control form-control-sm" id="precioTotalEdit" onfocus="this.select();" readonly>
</div>
</div>
<div class="form-group row">
<label for="precioigvEdit" class="col-sm-4 col-form-label col-form-label-sm">I.G.V. </label>
<div class="col-sm-8">
<input type="text" v-model="igv" class="form-control form-control-sm" id="precioigvEdit" readonly>
</div>
</div>
<div class="form-group row">
<label for="descuentoEdit" class="col-sm-4 col-form-label col-form-label-sm">Descuento</label>
<div class="col-sm-8">
<input v-model="item.descuento" type="text" class="form-control form-control-sm" id="descuentoEdit" data-toggle="popover" data-placement="top" data-html="true" data-content="Para montos. Ej: 10<br>Para porcentajes agrega %. Ej: 10%" data-trigger="hover" tabindex="4" onfocus="this.select();">
</div>
</div>
<div class="form-group row">
<label for="precioSubtotalEdit" class="col-sm-4 col-form-label col-form-label-sm">Total <small id="simboloparatodos" class="text-muted">{{ moneda_id }}</small></label>
<div class="col-sm-8">
<input v-model="total" type="text" class="form-control font-weight-bold" id="precioSubtotalEdit" value="0" tabindex="5" onfocus="this.select();">
</div>
</div>
<div class="form-group row">
<label for="precio20Edit" class="col-sm-4 col-form-label col-form-label-sm">Precio <small class="text-muted" id="porcentajeinicial">+ {{item.primer_margen}}%</small></label>
<div class="col-sm-8">
<div id="margeninicialx">{{ item.masprimermargen }}</div>
</div>
</div>
<div class="form-group row">
<label for="precio35Edit" class="col-sm-4 col-form-label col-form-label-sm">Precio <small class="text-muted" id="porcentajefinal">+ {{item.segundo_margen}}%</small></label>
<div class="col-sm-8">
<div id="margenfinalx"> {{ item.massegundomargen }}</div>
</div>
</div>
<div class="form-group row">
<label for="itemEdit" class="col-sm-4 col-form-label col-form-label-sm">Item</label>
<div class="col-sm-8">
<small id="marcaEdit">{{ item.marca }}</small> {{ item.nombre }}<br><span class="text-muted small" title="códigos" id="codigoedit" v-for="cod in codigos" v-bind:key="cod.id">{{ cod.nombre_codigo }}-{{ cod.pivot.nombre }}<br></span>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">Cancelar</button>
<button type="button" class="btn btn-sm btn-primary" tabindex="5" #click="anadiritem">Añadir</button>
</div>
</div>
</div>
</div>
<!-- FIN DE MODAL PARA EDITAR ITEM -->
<!-- SECCION SELECCIONAR ITEM Y AGREGAR A LA TABLA -->
<div class="">
<div class="input-group input-group-sm mb-3">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-search"></i></span>
</div>
<div class="prefetch">
<input type="text" id="item" class="form-control typeahead" placeholder="Buscar item (Ctrl+.)" v-model.trim="q" #keyup.enter="buscaritem">
</div>
<div class="input-group-append">
<button class="btn btn-outline-secondary" id="button-addon2" disabled></button>
</div>
</div>
</div>
<table class="table table-striped table-hover table-sm table-responsive-md text-nowrap mt-3" id="detailFactura">
<thead>
<tr>
<th>Ítem</th>
<th class="text-right">Cnt.</th>
<th class="text-right">Costo U.</th>
<th></th>
<th class="text-right text-nowrap">
SubTotal
</th>
<th class="text-right text-nowrap">
I.G.V
</th>
<th class="text-right text-nowrap"><span class="text-muted">
Total
<div id="monedaText"></div>
</span></th>
</tr>
</thead>
<tbody>
<tr v-for="itemtabla in itemstabla" v-bind:key="itemtabla.id">
<td class="overflow-hidden" style="max-width: 299px; text-overflow: ellipsis">{{ item.nombre }} <small class="text-muted">({{ itemtabla.marca }})</small> </td>
<td class="text-right"><small class="text-muted mr-1" title="Unidades">{{ itemtabla.unidad }}</small>{{ itemtabla.cantidad }}</td>
<td class="text-right">{{ itemtabla.precio }}</td>
<td class="text-right">
<i class="fas fa-pencil-alt mr-2"></i>
<i class="far fa-trash-alt"></i>
</td>
<td class="text-right">{{ preciosegunmoneda(itemtabla.precio, itemtabla.moneda, itemtabla.cantidad, itemtabla.impuesto, itemtabla.descuento).subtotal }}</td>
<td class="text-right">{{ preciosegunmoneda(itemtabla.precio, itemtabla.moneda, itemtabla.cantidad, itemtabla.impuesto, itemtabla.descuento).igv }}</td>
<td class="text-right">{{ preciosegunmoneda(itemtabla.precio, itemtabla.moneda, itemtabla.cantidad, itemtabla.impuesto, itemtabla.descuento).total }}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="2" class="text-right" id="cantidadItems">{{ itemstabla.length }} </td>
<td class="small font-weight-bolder">ítem(s)</td>
</tr>
</tfoot>
</table>
<!-- FIN DE SECCION SELECCIONAR ITEM Y AGREGAR A LA TABLA -->
</div>
</template>
<script>
import {
required,
minLength,
maxLength,
between
} from 'vuelidate/lib/validators'
import Bloodhound from 'corejs-typeahead/dist/bloodhound';
import typeahead from 'corejs-typeahead/dist/typeahead.jquery';
export default {
name: 'tabla-item',
props: {
tienda: Number,
},
data() {
return {
resource: 'venta',
error: false,
submitStatus: null,
isLoading: false,
fullPage: true,
q: '',
suggestions: null,
item: {},
}
},
created() {
},
mounted() {
let contextoVue = this
// Cargar los datos del typeahead en items
this.suggestions = new Bloodhound({
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title'),
queryTokenizer: Bloodhound.tokenizers.whitespace,
identify: function (item) {
return item.id;
},
remote: {
url: '/item/records/' + this.tienda + '/' + '%QUERY',
wildcard: '%QUERY'
}
});
let inputEl = $('.prefetch input');
inputEl.typeahead({
minLength: 1,
highlight: true,
}, {
name: 'suggestions',
source: this.suggestions,
limit: 20,
display: 'label',
templates: {
suggestion: (data) => {
let codigos = data.codigos
let nuevoscodigos = ""
codigos.forEach(function (valor, indice, array) {
nuevoscodigos = nuevoscodigos + valor.pivot.nombre + ','
});
let nuevoprecio = ((parseFloat(contextoVue.preciosegunmoneda(data.precio, data.moneda,'1',data.impuesto_id).precio) * parseFloat(data.primer_margen) / 100) + parseFloat(contextoVue.preciosegunmoneda(data.precio, data.moneda,'1',data.impuesto_id).precio)).toFixed(2)
return `<div class="ss-suggestion">
<div class="codigos">
${data.nombre_marca} <span title="Código global"> - ${nuevoscodigos}</span>
<span title="Código de barras"></span>
</div>
<div class="searchproducto" style="text-transform: capitalize;">
<strong>${data.nombre}</strong>
</div>
<div class="d-flex mt-1"><div class="searchprice text-primary">${contextoVue.moneda_id}
<span id="pricechange">${nuevoprecio}</span></div>
<div class="searchstock flex-grow-1 text-danger"><b>Stock</b>:
<b style="color: red;">${data.stock}</b><b></b></div></div></div>`;
}
}
});
// Cuando se hace click en un item
$('.prefetch input').bind('typeahead:selected', function (evt, suggestion) {
$('#editarItem').modal('show')
let preciosegunmoneda = ((parseFloat(contextoVue.preciosegunmoneda(suggestion.precio, suggestion.moneda, '1', suggestion.impuesto_id).precio) * parseFloat(suggestion.primer_margen) / 100) + parseFloat(contextoVue.preciosegunmoneda(suggestion.precio, suggestion.moneda, '1', suggestion.impuesto_id).precio)).toFixed(2)
let massegundomargen = ((parseFloat(contextoVue.preciosegunmoneda(suggestion.precio, suggestion.moneda, '1', suggestion.impuesto_id).precio) * parseFloat(suggestion.segundo_margen) / 100) + parseFloat(contextoVue.preciosegunmoneda(suggestion.precio, suggestion.moneda, '1', suggestion.impuesto_id).precio)).toFixed(2)
contextoVue.item = {
id: suggestion.id,
stock: suggestion.stock,
nombre: suggestion.nombre,
precio: preciosegunmoneda,
marca: suggestion.nombre_marca,
impuesto: suggestion.impuesto_id,
primer_margen: suggestion.primer_margen,
segundo_margen: suggestion.segundo_margen,
masprimermargen: preciosegunmoneda,
massegundomargen: massegundomargen,
codigos: suggestion.codigos,
cantidad: '1',
descuento: '0',
moneda: contextoVue.moneda_id,
unidad: suggestion.nombre_unidad,
}
});
// Fin typeahead items
},
updated() {
},
computed: {
total() {
return this.item.precio * this.item.cantidad - this.item.descuento
},
igv() {
if(this.item.impuesto=="1") {
return (parseFloat(this.total) * 18/100).toFixed(2)
}
else {
return 0
}
},
subtotal() {
return (parseFloat(this.total) - this.igv).toFixed(2)
},
tipocambio: {
get() {
return this.$store.state.venta.tipocambio
},
set(value) {
this.$store.commit('settipocambio', value)
}
},
moneda_id: {
get() {
return this.$store.state.venta.moneda_id
},
set(value) {
this.$store.commit('setmonedaid', value)
}
},
itemstabla: {
get() {
return this.$store.state.itemstabla
},
},
impuestos: {
get() {
return this.$store.state.impuestos
},
},
},
watch: {
moneda_id: function (val) {
//this.$store.commit('actualizaritemstabla')
},
},
methods: {
submit() {},
preciosegunmoneda(precio, moneda, cantidad, impuesto, descuento) {
let total,nuevoprecio,subtotal,igv = 0
if ((this.moneda_id == 'S/' && moneda == 'S/') || (this.moneda_id == '$' && moneda == '$')) {
nuevoprecio = parseFloat(precio).toFixed(2)
total = parseFloat((parseFloat(precio) * cantidad) - descuento).toFixed(2)
igv = parseFloat((parseFloat(total) * (impuesto=='1' ? 18/100 : 0))).toFixed(2)
subtotal = parseFloat(total - igv).toFixed(2)
return {precio: nuevoprecio, total: total, igv: igv, subtotal: subtotal }
}
else if (this.moneda_id == 'S/' && moneda == '$') {
nuevoprecio = parseFloat(precio * this.tipocambio).toFixed(2)
total = parseFloat((parseFloat(precio * this.tipocambio) * cantidad) - descuento).toFixed(2)
igv = parseFloat((parseFloat(total) * (impuesto=='1' ? 18/100 : 0))).toFixed(2)
subtotal = parseFloat(total - igv).toFixed(2)
return {precio: nuevoprecio, total: total, igv: igv, subtotal: subtotal }
}
else if (this.moneda_id == '$' && moneda == 'S/') {
nuevoprecio = parseFloat(precio / this.tipocambio).toFixed(2)
total = parseFloat((parseFloat(precio / this.tipocambio) * cantidad) - descuento).toFixed(2)
igv = parseFloat((parseFloat(total) * (impuesto=='1' ? 18/100 : 0))).toFixed(2)
subtotal = parseFloat(total - igv).toFixed(2)
return {precio: nuevoprecio, total: total, igv: igv, subtotal: subtotal }
}
},
deleteitem(i) {
this.$store.commit('EliminarItemTabla', i);
},
editaritem(item) {
$('#editarItem').modal('show')
this.item = item
},
anadiritem(event) {
$('#editarItem').modal('hide')
this.$store.commit('setitemstabla', this.item )
},
},
validations: {
form: {
nombre_codigo: {
required,
},
}
}
}
</script>

With the use of editaritem like you do, I think you're actually passing the item itself, so it gets modified as it's a reference.
editaritem(item) {
$('#editarItem').modal('show')
this.item = { ...item } // or some way to copy the object
}
And after you finish edit, perform the actual update of the edited object.

Related

how do I put a button to select the data of a registration and update this code in Vue.js? (CLOSED)

I made a "table" where I register this data, it is a crud that saves in memory and I already made the option to save and delete, I now need to update/edit, I would like to make a button that selects the registration and the data of it appears for editing, basically it's a simple crud system in Vue.js. I've done it to save and to delete, now I need the rest.
<body>
<div id="app">
<div class="container">
<h1>Formulário de Cadastro v.Vue</h1>
<form class="row">
<div class="col-sm-2">
<label>Nome:</label>
<input type="text" placeholder="Nome" v-model="form.name" class="form-control">
</div>
<div class="col-sm-2">
<label>Idade:</label>
<input type="number" placeholder="Idade" v-model="form.age" class="form-control">
</div>
<div class="col-sm-2">
<label>Telefone:</label>
<input type="tel" placeholder="Telefone" v-model="form.phone" class="form-control">
</div>
<div class="col-sm-2">
<label>Gênero:</label>
<select class="form-select form-control" v-model="form.gender">
<option>Masculino</option>
<option>Feminino</option>
</select>
</div>
<div class="col-sm-2">
<label>Data de Cadastro:</label>
<input type="date" v-model="form.registerdate" class="form-control">
</div>
</form>
<br />
<div>
<input type="button" value="Salvar" v-on:click="save" class="btn btn-sm btn-success" />
<input type="button" value="Atualizar" v-on:click="update" class="btn btn-sm btn-primary" />
</div>
<br />
<div class="container-fluid">
<table class="table table-sm table-bordered" v-if="contacts.length > 0">
<tr>
<th>ID</th>
<th>Nome</th>
<th>Idade</th>
<th>Telefone</th>
<th>Gênero</th>
<th>Data de Cadastro</th>
<th>Opções</th>
</tr>
<tr v-for="contact in contacts">
<td>{{contact.id}}</td>
<td>{{contact.name}}</td>
<td>{{contact.age}}</td>
<td>{{contact.phone}}</td>
<td>{{contact.gender}}</td>
<td>{{contact.registerdate}}</td>
<td>
<input type="button" value="Excluir" class="btn btn-sm btn-danger"
v-on:click="remove(contact.id)" />
<input type="button" value="Selecionar" class="btn btn-sm btn-primary"
v-on:click="select(contact.id)" />
</td>
</tr>
<div :class="classAlert" role="alert" v-if="showMessage">
{{messageAlert}}
</div>
</table>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue#2/dist/vue.js"></script>
<script>
var app = new Vue({
el: "#app",
data: {
classAlert: '',
messageAlert: '',
showMessage: false,
inputName: '',
currentCount: 0,
currentId: 1,
form: {
name: '',
age: 0,
phone: '',
gender: '',
registerdate: ''
},
contacts: []
},
methods: {
incrementCount: function () {
this.currentCount++;
},
save: function () {
var _this = this;
// if (this.selectContact === null) {
// }
// else {
// }
this.contacts.push({
id: this.currentId++,
name: this.form.name,
age: this.form.age,
phone: this.form.phone,
gender: this.form.gender,
registerdate: this.form.registerdate
});
this.classAlert = 'alert alert-success';
this.messageAlert = "Salvo Com sucesso";
this.showMessage = true;
setTimeout(function () {
_this.showMessage = false
}, 3000);
},
remove: function (id) {
var _this = this;
this.contacts = this.contacts.filter(p => p.id != id);
this.classAlert = 'alert alert-danger'
this.messageAlert = "Excluido Com sucesso";
this.showMessage = true;
setTimeout(function () {
_this.showMessage = false
}, 3000);
},
select: function (id) {
var _this = this;
const contact = this.contacts.filter(contact => contact.id === id);
this.form.name = contact[0].name;
this.form.age = contact[0].age;
this.form.phone = contact[0].phone;
this.form.gender = contact[0].gender;
this.form.registerdate = contact[0].registerdate;
},
update: function () {
var _this = this;
const l_contactUpdate = this.contacts.filter(l_contactUpdate => l_contactUpdate.name === name);
this.form.name = form.name;
}
// update/edit method??
}
});
</script>

Vue shopping cart add to cart not working

I cannot see what i have done wrong, i am trying to add my products to my cart but i am not having any luck. How do i add to cart? i can figure out the delete and clear from understanding how to add to cart. i have read through some documentation and managed to get this far but now i am stuck.
i am not sure if i am targeting wrong or i have named something wrong. I used a normal array and it worked alright but i am trying to do it with an api now, does this change the methods?
<section>
<div class ="container" >
<div id="app" >
<div class="row">
<div class="col-md-12" >
<div class="card">
<div class="card-body">
<!-- Cart -->
<table>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
<tr v-for="(product, index) in cart">
<td>{{ item.productName }}</td>
<td>{{ item.price }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.price * item.quantity }} Coins</td>
</tr>
</table>
</div>
</div>
<!-- Shop -->
<div class="row">
<div class="col-md-3" v-for="product in products" :key="product.id" >
<div class="card" >
<div class="card-body">
<div class="product">
<img :src="product.productImage" alt="Product Img">
<h4 class="text-info">{{ product.productName }}</h4>
<h4 class="text-muted">{{ product.price }} Coins</h4>
<p class="text-muted">{{ product.productDescription }}</p>
<input type="number" name="quantity" class="form-control" value="1">
<input type="hidden" name="productName" value="{{productID}}">
<input type="hidden" name="price" value="{{productID.price}}">
<input type="submit" name="add_to_cart" #click="addToCart" class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" value="Add to Cart">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script>
var app = new Vue({
el: '#app',
data: {
products: '',
cart: []
},
methods: {
getproducts: function () {
axios.get('http://localhost:8888/DevsDev2022/productAPI/products')
.then(function (response) {
app.products = response.data;
})
.catch(function (error) {
console.log(error);
});
},
addToCart: function (product) {
var cartItem = this.cart.find(item => item.productName === product.productName);
if (cartItem) {
cartItem.quantity++;
} else {
this.cart.push({
productName: product.productName,
price: product.price,
quantity: 1
});
}
},
removeFromCart: function (product) {
var cartItem = this.cart.find(item => item.productName === product.productName);
if (cartItem.quantity > 1) {
cartItem.quantity--;
} else {
this.cart.splice(this.cart.indexOf(cartItem), 1);
}
},
clearCart: function () {
this.cart = [];
}
},
mounted: function () {
this.getproducts();
}
});
</script>
You are missing your template tag around your template
Please take a look at following snippet:
new Vue({
el: '#app',
data() {
return {
products: [{id: 1, productName: 'aaa', price: 25, productDescription: 'rrrrrrr'}, {id: 2, productName: 'bbb', price: 35, productDescription: 'eeeee'}],
cart: []
}
},
methods: {
getproducts() {
/*axios.get('http://localhost:8888/DevsDev2022/productAPI/products')
.then(function (response) {
app.products = response.data;
})
.catch(function (error) {
console.log(error);
});*/
},
addToCart(product) {
var cartItem = this.cart.find(item => item.productName === product.productName);
if (cartItem) {
cartItem.quantity++;
} else {
this.cart.push({
productName: product.productName,
price: product.price,
quantity: 1
});
}
},
removeFromCart(product) {
var cartItem = this.cart.find(item => item.productName === product.productName);
if (cartItem.quantity > 1) {
cartItem.quantity--;
} else {
this.cart.splice(this.cart.indexOf(cartItem), 1);
}
},
clearCart() {
this.cart = [];
}
},
mounted: function () {
this.getproducts();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<section>
<div class ="container" >
<div class="row">
<div class="col-md-12" >
<div class="card">
<div class="card-body">
<!-- Cart -->
<table>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
<tr v-for="(product, index) in cart">
<td>{{ product.productName }}</td>
<td>{{ product.price }}</td>
<td>{{ product.quantity }}</td>
<td>{{ product.price * product.quantity }} Coins</td>
</tr>
</table>
</div>
</div>
<!-- Shop -->
<div class="row">
<div class="col-md-3" v-for="product in products" :key="product.id" >
<div class="card" >
<div class="card-body">
<div class="product">
<img :src="product.productImage" alt="Product Img">
<h4 class="text-info">{{ product.productName }}</h4>
<h4 class="text-muted">{{ product.price }} Coins</h4>
<p class="text-muted">{{ product.productDescription }}</p>
<input type="number" name="quantity" class="form-control" value="1">
<input type="hidden" name="productName" :value="product.name">
<input type="hidden" name="price" :value="product.price">
<input type="submit" name="add_to_cart" #click="addToCart(product)" class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" value="Add to Cart">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>

The below given code is not working for me. And giving an error "vueuploadComponent.html:396 Uncaught ReferenceError: FileUpload is not defined"

Here is the code in vue.js for multi Image uploading. Actually this code is not working for me I am facing an error "vueuploadComponent.html:396 Uncaught ReferenceError: FileUpload is not defined". So here I thing i doing some thing wrong with import a module. So please let me know where I am going wrong.
<template>
<div id = "vueUpload" class="example-full">
<button type="button" class="btn btn-danger float-right btn-is-option" #click.prevent="isOption = !isOption">
<i class="fa fa-cog" aria-hidden="true"></i>
Options
</button>
<h1 id="example-title" class="example-title">Full Example</h1>
<div v-show="$refs.upload && $refs.upload.dropActive" class="drop-active">
<h3>Drop files to upload</h3>
</div>
<div class="upload" v-show="!isOption">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Thumb</th>
<th>Name</th>
<th>Width</th>
<th>Height</th>
<th>Size</th>
<th>Speed</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr v-if="!files.length">
<td colspan="9">
<div class="text-center p-5">
<h4>Drop files anywhere to upload<br/>or</h4>
<label :for="name" class="btn btn-lg btn-primary">Select Files</label>
</div>
</td>
</tr>
<tr v-for="(file, index) in files" :key="file.id">
<td>{{index}}</td>
<td>
<img class="td-image-thumb" v-if="file.thumb" :src="file.thumb" />
<span v-else>No Image</span>
</td>
<td>
<div class="filename">
{{file.name}}
</div>
<div class="progress" v-if="file.active || file.progress !== '0.00'">
<div :class="{'progress-bar': true, 'progress-bar-striped': true, 'bg-danger': file.error, 'progress-bar-animated': file.active}" role="progressbar" :style="{width: file.progress + '%'}">{{file.progress}}%</div>
</div>
</td>
<td>{{file.width || 0}}</td>
<td>{{file.height || 0}}</td>
<td>{{$formatSize(file.size)}}</td>
<td>{{$formatSize(file.speed)}}</td>
<td v-if="file.error">{{file.error}}</td>
<td v-else-if="file.success">success</td>
<td v-else-if="file.active">active</td>
<td v-else></td>
<td>
<div class="btn-group">
<button class="btn btn-secondary btn-sm dropdown-toggle" type="button">
Action
</button>
<div class="dropdown-menu">
<a :class="{'dropdown-item': true, disabled: file.active || file.success || file.error === 'compressing' || file.error === 'image parsing'}" href="#" #click.prevent="file.active || file.success || file.error === 'compressing' ? false : onEditFileShow(file)">Edit</a>
<a :class="{'dropdown-item': true, disabled: !file.active}" href="#" #click.prevent="file.active ? $refs.upload.update(file, {error: 'cancel'}) : false">Cancel</a>
<a class="dropdown-item" href="#" v-if="file.active" #click.prevent="$refs.upload.update(file, {active: false})">Abort</a>
<a class="dropdown-item" href="#" v-else-if="file.error && file.error !== 'compressing' && file.error !== 'image parsing' && $refs.upload.features.html5" #click.prevent="$refs.upload.update(file, {active: true, error: '', progress: '0.00'})">Retry upload</a>
<a :class="{'dropdown-item': true, disabled: file.success || file.error === 'compressing' || file.error === 'image parsing'}" href="#" v-else #click.prevent="file.success || file.error === 'compressing' || file.error === 'image parsing' ? false : $refs.upload.update(file, {active: true})">Upload</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#" #click.prevent="$refs.upload.remove(file)">Remove</a>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="example-foorer">
<div class="footer-status float-right">
Drop: {{$refs.upload ? $refs.upload.drop : false}},
Active: {{$refs.upload ? $refs.upload.active : false}},
Uploaded: {{$refs.upload ? $refs.upload.uploaded : true}},
Drop active: {{$refs.upload ? $refs.upload.dropActive : false}}
</div>
<div class="btn-group">
<file-upload
class="btn btn-primary dropdown-toggle"
:post-action="postAction"
:put-action="putAction"
:extensions="extensions"
:accept="accept"
:multiple="multiple"
:directory="directory"
:create-directory="createDirectory"
:size="size || 0"
:thread="thread < 1 ? 1 : (thread > 5 ? 5 : thread)"
:headers="headers"
:data="data"
:drop="drop"
:drop-directory="dropDirectory"
:add-index="addIndex"
v-model="files"
#input-filter="inputFilter"
#input-file="inputFile"
ref="upload">
<i class="fa fa-plus"></i>
Select
</file-upload>
<div class="dropdown-menu">
<label class="dropdown-item" :for="name">Add files</label>
<a class="dropdown-item" href="#" #click="onAddFolder">Add folder</a>
<a class="dropdown-item" href="#" #click.prevent="addData.show = true">Add data</a>
</div>
</div>
<button type="button" class="btn btn-success" v-if="!$refs.upload || !$refs.upload.active" #click.prevent="$refs.upload.active = true">
<i class="fa fa-arrow-up" aria-hidden="true"></i>
Start Upload
</button>
<button type="button" class="btn btn-danger" v-else #click.prevent="$refs.upload.active = false">
<i class="fa fa-stop" aria-hidden="true"></i>
Stop Upload
</button>
</div>
</div>
<div class="option" v-show="isOption">
<div class="form-group">
<label for="accept">Accept:</label>
<input type="text" id="accept" class="form-control" v-model="accept">
<small class="form-text text-muted">Allow upload mime type</small>
</div>
<div class="form-group">
<label for="extensions">Extensions:</label>
<input type="text" id="extensions" class="form-control" v-model="extensions">
<small class="form-text text-muted">Allow upload file extension</small>
</div>
<div class="form-group">
<label>PUT Upload:</label>
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio" name="put-action" id="put-action" value="" v-model="putAction"> Off
</label>
</div>
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="radio" name="put-action" id="put-action" value="/upload/put" v-model="putAction"> On
</label>
</div>
<small class="form-text text-muted">After the shutdown, use the POST method to upload</small>
</div>
<div class="form-group">
<label for="thread">Thread:</label>
<input type="number" max="5" min="1" id="thread" class="form-control" v-model.number="thread">
<small class="form-text text-muted">Also upload the number of files at the same time (number of threads)</small>
</div>
<div class="form-group">
<label for="size">Max size:</label>
<input type="number" min="0" id="size" class="form-control" v-model.number="size">
</div>
<div class="form-group">
<label for="minSize">Min size:</label>
<input type="number" min="0" id="minSize" class="form-control" v-model.number="minSize">
</div>
<div class="form-group">
<label for="autoCompress">Automatically compress:</label>
<input type="number" min="0" id="autoCompress" class="form-control" v-model.number="autoCompress">
<small class="form-text text-muted" v-if="autoCompress > 0">More than {{$formatSize(autoCompress)}} files are automatically compressed</small>
<small class="form-text text-muted" v-else>Set up automatic compression</small>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="add-index" class="form-check-input" v-model="addIndex"> Start position to add
</label>
</div>
<small class="form-text text-muted">Add a file list to start the location to add</small>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="drop" class="form-check-input" v-model="drop"> Drop
</label>
</div>
<small class="form-text text-muted">Drag and drop upload</small>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="drop-directory" class="form-check-input" v-model="dropDirectory"> Drop directory
</label>
</div>
<small class="form-text text-muted">Not checked, filter the dragged folder</small>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="create-directory" class="form-check-input" v-model="createDirectory"> Create Directory
</label>
</div>
<small class="form-text text-muted">The directory file will send an upload request. The mime type is <code>text/directory</code></small>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input type="checkbox" id="upload-auto" class="form-check-input" v-model="uploadAuto"> Auto start
</label>
</div>
<small class="form-text text-muted">Automatically activate upload</small>
</div>
<div class="form-group">
<button type="button" class="btn btn-primary btn-lg btn-block" #click.prevent="isOption = !isOption">Confirm</button>
</div>
</div>
<div :class="{'modal-backdrop': true, 'fade': true, show: addData.show}"></div>
<div :class="{modal: true, fade: true, show: addData.show}" id="modal-add-data" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Add data</h5>
<button type="button" class="close" #click.prevent="addData.show = false">
<span>×</span>
</button>
</div>
<form #submit.prevent="onAddData">
<div class="modal-body">
<div class="form-group">
<label for="data-name">Name:</label>
<input type="text" class="form-control" required id="data-name" placeholder="Please enter a file name" v-model="addData.name">
<small class="form-text text-muted">Such as <code>filename.txt</code></small>
</div>
<div class="form-group">
<label for="data-type">Type:</label>
<input type="text" class="form-control" required id="data-type" placeholder="Please enter the MIME type" v-model="addData.type">
<small class="form-text text-muted">Such as <code>text/plain</code></small>
</div>
<div class="form-group">
<label for="content">Content:</label>
<textarea class="form-control" required id="content" rows="3" placeholder="Please enter the file contents" v-model="addData.content"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" #click.prevent="addData.show = false">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
<div :class="{'modal-backdrop': true, 'fade': true, show: editFile.show}"></div>
<div :class="{modal: true, fade: true, show: editFile.show}" id="modal-edit-file" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit file</h5>
<button type="button" class="close" #click.prevent="editFile.show = false">
<span>×</span>
</button>
</div>
<form #submit.prevent="onEditorFile">
<div class="modal-body">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control" required id="name" placeholder="Please enter a file name" v-model="editFile.name">
</div>
<div class="form-group" v-if="editFile.show && editFile.blob && editFile.type && editFile.type.substr(0, 6) === 'image/'">
<label>Image: </label>
<div class="edit-image">
<img :src="editFile.blob" ref="editImage" />
</div>
<div class="edit-image-tool">
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" #click="editFile.cropper.rotate(-90)" title="cropper.rotate(-90)"><i class="fa fa-undo" aria-hidden="true"></i></button>
<button type="button" class="btn btn-primary" #click="editFile.cropper.rotate(90)" title="cropper.rotate(90)"><i class="fa fa-repeat" aria-hidden="true"></i></button>
</div>
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" #click="editFile.cropper.crop()" title="cropper.crop()"><i class="fa fa-check" aria-hidden="true"></i></button>
<button type="button" class="btn btn-primary" #click="editFile.cropper.clear()" title="cropper.clear()"><i class="fa fa-remove" aria-hidden="true"></i></button>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" #click.prevent="editFile.show = false">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script src='https://unpkg.com/vue#2.5.16/dist/vue.js'></script>
<script type="module">
import Cropper from 'cropperjs'
import ImageCompressor from '#xkeshi/image-compressor'
import { FileUpload } from "vue-upload-component";
</script>
<script>
new Vue({
el: '#vueUpload',
components: {
FileUpload,
},
data() {
return {
files: [],
accept: 'image/png,image/gif,image/jpeg,image/webp',
extensions: 'gif,jpg,jpeg,png,webp',
// extensions: ['gif', 'jpg', 'jpeg','png', 'webp'],
// extensions: /\.(gif|jpe?g|png|webp)$/i,
minSize: 1024,
size: 1024 * 1024 * 10,
multiple: true,
directory: false,
drop: true,
dropDirectory: true,
createDirectory: false,
addIndex: false,
thread: 3,
name: 'file',
postAction: '/upload/post',
putAction: '/upload/put',
headers: {
'X-Csrf-Token': 'xxxx',
},
data: {
'_csrf_token': 'xxxxxx',
},
autoCompress: 1024 * 1024,
uploadAuto: false,
isOption: false,
addData: {
show: false,
name: '',
type: '',
content: '',
},
editFile: {
show: false,
name: '',
}
}
},
watch: {
'editFile.show'(newValue, oldValue) {
// 关闭了 自动删除 error
if (!newValue && oldValue) {
this.$refs.upload.update(this.editFile.id, { error: this.editFile.error || '' })
}
if (newValue) {
this.$nextTick( () => {
if (!this.$refs.editImage) {
return
}
let cropper = new Cropper(this.$refs.editImage, {
autoCrop: false,
})
this.editFile = {
...this.editFile,
cropper
}
})
}
},
'addData.show'(show) {
if (show) {
this.addData.name = ''
this.addData.type = ''
this.addData.content = ''
}
},
},
methods: {
inputFilter(newFile, oldFile, prevent) {
if (newFile && !oldFile) {
// Before adding a file
// 添加文件前
// Filter system files or hide files
// 过滤系统文件 和隐藏文件
if (/(\/|^)(Thumbs\.db|desktop\.ini|\..+)$/.test(newFile.name)) {
return prevent()
}
// Filter php html js file
// 过滤 php html js 文件
if (/\.(php5?|html?|jsx?)$/i.test(newFile.name)) {
return prevent()
}
// Automatic compression
// 自动压缩
if (newFile.file && newFile.error === "" && newFile.type.substr(0, 6) === 'image/' && this.autoCompress > 0 && this.autoCompress < newFile.size) {
newFile.error = 'compressing'
const imageCompressor = new ImageCompressor(null, {
convertSize: 1024 * 1024,
maxWidth: 512,
maxHeight: 512,
})
imageCompressor.compress(newFile.file)
.then((file) => {
this.$refs.upload.update(newFile, { error: '', file, size: file.size, type: file.type })
})
.catch((err) => {
this.$refs.upload.update(newFile, { error: err.message || 'compress' })
})
}
}
if (newFile && newFile.error === "" && newFile.file && (!oldFile || newFile.file !== oldFile.file)) {
// Create a blob field
// 创建 blob 字段
newFile.blob = ''
let URL = (window.URL || window.webkitURL)
if (URL) {
newFile.blob = URL.createObjectURL(newFile.file)
}
// Thumbnails
// 缩略图
newFile.thumb = ''
if (newFile.blob && newFile.type.substr(0, 6) === 'image/') {
newFile.thumb = newFile.blob
}
}
// image size
// image 尺寸
if (newFile && newFile.error === '' && newFile.type.substr(0, 6) === "image/" && newFile.blob && (!oldFile || newFile.blob !== oldFile.blob)) {
newFile.error = 'image parsing'
let img = new Image();
img.onload = () => {
this.$refs.upload.update(newFile, {error: '', height: img.height, width: img.width})
}
img.οnerrοr = (e) => {
this.$refs.upload.update(newFile, { error: 'parsing image size'})
}
img.src = newFile.blob
}
},
// add, update, remove File Event
inputFile(newFile, oldFile) {
if (newFile && oldFile) {
// update
if (newFile.active && !oldFile.active) {
// beforeSend
// min size
if (newFile.size >= 0 && this.minSize > 0 && newFile.size < this.minSize) {
this.$refs.upload.update(newFile, { error: 'size' })
}
}
if (newFile.progress !== oldFile.progress) {
// progress
}
if (newFile.error && !oldFile.error) {
// error
}
if (newFile.success && !oldFile.success) {
// success
}
}
if (!newFile && oldFile) {
// remove
if (oldFile.success && oldFile.response.id) {
// $.ajax({
// type: 'DELETE',
// url: '/upload/delete?id=' + oldFile.response.id,
// })
}
}
// Automatically activate upload
if (Boolean(newFile) !== Boolean(oldFile) || oldFile.error !== newFile.error) {
if (this.uploadAuto && !this.$refs.upload.active) {
this.$refs.upload.active = true
}
}
},
alert(message) {
alert(message)
},
onEditFileShow(file) {
this.editFile = { ...file, show: true }
this.$refs.upload.update(file, { error: 'edit' })
},
onEditorFile() {
if (!this.$refs.upload.features.html5) {
this.alert('Your browser does not support')
this.editFile.show = false
return
}
let data = {
name: this.editFile.name,
error: '',
}
if (this.editFile.cropper) {
let binStr = atob(this.editFile.cropper.getCroppedCanvas().toDataURL(this.editFile.type).split(',')[1])
let arr = new Uint8Array(binStr.length)
for (let i = 0; i < binStr.length; i++) {
arr[i] = binStr.charCodeAt(i)
}
data.file = new File([arr], data.name, { type: this.editFile.type })
data.size = data.file.size
}
this.$refs.upload.update(this.editFile.id, data)
this.editFile.error = ''
this.editFile.show = false
},
// add folder
onAddFolder() {
if (!this.$refs.upload.features.directory) {
this.alert('Your browser does not support')
return
}
let input = document.createElement('input')
input.style = "background: rgba(255, 255, 255, 0);overflow: hidden;position: fixed;width: 1px;height: 1px;z-index: -1;opacity: 0;"
input.type = 'file'
input.setAttribute('allowdirs', true)
input.setAttribute('directory', true)
input.setAttribute('webkitdirectory', true)
input.multiple = true
document.querySelector("body").appendChild(input)
input.click()
input.onchange = (e) => {
this.$refs.upload.addInputFile(input).then(function() {
document.querySelector("body").removeChild(input)
})
}
},
onAddData() {
this.addData.show = false
if (!this.$refs.upload.features.html5) {
this.alert('Your browser does not support')
return
}
let file = new window.File([this.addData.content], this.addData.name, {
type: this.addData.type,
})
this.$refs.upload.add(file)
}
}
})
</script>
Assuming you're using Lian Yue's vue-upload-component and have installed it successfully, try removing the curly braces from your import:
import FileUpload from "vue-upload-component";
This will set FileUpload to the default export from vue-upload-component which I believe is what you want

ERROR 422 when update data inside modal with axios.put [laravel + vuejs 3]

I am using Laravel + VueJS 3 to build a small project,
I use axios.put method for update details for single user in a table row via a modal, but I have problems when I click on submit button of a form inside a modal with axios.put, even I filled all data for all inputs but It still said the errors below, can you guys show me how can I fix this please?
Thanks!
ERROR
My backend:
public function updateUser(Request $req, User $user)
{
$req->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,' . $user->id,
'password' => 'same:confirmPassword|max:64',
'roles' => 'required',
]);
$input = $req->all();
if (!empty($input['password'])) {
$input['password'] = Hash::make($input['password']);
} else {
$input = Arr::except($input, 'password');
}
$user->update($input);
$user->syncRoles($input['roles']);
return $this->sendResponse($user, 'Updated!');
}
My JS Code:
import axios from "axios";
let API_URL = "http://localhost:8000";
export default {
name: "manageUsers",
components: {},
data() {
return {
users: [],
userInfo: {
id: 0,
name: "",
username: "",
phone: "",
email: "",
password: "",
},
};
},
methods: {
refreshUsers() {
axios.get(`${API_URL}/api/users/allusers`).then((res) => {
this.users = res.data.data;
});
},
getUserInfo(user) {
axios
.get(`${API_URL}/api/users/show/` + user.id)
.then((res) => {
this.userInfo.id = res.data.data.id;
this.userInfo.name = res.data.data.name;
this.userInfo.email = res.data.data.email;
this.userInfo.username = res.data.data.username;
this.userInfo.phone = res.data.data.phone;
})
.catch((error) => {
console.log("ERRRR:: ", error.response.data);
});
},
updateUser() {
axios
.put(`${API_URL}/api/users/update/${this.userInfo.id}`)
.then((res) => {
this.refreshUsers();
alert(res.data);
})
.catch((error) => {
console.log("ERRRR:: ", error.response.data);
});
},
},
mounted() {
this.refreshUsers();
},
};
My VueJS template code:
<template>
<table class="table table-striped" id="datatable">
<tbody>
<tr v-for="(user, id) in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.email }}</td>
<td>{{ user.username }}</td>
<td class="text-right">
<button
class="btn btn-link btn-warning btn-icon btn-sm"
data-toggle="modal" data-target="#userEditModal"
#click="getUserInfo(user)">
<i class="tim-icons icon-pencil"></i>
</button>
</td>
</tr>
</tbody>
</table>
<!-- EDIT USER MODAL -->
<div class="modal modal-black fade" id="userEditModal" tabindex="-1" role="dialog"
aria-labelledby="userEditModal" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="userEditModal">
Edit user <strong class="text-primary">{{ userInfo.username }}</strong>
</h4>
<button type="button" class="close"
data-dismiss="modal" aria-hidden="true">
<i class="tim-icons icon-simple-remove"></i>
</button>
</div>
<form class="form-horizontal">
<div class="modal-body">
<div class="d-flex flex-row">
<label class="col-md-3 col-form-label">Username</label>
<div class="col-md-9">
<div class="form-group">
<input type="text" class="form-control" name="username"
v-model="userInfo.username" />
</div>
</div>
</div>
<div class="d-flex flex-row">
<label class="col-md-3 col-form-label">Name</label>
<div class="col-md-9">
<div class="form-group">
<input type="text" class="form-control" name="name"
v-model="userInfo.name" />
</div>
</div>
</div>
<div class="d-flex flex-row">
<label class="col-md-3 col-form-label">Email</label>
<div class="col-md-9">
<div class="form-group">
<input type="email" name="email" class="form-control"
v-model="userInfo.email" />
</div>
</div>
</div>
<div class="d-flex flex-row">
<label class="col-md-3 col-form-label">Roles</label>
<div class="col-md-9">
<div class="form-group">
<input type="roles" name="roles" class="form-control"
v-model="userInfo.roles" />
</div>
</div>
</div>
</div>
<div class="modal-footer d-flex flex-row">
<button type="button" class="btn btn-secondary" data-dismiss="modal">
Close
</button>
<button type="submit" class="btn btn-primary" data-dismiss="modal"
#click="updateUser()">
Save changes
</button>
</div>
</form>
</div>
</div>
</div>
<!-- END EDIT USER MODAL -->
</template>```
I think you're not passing any parameters to your put call. axios docs
example:
axios.put('https://httpbin.org/put', { hello: 'world' });
When an issue like this arises you can always check your network tab in your browser. to see what data is send to your server.

Using same .vue component twice in same parent component - how to differentiate

I want to use the same component twice from a parent component; all that was stopping me was the use of an ID for a dropdown in the subcomponents! So I currently have duplicated the subcomponent.
My parent component:
<template>
<div id="compare-page">
<div class="row">
<div class="col-md-12">
<div class="row campaign-overview">
<div class="col-md-4">
<h2>{{ campaign.name }}</h2>
<strong>Published</strong>
<strong>Starts: {{ campaign.start_date }} - Ends: {{ campaign.end_date }}</strong>
</div>
<div class="col-md-4 text-right">
<select name="daterange" id="" #change="onChangeDaterange($event)">
<option v-for="(daterange, index) in dateranges" v-bind:key="daterange.index" :value="index" :data-start="daterange.start" :data-end="daterange.end" :selected="daterange.optionSelected == 1">{{ index }}</option>
</select>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<left-device ref="leftDevice"></left-device>
</div>
<div class="col-md-6">
<right-device ref="rightDevice"></right-device>
</div>
</div>
</div>
</template>
<script>
import left from './compared_device_component_left.vue';
import right from './compared_device_component_right.vue';
export default {
components: {
'left-device': left,
'right-device': right,
},
mounted() {
this.fetchCampaignData().then(result => {
});
},
data() {
return {
campaign_id: this.$route.params.campaign_id,
campaign: [],
dateranges: [],
leftDevice: '',
rightDevice: ''
}
},
methods: {
fetchCampaignData() {
return this.$http.get('/api/portal/campaign/' + this.campaign_id + '/campaign?' + this.axiosParams)
.then(function(data){
return data.json();
}).then(function(data){
this.campaign = data.campaign;
this.dateranges = data.dateranges;
});
},
onChangeDaterange(event) {
this.$router.push('?range=' + event.target.value);
this.$refs.leftDevice.onChangeDaterange(event);
this.$refs.rightDevice.onChangeDaterange(event);
},
},
computed: {
axiosParams() {
const params = new URLSearchParams();
if(this.$route.query.range) params.append('range', this.$route.query.range);
return params;
}
},
}
</script>
<style lang="scss">
</style>
One of my subcomponents
<template>
<div>
<div class="row device-overview">
<div class="col-md-4">
<select name="devices" id="deviceLeft" #change="onChangeDevice($event)">
<option v-for="(device, index) in devices" v-bind:key="device.index" :value="index" :data-device_id="device.device_id" :data-address="device.address" :selected="device.optionSelected == 1">{{ device.device_id }} | {{ device.address }}</option>
</select>
</div>
</div>
<div class="row totals">
<div class="col-md-4 card">
<div class="card-body">
<h5 class="card-title">Total Interactions</h5>
<p class="card-text">{{ total_interactions }}</p>
</div>
</div>
<div class="col-md-4 card">
<div class="card-body">
<h5 class="card-title">Average Interactions Per Day</h5>
<p class="card-text">{{ average_daily_interactions }}</p>
</div>
</div>
<div class="col-md-4 card">
<div class="card-body">
<h5 class="card-title">Total Average Dwell Time</h5>
<p class="card-text">{{ average_dwell_time }}s</p>
</div>
</div>
</div>
<div class="row interactions">
<div class="col-md-12">
<h5>Total Interactions Per Product</h5>
<table class="table table-condensed table-hover">
<tbody>
<tr>
<td class="">Tag</td>
<td class="">Mapped Product</td>
<td class="">Lifts</td>
</tr>
<tr v-for="interaction in table_product_interactions">
<td class="">{{ interaction.tag }}</td>
<td class="">{{ interaction.mapping }}</td>
<td>{{ interaction.tag_count }}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-12">
<h5>Total Interactions Per Day</h5>
<div class="chart" ref="chartdaily"></div>
</div>
</div>
<div class="row interactions">
<div class="col-md-8">
<h5>Total Interactions Per Hour</h5>
<div class="chart" ref="charthourly"></div>
</div>
</div>
</div>
</template>
<script>
import * as am4core from "#amcharts/amcharts4/core";
import * as am4charts from "#amcharts/amcharts4/charts";
import am4themes_animated from "#amcharts/amcharts4/themes/animated";
am4core.useTheme(am4themes_animated);
export default {
mounted() {
this.fetchData(null).then(result => {
this.chartTotalPerDay();
this.chartTotalPerHour();
});
},
data() {
return {
campaign_id: this.$route.params.campaign_id,
campaign: [],
dateranges: [],
device: [],
devices: [],
total_interactions: '',
average_daily_interactions: '',
average_dwell_time: '',
interaction_type: '',
chart_daily_interactions: [],
chart_hourly_interactions: [],
table_product_interactions: []
}
},
methods: {
fetchData(deviceID) {
// Use JS to get by ID!
var device = document.getElementById("deviceLeft");
// ... call to API
},
chartTotalPerDay() {
// ...
},
chartTotalPerHour() {
// ...
},
onChangeDaterange(event) {
// Use JS to get by ID!
var device = document.getElementById("deviceLeft");
// st cookie
document.cookie = "deviceLeft="+device.value;
this.fetchData(device.value).then(result => {
this.chartTotalPerDay();
this.chartTotalPerHour();
});
},
onChangeDevice(event) {
// st cookie
document.cookie = "deviceLeft="+event.target.value;
this.fetchData(event.target.value).then(result => {
this.chartTotalPerDay();
this.chartTotalPerHour();
});
}
},
created() {
},
computed: {
axiosParams() {
const params = new URLSearchParams();
if(this.$route.query.range) params.append('range', this.$route.query.range);
return params;
}
},
filters: {
},
directives: {
},
}
</script>
<style lang="scss">
.interactions {
h5 {
text-align: center;
}
}
.chart {
min-height: 550px;
}
</style>
I am currently using
var device = document.getElementById("deviceLeft");
...in my duplicated left/right subcomponents (this is 'left' and I also have 'right').
Should I use a 'ref' for my devices dropdown, so that each instance of the subcomponent is looking at the devices dropdown relative to that instance? Currently if I merge these into one the devices dropdown ID is the same for both instances and therefore I get incorrect ID in one of the instances!
Any helpful pointers would be much appreciated.
Thanks,
K...