Add data using the :data-obj directive after async function finishes - javascript

I'm trying to add some data to the DOM using the :data-obj directive. However, I want to delay this process until an asynchronous function has completed. Unfortunately, every time I attempt this, it only adds the promise. I've tried awaiting the response in the computed method, but still got back a promise and eslint hates it. Any ideas?
<template>
<div
class="test-data"
:data-obj="getData()"
/>
</template>
<script>
export default {
name: 'compName',
components: {
...,
},
props: {
...
},
setup(props) {
/**
* Get data from api
*/
function getTestData() {
return new Promise((resolve) => {
const query = "theThingsINeed"
fetch(query).then((resp) => {
resolve(resp);
});
});
}
const getdata = async () => {
const data = await getTestData();
return JSON.stringify(data);
};
return {
getdata,
};
},
};
</script>

I was able to get it to work by using the mounted method and my function there.
<template>
<div
class="test-data"
:data-obj="getData()"
/>
</template>
...
data() {
return {
data: null,
};
},
mounted() {
this.testData = async () => {
const data = await this.getTestData();
this.data = JSON.stringify(data);
};
this.testData();
},
methods: {
/**
* Get data from api
*/
getTestData() {
return new Promise((resolve) => {
const query = "theThingsINeed"
fetch(query).then((resp) => {
resolve(resp);
return resp;
});
});
},

Related

jest Mocking a jQuery function from a promise

I have a function that calls a jQuery function.
the jQuery function called dataFunc and should return an object.
I want to test the promise, not the dataFunc function.
For that, I want to mock the response that dataFunc should return
I want this row const { data } = await service.auth( buttonData ); data to return
{ access_level: 0 };
How can I do that?
This is my code:
This function with the promise I want to test:
auth(buttonData){
const myPromise = new Promise((resolve, reject) => {
const success = (e, data) => {
resolve({data, error: null});
};
const error = () => {
resolve({data: null, error: 'Error'});
};
jQuery(buttonData).dataFunc({
success,
error,
});
});
return myPromise;
}
This is what I have done in jest so far:
describe('service.test.js', () => {
beforeEach(() => {
global.jQuery = () => {
return {
dataFunc: jest.fn(() => ({access_level: 0})),
};
};
});
afterEach(() => {
jest.clearAllMocks();
});
test('should do something', async () => {
// Arrange
const service = new Service();
const approveButton = document.createElement('button');
// Act
const {data} = await service.auth(buttonData);
console.log(data);
});
});
To fulfill auth function you need either reject or resolve the value.
However, when you mock jQuery method dataFunc to return an explicit value, you override the default behavior and it never calls resolve or reject. Therefore your promise will hang.
You don't necessarily need to mock but provide the original functionality dataFunc carries or provide one that is necessary for the current test.
To fix your example you can pass the argument and call it.
global.jQuery = () => {
return {
dataFunc: ({success, error}) => {
success(jest.fn(), {access_level: 0})
},
};
};

Redux - Asynchronous response from web socket request

I have a websocket interface which I implemented so that I can use to send requests.
The problem is that the response is asynchronous and it initially returns the empty array because retObj is not updated from the callback function that I sent in. How can I make this function so that it will return the populated array when it has been updated.
This is how my Service looks like:
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
interface.sendRequest(function (returnObject) {
//
}).then(d => d)
}
}
}
export default carService()
And this is how my action looks like:
import { GET_CARS } from '../constants'
import carService from '../carService'
export const getCars = () => async (dispatch) => {
try {
const cars = await carService.getCars()
console.log("At cars actions: ", cars) // logs: Array []
dispatch(getCarsSuccess(cars))
} catch (err) {
console.log('Error: ', err)
}
}
const getCarsSuccess = (cars) => ({
type: GET_CARS,
payload: cars
})
You simply have to wrap your callback into promise, since it was not a promise to begin with, which is why you cannot use then or await
import * as interface from '../webSocket'
const carService = () => {
return {
getCars: () => {
return new Promise(resolve => interface.sendRequest(function (returnObject) {
resolve(returnObject.msg)
}));
}
}
}
export default carService()
The problem is, you cant await a function unless it returns a Promise. So, as you can guess, the problem lies in carService.getCars's definition. Try this:
getCars: () => {
return new Promise((resolve, reject) => {
interface.sendRequest(function(returnObject) {
// if theres an error, reject(error)
resolve(returnObject);
})
})
}
Or, if sendRequest os am async function, simply return the return value of sendRequest:
getCars: () => {
return interface.sendRequest()
}

