How to run method inside a response function using Vue.js - vue.js

I'm trying to run a method when I get success response from API, but the method dont run. I made a quick example here to show.
The test() function should be executed after i get the response, since its calling another API endpoint. Here is the vue.js code.
var app = new Vue({
el: "#contents",
data: {
id: null,
details: [],
},
methods: {
fetchProductDetails: function(){
let vue = this;
axios.post("/api/get-details", {
id : id
})
.then(function (response) {
vue.details = response.data;
this.test();
})
.catch(function (error) {});
},
test: function () {
console.log(app.details);
}
},
mounted: function(){
this.fetchProductDetails();
},
});

You should run vue.test() instead of this.test(), just like you use vue.details = response.data instead of this.details = response.data.
When using an unnamed function in .then(), this no longer refers to your vue application, but to the unnamed function. You could use ES6 arrow function syntax in order to avoid having to set this to a specific variable, as arrow functions use their parent's scope for this instead of setting this to refer to themselves:
axios.post("/api/get-details", { id: this.id })
.then(response => {
this.details = response.data;
this.test();
})
.catch(error => { console.log(error)});
Arrow functions (and ES6 in general) are not supported by IE11 however. so you'd need to use Babel to compile it back to something ES5 JavaScript if you need to support older browsers.

Related

Use tableTop.js to return an array that can be used in Vue Components

I am attempting to build an array of objects from a spreadsheet using tableTop.js that can be passed into other functions and vue components. I have been unsuccessful in returning anything I can actually use. I found this post that got me close to what I am after however what it is returning is an array of arrays of objects with two undefined array items beginning with [ob: Observer]
If I log out data in the getLibrary() function I can see the correct array how I need to receive it in my component.
If I don't push the data into the gData array in libraryData I receive undefined in vue from the function. I have attempted promises, normal functions etc. but nothing seems to work. Very appreciative of any help anyone can provide thanks.
Image 1 is what I am logging out in library data that I am trying to receive in vue.
Image 2 is what I am getting in vue
libraryData.js
// let gData = []
export default async function () {
let spreadSheet = 'url'
Tabletop.init({
key: spreadSheet,
callback: (data, tabletop) => { return getLibraryData(data,m tabletop) },
simpleSheet: true
})
}
export function getLibraryData(data, tabletop) {
// gData.push(data);
///gData = data
log(data)
// I just want to return the data here to be used in vue
return data;
}
index.js
import Vue from 'vue'
import libraryData from './partials/libraryData.js'
// Too be added into a vue-lodaer?
new Vue({
el: '#vhsLibrary',
router,
template: '<vhsLibrary/>',
})
window.addEventListener('DOMContentLoaded', () => {
libraryData()
})
vue_component.vue
<script>
import { getLibraryData } from '../../js/partials/library_data';
export default {
data: () => {
return {
gData: null
}
},
mounted () {
this.gData = getLibraryData()
log('Get Library', getLibraryData())
}
}
</script>
There's a few issues here:
You use async, but you never await. In your case, we want to await the resolution or rejection of a Promise:
export default async function () {
return await new Promise((resolve, reject) => {
const spreadSheet = 'url'
Tabletop.init({
key: spreadSheet,
callback: (data, tabletop) => { resolve({data, tabletop}) },
simpleSheet: true
})
})
}
There's no reason for the additional function because it has no gains. Let's look at Vue now.
First, your gData variable is initialized as null as opposed to []. Let's change that:
data () {
return {
gData: []
}
},
Next, let's update our mounted method. We can use the same async/await pattern here:
async mounted () {
const { data } = await getLibraryData()
this.gData = data
}
And now you can v-for="(row, index) in gData" to iterate it.
Here's a codepen for you, too

How Can I pass params with an API client to vue-head?

