How to insert vue.js computed data into form data? - javascript

<template>
<form #submit.prevent="uploadMeasurement(measure)">
<input v-model="measure.length">
<input v-model="measure.width">
</form>
</template>
<script>
export default {
data() {
return {
measure: this.createFreshMeasure(),
};
},
computed: {
sqftTotal: function() {
return this.length * this.width;
}
},
methods: {
uploadMeasurement(measure) {
MeasurementService.uploadMeasurement(measure)
.then(...);
this.measure = this.createFreshMeasure();
})
.catch(error => {
this.error = error.response.data.error;
});
},
createFreshMeasure() {
return {
length: this.length,
width: this.width,
sqftTotal: this.sqftTotal
};
}
</script>
On submit, I'd like to calculate a square footage value using the values placed into the length and width inputs and send all three into the Mongo database.
The database is storing a value for sqftTotal when I send a hard-coded value directly over Postman, so it's capable of doing it, but this Vue form isn't accomplishing that task.

methods: {
uploadMeasurement() {
let measure = this.measure;
measure.sqftTotal = this.sqftTotal;
MeasurementService.uploadMeasurement(measure)
...
Got it, thanks to everyone for your input. Had to remove the argument from the method and declare it before the service call.

The easiest way to accomplish this would be something like this.. I have commented different options within the code to help explain things..
new Vue({
el: "#root",
template: `
<div>
<form ref="form">
<!--<input v-model="measure.length">
<input v-model="measure.width">-->
<input v-model="length">
<input v-model="width">
</form>
<button #click.prevent="uploadMeasurement">Submit</button>
</div>
`,
data: {
//measure: ""
length: "",
width: "",
},
computed: {
sqftTotal: function() {
//return this.measure.length * this.measure.width;
return this.length * this.width;
}
},
methods: {
uploadMeasurement() {
/** This is where you would POST the data **/
// You can either submit the form:
// -NOTE: '...$refs.form...' below must match the ref
// you assign to the <form ref="form"> element.
// this.$refs.form.$el.submit();
// ~ OR ~
// manually POST via fetch, etc:
// fetch('/url/to/post/to', {
// method: 'POST',
// body: JSON.stringify({
// length: this.measure.length,
// width: this.measure.width,
// sqftTotal: this.sqftTotal
// })
// })
alert(JSON.stringify({
//length: this.measure.length,
//width: this.measure.width,
length: this.length,
width: this.width,
sqftTotal: this.sqftTotal
}));
},
createFreshMeasure() {
this.length = 10;
this.width = 5;
//return {
// length: 10,
// width: 5
//};
}
},
created() {
this.createFreshMeasure();
//this.measure = this.createFreshMeasure();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="root"></div>

I recommend cleaning up your code like below, as Vue often has issues when using object properties as a model like that
<template>
<form #submit.prevent="uploadMeasurement()">
<input v-model="length">
<input v-model="width">
</form>
</template>
<script>
export default {
data() {
return {
length: null,
width: null,
};
},
computed: {
sqftTotal: function() {
return this.length * this.width;
}
},
methods: {
uploadMeasurement() {
MeasurementService.uploadMeasurement({
length: this.length,
width: this.width,
sqftTotal: this.sqftTotal
})
.then(() => {
console.log('save success!');
})
.catch(error => {
this.error = error.response.data.error;
});
},
}
</script>

Related

How to detect changes in property values in object in vue?

On top of How to watch only after the initial load from API in VueJS?, I wanted to detect any changes in values of the properties in the json object.
Initially the user object is
user: {
userId: 0,
id: 0,
title: "",
completed: false,
},
I have two input fields,
<input type="text" v-model="user.userId" /> <br />
<input type="text" v-model="user.title" /> <br />
and a button <button :disabled="isLoaded">Update</button>
If none of the input values changed, the button should be still disabled. Example, if the userId is changed to 1, the button should be enabled but if the value is changed back to 0, the button should be disabled. I referred Vue js compare 2 object and remove differences in watcher and I tried following but failed.
<template>
<div id="app">
<div v-if="!isFetching">
<input type="text" v-model="user.userId" /> <br />
<br />
<input type="text" v-model="user.title" /> <br />
<br />
<button :disabled="isLoaded">Update</button>
</div>
<div v-else>Loading...</div>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
user: {
userId: 0,
id: 0,
title: "",
completed: false,
},
isFetching: false,
isLoaded: true,
};
},
watch: {
user: {
handler(oldVal, newVal) {
this.checkObject(oldVal, newVal);
},
deep: true,
},
},
methods: {
checkObject: (obj1, obj2) => {
const isEqual = (...objects) =>
objects.every(
(obj) => JSON.stringify(obj) === JSON.stringify(objects[0])
);
console.log(obj1, obj2);
console.log(isEqual(obj1, obj2));
},
},
created() {
this.isFetching = true;
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => {
this.user = json;
this.isLoaded = true;
})
.finally(() => (this.isFetching = false));
},
};
</script>
Here's a live demo: https://codesandbox.io/embed/friendly-hopper-ynhxc?fontsize=14&hidenavigation=1&theme=dark
Here is one way you could solve this. So below I'm storing two user objects, one is my base line comparison compareUser, and the other is the user that is under edit. When something changes which the deep watch on user will notify me about, I use a utility function like isEqual from lodash to perform a semantic comparison of the base line object and the current user object, and see if there are any differences.
If I want to update my base line that I'm comparing to, then I update the compareUser from the current user by cloning it.
You can of course replace things like isEqual and cloneDeep by rolling your own to avoid the extra library if that's an issue.
<script>
import { isEqual, cloneDeep } from "lodash";
const createDefault = function () {
return {
userId: 0,
id: 0,
title: "",
completed: false,
};
};
export default {
name: "App",
data() {
return {
compareUser: createDefault(),
user: createDefault(),
isLoaded: false,
isDifferent: false,
};
},
watch: {
user: {
handler() {
this.isDifferent = !isEqual(this.user, this.compareUser);
},
deep: true,
},
},
methods: {
setCompareUser(user) {
this.compareUser = cloneDeep(user);
this.isDifferent = false;
},
},
async created() {
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
const user = await response.json();
this.user = user;
this.setCompareUser(user);
this.isLoaded = true;
},
};
</script>
Demo:
https://codesandbox.io/s/modern-tdd-yg6c1

How to put variables in Vue

i'm learning vue and i have small problem.
I've created code, which receive some informations from webserwer (via socket) and this code works fine.
But i would like to do very simple thing - display info as variable in HTML and i have problem with it.
My code is:
export default {
components: {
BCard,
BButton,
},
data() {
return {
connection: null,
json: {
cmd: 'start',
width: 1024,
height: 800,
url: 'https://www.google.com',
token: '',
service_id: 1,
thumbnail_width: 100,
thumbnail_height: 100,
},
}
},
created() {
console.log('Starting Connection to WebSocket')
this.connection = new WebSocket('ws://127.0.0.1:8080/')
// this.connection = new WebSocket('ws://echo.websocket.org')
this.connection.onopen = function (event) {
console.log(event)
console.log('Success')
}
this.connection.onmessage = webSocketOnMSG
},
methods: {
sendMessage(message) {
console.log(this.connection)
console.log(message)
console.log(JSON.stringify(message))
const dat = this.connection.send(JSON.stringify(message))
console.log('TT', dat)
},
drawItem() {
const img = document.createElement('img')
const canvas = document.getElementById('canvasId')
img.src = 'http://image...'
img.onload = function (a) {
const h = a.target.height
const w = a.target.width
const c = canvas.getContext('2d')
canvas.width = w
canvas.height = h
c.drawImage(img, 0, 0)
document.getElementById('draw-image-test').appendChild(canvas)
}
},
webSocketOnMSG(msg) {
console.log(msg)
},
},
}
and i would like to add code like this:
data: {
xposition: 'xpos',
yposition: 'ypos'
}
but when i'm adding it to created earlier data() i have error, so this doesn't work:
data() {
xposition: 'xpos',
yposition: 'ypos',
return {...}
}
where should i add code to replace variables {{xposition}} and {{yposition}} in HMTL?
You must put your new variables inside your returned object in the data function, alongside your 'json' variable. You need to declare them first as empty values, and then add the proper values in your API call callback
data() {
return {
xposition: '',
yposition: '',
...
}
}
webSocketOnMSG(msg) {
// this will change your component's xposition property
this.xposition = msg.propertyYouWantToAccess
},

VueJS data not reactive mounted

I have an API which returns all the currency rate, i used a function getRate() on mounted but rate['usd'] is undefined, if i call the function again on that page it returns the actual data, i tried beforeCreated beforeMounted but they are not working, how to make the data reactive on load or am i doing something wrong?
<template>
<span v-text="rate['usd']"></span>
</template>
<script>
data() {
return {
rate: null
}
},
methods: {
getRate() {
this.$vs.loading()
this.$http.post('wallet/rate' ,[])
.then(response => {
for(let key in response.data.data.data){
this.rate[response.data.data.data[key].name] = response.data.data.data[key].value
}
this.$vs.loading.close()
})
.catch(error => {
this.$vs.loading.close()
})
},
},
mounted() {
this.getRate()
}
</script>
Does this work?
<template>
<span v-text="rate.usd"></span>
</template>
<script>
data() {
return {
rate: null
}
},
methods: {
getRate() {
const rate = {}
this.$vs.loading()
this.$http.post('wallet/rate' ,[])
.then(response => {
for(let key in response.data.data.data){
rate[response.data.data.data[key].name] = response.data.data.data[key].value
}
this.$vs.loading.close()
this.rate = rate
})
.catch(error => {
this.$vs.loading.close()
})
},
},
mounted() {
this.getRage()
}
</script>

Passing variables from within VueJS components

I am new to VueJs and Laravel. I am trying to build a chat app following this tutorial, and my problem is that I am not able to pass a CONTACT variable (object type) from ChatApp.vue to Conversation.vue. The console gives this error
[Vue warn]: Invalid prop: type check failed for prop "contact". Expected Object, got Null
found in ---> <MessageFeed> at resources/js/officer/components/MessageFeed.vue
<Conversation> at resources/js/officer/components/Conversation.vue
<ChatApp> at resources/js/officer/components/ChatApp.vue
<Root>
first the variable has to pass through ChatApp to Conversation and then to MessageFeed. I tried printing it on console, in ChatApp.vue, it printed the variable's property i.e. name but when I try to print it in Conversation.vue, it says it is null and also gives the above error.
please see the code below:
ChatApp.vue
<template>
<div class="chat-app">
<Conversation :contact="selectedContact" :messages="messages" #new="saveNewMessage"/>
<ContactsList :contacts="contacts" #selected="startConversationWith"/>
</div>
</template>
<script>
import Conversation from "./Conversation";
import ContactsList from "./ContactsList";
const axios = require('axios');
export default {
props: {
user: {
type: Object,
required: true
}
},
data() {
return {
selectedContact: null,
messages: [],
contacts: []
}
},
mounted() {
Echo.private(`messages.${this.user.id}`)
.listen('NewMessage', (e) => {
this.handleIncoming(e.message);
});
console.log(this.user);
axios.get('/officer/contacts')
.then((response) => {
this.contacts = response.data;
console.log("chatapp1: " + this.contacts[0].name);
})
.catch((error) => {
// handle error
console.log(error);
});
},
methods: {
startConversationWith(contact) {
axios.get(`/officer/conversation/${contact.id}`)
.then((response) => {
this.message = response.data;
this.selectedContact = contact;
console.log("chatapp: " + this.selectedContact.name);
});
},
saveNewMessage(message) {
this.messages.push(message);
},
handleIncoming(message) {
if (this.selectedContact && message.from === this.selectedContact.id) {
this.saveNewMessage(message);
return;
}
this.updateUnreadCount(message.from_contact, false);
},
updateUnreadCount(contact, reset) {
this.contacts = this.contacts.map((single) => {
if (single.id !== contact.id) {
return single;
}
if (reset)
single.unread = 0;
else
single.unread += 1;
return single;
})
}
},
components: {Conversation, ContactsList}
}
Conversation.vue
<template>
<div class="conversation">
<h1>{{ contact ? contact.name : 'Select a Contact' }}</h1>
<MessagesFeed :contact="contact" :messages="messages"/>
<MessageComposer #send="sendMessage"/>
</div>
</template>
<script>
import MessagesFeed from './MessageFeed';
import MessageComposer from './MessageComposer';
export default {
props: {
contact: {
type: Object,
default: null
},
messages: {
type: Array,
default: []
}
},
mounted() {
console.log("conversation: "+this.contact);
},
methods: {
sendMessage(text) {
if (!this.contact) {
return;
}
console.log(text);
axios.post('/conversation/send', {
contact_id: this.contact.id,
text: text
}).then((response) => {
this.$emit('new', response.data);
})
}
},
components: {MessagesFeed, MessageComposer}
}
MessageFeed.vue
<template>
<div class="feed" ref="feed">
<ul v-if="contact">
<li v-for="message in messages" :class="`message${message.to == contact.id ? 'sent' : 'received'}`"
:key="message.id">
<div class="text">
{{message.text}}
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
contact: {
type: Object,
required: true
},
messages: {
type: Array,
required: true
}
},
methods: {
scrollToBottom() {
setTimeout(() => {
this.$refs.feed.scrollTop = this.$refs.feed.scrollHeight - this.$refs.feed.clientHeight;
}, 1);
}
},
watch: {
contact(contact) {
this.scrollToBottom();
},
messages(messages) {
this.scrollToBottom();
}
}
}
please, let me know what am I missing here.
Because you konfigurated it to be an Object but you declared it on the initial state as null
selectedContact: null
That should be an object
It should look something like this i guess:
selectedContact: {
id:0
}
I fixed the issue.
This was due to axios.get() response thing. It was fetching data from database after a couple of minutes and the Conversation.vue and ContactList.vue were mounting immediately so they were not getting data.
With the help of my friend, it was fixed by loading the component only when axios have got all the contactlist from database and the very first contact have been passed to the Conversation.vue component i.e using v-if property of component.