Error when trying to call a constant to a view: the method is not defined

Thanks for the help you have given me...
I'm trying to call a constant on view since it's supposed to return a value. In this case in the method, there is a function that returns me a string. That later I'm going to send him to another sight for his call.
I have a view that I call 2 times because I try to print 2 carousels with different information.
<app-carousel v-if="trendingMovies" :title="$t('home.new_movies')" :view-all-url="MoviesUrl" :data="trendingMovies"></app-carousel>
<app-carousel v-if="trendingSeries" :title="$t('home.new_tv_series')" :view-all-url="SeriesUrl" :data="trendingSeries"></app-carousel>
In the export default I have this:
async asyncData () {
try {
const trendingMovies = await this.treding('movies');
const trendingSeries = await this.treding('series');
return { trendingMovies, trendingSeries };
} catch {
console.log(404);
}
},
methods: {
async treding(media) {
let { data } = await axios.get('/api/v1/get/type/' + media);
return await data.list;
},
}
What I am trying to do is store its respective string in each constant and then pass it to the carousel view.
The problem is that I always get the following error in the <app-carousel> tags.
You can try refactoring the asyncData with the following approach:
async asyncData () {
let trendingMovies = null;
let trendingSeries = null;
try {
trendingMovies = await this.treding('movies');
trendingSeries = await this.treding('series');
} catch {
console.log(404);
}
return { trendingMovies, trendingSeries };
},
This way, it makes sure that asyncData always returns the correct data object.
If it's nuxt, then the answer from the documentation:
Warning: You don't have access to the component instance through this inside asyncData because it is called before initiating the component.
You need to make a method not in Vue. Something like:
async function treding(media) {
const { data } = await axios.get('/api/v1/get/type/' + media);
return data.list;
}
export default {
async asyncData(context) {
try {
const promises = [treding('movies'), treding('series')];
const [trendingMovies, trendingSeries] = await Promise.all(promises);
return { trendingMovies, trendingSeries };
} catch {
console.log(404);
return { trendingMovies: null, trendingSeries: null };
}
}
}
Or you can don't use asyncData
data() {
return {
trendingMovies: null,
trendingSeries: null,
};
},
async created() {
try {
const promises = [this.treding('movies'), this.treding('series')];
const [trendingMovies, trendingSeries] = await Promise.all(promises);
this.trendingMovies = trendingMovies;
this.trendingSeries = trendingSeries;
catch {
console.log(404);
}
},
methods: {
async treding(media) {
const { data } = await axios.get('/api/v1/get/type/' + media);
return data.list;
},
},

Cannot find a way to wait for vue.js async function

I have the following code that works when I uncomment the "return" in fetchData below. How can I set it up so I wait for this.fetchData to finish to fill items? I have tried variants of other promises, async, await, but cannot get it to actually work...
So far I set a this.items inside fetchData but I realize it's absolutely not what I want to actually do. It shows me the first X lines of my call, because of setting it this way, but all the code that is supposed to happen after the fetchData call is bugging, starting with trying to do items.length.
import axios from 'axios';
new Vue({
el: '#app',
data() {
return {
search: '',
totalItems: 0,
items: [],
loading: true,
pagination: {},
headers: [ //some headers
],
items: []
}
},
watch: {
pagination: {
handler () {
this.getDataFromApi()
.then(data => {
this.items = data.items
this.totalItems = data.total
})
},
deep: true
}
},
mounted () {
this.getDataFromApi()
.then(data => {
this.items = data.items
this.totalItems = data.total
})
},
methods: {
fetchData() {
axios.get('/api/v1/translations').then(response => {
//console.log(response.data)
this.items = response.data.data
})
//the return below is working 100%:
//return [{id:1,key:1,lg:'en',text:'woopie'}];
},
getDataFromApi () {
this.loading = true
return new Promise((resolve, reject) => {
const { sortBy, descending, page, rowsPerPage } = this.pagination
let items = this.fetchData()
const total = items.length
//some code to setup pagination and sort
setTimeout(() => {
this.loading = false
resolve({
items,
total
})
}, 1000)
})
}
},
})
Updated answer:
Apologies, in my original answer below I completely missed this:
So far I set a this.items inside fetchData but I realize it's absolutely not what I want to actually do.
My answer assumed you did want this.items. Sorry about that.
To just get the info from fetchData without using this.items, use the then handler on the promise from axios.get to return a new promise (remember, then and catch return promises) that resolves with response.data.data:
fetchData() {
// vvvvvv------------------------
return axios.get('/api/v1/translations').then(response => {
return response.data.data;
// ^^^^^^^^^^^^^^^^^^^^^^^^^
})
},
This can be written more succinctly:
fetchData() {
return axios.get('/api/v1/translations').then(response => response.data.data);
},
Then, in getDataFromApi, use a then handler on that promise:
getDataFromApi () {
this.loading = true
return this.fetchData().then(items => {
// You might use this at some stage: const { sortBy, descending, page, rowsPerPage } = this.pagination
this.loading = false;
return { items, total: items.length };
});
}
Note that it's important that the consumer of getDataFromApi handles promise rejection (e.g., uses a catch handler on it). If you don't want the consumer to do that, don't return the promise, and use a catch within getDataFromApi instead.
Original answer:
You return the promise from axios.get:
fetchData() {
// vvvvvv------------------------
return axios.get('/api/v1/translations').then(response => {
//console.log(response.data)
this.items = response.data.data
})
},
...and then use it rather than creating a new one in getFromApi:
getDataFromApi () {
this.loading = true
return this.fetchData().then(() => {
// You might use this at some stage: const { sortBy, descending, page, rowsPerPage } = this.pagination
this.loading = false;
return { items: this.items, total: this.items.length };
});
}
Remember that then (and catch) create new promises; there's no reason for new Promise if you already have a promise to work with. More on that part: What is the explicit promise construction antipattern and how do I avoid it?
(Note that I'm assuming you want this.items on the instance for some reason. If you didn't, you'd just want to use response.data as the return value of your axios.get...then callback and use it in getDataFromApi.)

How to return value from callback in Graphql resolve function?

How do I return the value from callback function and pass it to resolve function in Graphql?
Here's the dummy code to show the concept:
This function runs the sql query:
function runQuery(query, cb){
....
var value = "Something";
cb(null, value);
}
This takes the value from callback function pass it to resolve function in graphql:
function getTitle() {
return runQuery("...", function(err, value){
return value;
});
}
Graphql schema:
var SampleType = new GraphQLObjectType({
name: 'Sample',
fields: () => ({
title: { type: GraphQLString },
}),
});
query: new GraphQLObjectType({
name: 'Query',
fields: () => ({
sample: {
type: SampleType,
resolve: () => getTitle(),
},
}),
}),
You can make use of promises and async to accomplish this.
async function getTitle() {
const queryResult = await runQuery("...");
// ...
// Do post-query stuff here that you currently have in your callback
// ...
return queryResult
}
async function runQuery() {
const value = 'something';
// ...
return value;
}
Node fully supports async/await as of 7.10.0. Use TypeScript or Babel if you're in the browser or are locked into a lower version of node.
Basically you can create a promise around that runQuery so that you can use async await when using the query data
const getTitle = () => {
return new Promise((resolve, reject) => {
runQuery("...", (error, response) => !error
? resolve(response)
: reject(error))
})
}
const asyncFunction = async () => {
const data = await getTitle()
.then((response) => {
// handle response and return what you want
return response.data
})
.catch((error) => {
// handle error, log it, etc, in whatever way you want
console.log(error.message)
return null
})
if(data) { // data is valid
// do what you want with the valid data (no error)
} else { // there was an error
// handle if there is an error
}
}
asyncFunction()

Categories

Resources