I am passing params from my API to vue-head but every time I do that it send me undefined in the head this is the code:
export default {
data: () => ({
errors: [],
programs: [],
}),
methods: {
getProgram() {
this.api.http.get(`videos/program/${this.programSlug}`)
.then(response => {
this.programs = response.data
})
.catch(error => {
this.errors = error
});
}
},
head: {
title: function() {
return {
inner: this.programs.name,
separator: '|',
complement: 'Canal 10'
};
}
}
}
any idea what I am doing wrong with my code??
First verify you are fetching the information correctly. Use console log and go to network tab and verify you are fetching the data correct, you might have to comment out vue-head. But what I think is that the problem might be due to vue-head rendering before the api call finishes then no data is being passed.
If you are using vue-router this can be easily solved with beforeRouteEnter() hook. But if not! apparently vue-head has an event that you can emit to update the component after render.
I haven't tried this but it should work. you can add the function below to your methods and call it after the promise is resolved i.e in the then closure.
methods: {
getProgram() {
this.api.http.get(`videos/program/${this.programSlug}`)
.then(response => {
this.programs = response.data
this.$emit('updateHead')
})
.catch(error => {
this.errors = error
});
}
}

Vue.js Creating Reusable Functions

My code is horrible. I'm new on this Vue.js. As you can see I have two AJAX call that needs to become a function. How I'm going to do it. I can't use for example
var app = this
this.allAdmissions('http://localhost/school/api/admissions', app.admission)
and then in my methods I will just
allAdmissions: _.debounce( function(url, value){
axios.get(url)
.then( function(response ){
value = response.data.admissions
})
.catch( function(error){
console.log(error)
})
}),
It doesn't work. I need to create a function to combine this two.
var app = new Vue({
el: '#app',
data: {
value: '',
admissions: [],
schoolyear: []
},
created: function(){
this.allAdmissions('http://localhost/school/api/admissions')
this.allSchoolYear('http://localhost/school/api/schoolyear')
},
methods: {
allAdmissions: _.debounce( function(url){
var app = this
axios.get(url)
.then( function(response ){
app.admissions = response.data.admissions
})
.catch( function(error){
console.log(error)
})
}),
allSchoolYear: _.debounce( function(url){
var app = this
axios.get(url)
.then( function(response ){
app.schoolyear = response.data.schoolYear
})
.catch( function(error){
console.log(error)
})
})
}
})
I see that you are using promises for the ajax request which is a great step forward. I would suggest creating a separate js object with a single entrypoint (public) method to consolidate the logic of making both requests. this single object would return a composed promise that is resolved when both requests are fulfilled. take a look at Promise.All for this. then you would call your custom method from the ready hook inside the vue instance. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all.
what this will get you is code that is easier to reason about and debug for typos or any other flow mistake you may have made.
Also another suggestion is that you use arrow functions for your callbacks like so.
.then((response) => {
this.admissions = response.data.admissions
})
what this will allow you to do is skip the
var app = this
binding so that 'this' retains the reference to your current vue instance.

Unit testing HTTP request with Vue, Axios, and Mocha