Not getting any results back from Fuse.js with Vue

So I'm fairly new to Vue and I'm trying to make a customer list search work with Fuse.js.
I do get the array of customers back and it's being assigned to customer_search. my keys are populated properly and the only issue is that results doesn't return anything. I'm wondering if I need to structure my customer array differently or am I missing something else altogether?
Any help would be appreciated.
Here is my code:
<template>
<div>
<div class="container">
<h1>Search</h1>
<input type="text" class="input-search" value="" v-model="query">
<p v-html="results"></p>
<p v-for="info in data" >{{info}}</p>
</div>
</div>
</template>
<script>
import Fuse from 'fuse.js'
import $ from 'jquery'
import PageService from '../../common/services/PageService'
const Search = {
data(){
return {
data: {},
fuse: {},
results: {},
query: '',
options: {
keys: [
'id',
'name',
'company',
],
minMatchCharLength: 3,
shouldSort: true,
threshold: 0.5
},
}
},
methods:{
runQuery(query){
if(query.length >= 3)
this.results = this.fuse.search(query)
},
},
computed:{
customers: function(){
return this.data
},
customer_search: function(){
return Object.values(this.data)
},
},
watch: {
query: function(){
this.runQuery(this.query)
}
},
created(){
this.fuse = new Fuse(this.customer_search, this.options)
if(this.$store.state.search != ''){
this.query = this.$store.state.search
}
PageService.getSearchObject().then((response)=>{
this.data = response.data
}).catch((err)=>{
console.log('Error')
});
},
}
export default Search
</script>
I think your runQuery method is created before your this.fuse get created so the this.fuse inside your runQuery method is not up-to-date.
Maybe try:
methods:{
runQuery(query){
if(query.length >= 3)
this.results = new Fuse(this.customer_search, this.options).search(query)
},
},

Categories