I'm really struggling trying to test a request in VueJS using Mocha/Chai-Sinon, with Axios as the request library and having tried a mixture of Moxios and axios-mock-adaptor. The below examples are with the latter.
What I'm trying to do is make a request when the component is created, which is simple enough.
But the tests either complain about the results variable being undefined or an async timout.
Am I doing it right by assigning the variable of the getData() function? Or should Ireturn` the values? Any help would be appreciated.
Component
// Third-party imports
import axios from 'axios'
// Component imports
import VideoCard from './components/VideoCard'
export default {
name: 'app',
components: {
VideoCard
},
data () {
return {
API: '/static/data.json',
results: null
}
},
created () {
this.getData()
},
methods: {
getData: function () {
// I've even tried return instead of assigning to a variable
this.results = axios.get(this.API)
.then(function (response) {
console.log('then()')
return response.data.data
})
.catch(function (error) {
console.log(error)
return error
})
}
}
}
Test
import Vue from 'vue'
import App from 'src/App'
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
let mock = new MockAdapter(axios)
describe('try and load some data from somewhere', () => {
it('should update the results variable with results', (done) => {
console.log('test top')
mock.onGet('/static/data.json').reply(200, {
data: {
data: [
{ id: 1, name: 'Mexican keyboard cat' },
{ id: 2, name: 'Will it blend?' }
]
}
})
const VM = new Vue(App).$mount
setTimeout(() => {
expect(VM.results).to.be.null
done()
}, 1000)
})
})
I am not sure about moxios mock adaptor, but I had a similar struggle. I ended up using axios, and moxios, with the vue-webpack template. My goal was to fake retreiving some blog posts, and assert they were assigned to a this.posts variable.
Your getData() method should return the axios promise like you said you tried - that way, we have some way to tell the test method the promise finished. Otherwise it will just keep going.
Then inside the success callback of getData(), you can assign your data. So it will look like
return axios.get('url').then((response) {
this.results = response
})
Now in your test something like
it('returns the api call', (done) => {
const vm = Vue.extend(VideoCard)
const videoCard = new vm()
videoCard.getData().then(() => {
// expect, assert, whatever
}).then(done, done)
)}
note the use of done(). That is just a guide, you will have to modify it depending on what you are doing exactly. Let me know if you need some more details. I recommend using moxios to mock axios calls.
Here is a good article about testing api calls that helped me.
https://wietse.loves.engineering/testing-promises-with-mocha-90df8b7d2e35#.yzcfju3qv
So massive kudos to xenetics post above, who helped in pointing me in the right direction.
In short, I was trying to access the data incorrectly, when I should have been using the $data property
I also dropped axios-mock-adaptor and went back to using moxios.
I did indeed have to return the promise in my component, like so;
getData: function () {
let self = this
return axios.get(this.API)
.then(function (response) {
self.results = response.data.data
})
.catch(function (error) {
self.results = error
})
}
(Using let self = this got around the axios scope "problem")
Then to test this, all I had to do was stub the request (after doing the moxios.install() and moxios.uninstall for the beforeEach() and afterEach() respectively.
it('should make the request and update the results variable', (done) => {
moxios.stubRequest('./static/data.json', {
status: 200,
responseText: {
data: [
{ id: 1, name: 'Mexican keyboard cat' },
{ id: 2, name: 'Will it blend?' }
]
}
})
const VM = new Vue(App)
expect(VM.$data.results).to.be.null
VM.getData().then(() => {
expect(VM.$data.results).to.be.an('array')
expect(VM.$data.results).to.have.length(2)
}).then(done, done)
})

How do I use the vue-resource plugin to delete an object on demand?

Using the vue-resource plugin, it has an example like this:
new Vue({
ready: function() {
var resource = this.$resource('someItem{/id}');
// delete item
resource.delete({id: 1}).then(function (response) {
// handle success
}, function (response) {
// handle error
});
}
})
I am somewhat confused that this is under the ready property since it suggests it runs and deletes something as soon as the component loads. How do I actually use that resource.delete function from clicking an element? For example, I have this:
<div #click="deleteReward(reward)" class="glyphicon glyphicon-trash pull-right"></div>
which is currently calling this:
deleteReward(reward) {
this.rewards.$remove(reward);
this.$http.delete('api/reward/' + reward.id).then(function (response) {
console.log('deleted ' + reward.name);
});
},
But my understanding is that I should somehow be able to call resource.delete instead and not have to specify the URL and method explicitly. Am I wrong in that assumption?
What I want is to do something like #click="reward.delete()" and have it know to call the resource.delete automatically and have resource.delete accept the reward object as a parameter.
You could try something like this:
new Vue({
data: {
resource: null
},
ready: function() {
this.resource = this.$resource('api/reward{/id}')
},
methods: {
deleteReward(reward) {
this.resource.delete({id:reward.id}).then(function (response) {
console.log('deleted ' + reward.name)
})
}
}
})
And then:
<div #click="deleteReward(reward)"></div>