Change component's state on success from remote jquery ajax call - javascript

I can do this with axios, as far it returns promise which I can resolve later. Example with axios:
in api.js file I have this:
import axios from 'axios';
export default {
listUsers() {
return axios.get(`api/users`);
},
addUser(data) {
return axios.post(`api/adduser`, data);
}
}
And then I use this api object in my later code and it works good:
file usersActions.js:
import api from '../api';
const UsersActions = {
...
loadUsers() {
api.listUsers()
.then(({ data }) =>
AppDispatcher.dispatch({
type: Constants.LOAD_USERS_SUCCESS,
users: data
})
)
...
}
};
But can I do something like this with jquery.ajax?

Related

Vue.js import Axios within method then use it

I have got a component which might not request an ajax call if some data has been passed into it. However if the data hasn't been passed in I need to fetch it, so I want to import Axios then, save importing it for no reason.
How can I wait for the script to be imported before attempting to use it, as the below doesn't work:
export default {
props: {
vehicleId: {
type: Number|String,
required: true,
default: () => null
},
settings: {
type: Object,
default: () => null
}
},
beforeCreate() {
if (!this.settings) {
const Axios = () => import('../../../axiosConfig');
Axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
}
},
Dynamic import return a Promise, so you must to use then function.
Try something like that:
<script>
export default {
beforeCreate() {
if (!this.settings) {
import('../../../axiosConfig').then(axios => {
axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
});
}
},
};
</script>
Avoid the approach with async/await because the lifecycle functions don't support asynchronous in Vue.js.
You're almost there, import() is async, so just do:
// or use .then if you're not in an async function
const Axios = (await import('../../../axiosConfig')).default
Axios.get('/api/v1/media-slider-settings').then(response => {
this.settings = response.data;
});
and notice that import() returns the module, so you need to get the .default property if you need the default export (like in your case) or just call .someExportedName for importing a named export (i.e. non-default export from the module)

Vuex: Axios GET request returns undefined inside component

I am using Vuex/Axios to make GET requests to an API. When a component mounts, I am dispatching an action to the Vuex store and making the Axios GET request. In the Vuex action, the Axios GET request returns the response as expected but the response inside the component is returning undefined. What am I doing wrong?
axios/index.js
import axios from 'axios';
const API_URL = 'http://localhost:3000/api/v1/';
const plainAxiosInstance = axios.create({
baseURL: API_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
}
});
export { plainAxiosInstance };
Vuex module: store/modules/character.js. In this file response logs the correct response. The fetchCharacters event gets triggered in a component.
import { plainAxiosInstance } from '#/axios';
const characterModule = {
namespaced: true,
state: {
characters: []
},
mutations: {
SET_CHARACTERS(state, characters) {
state.characters = characters;
}
},
actions: {
async fetchCharacters({ commit }) {
await plainAxiosInstance
.get('/characters')
.then(response => {
let characters = response.data;
commit('SET_CHARACTERS', characters);
console.log(characters); <-- Logs the expected response data
return characters;
})
.catch(error => console.log('Failed to fetch characters', error));
}
},
getters: {}
};
export default characterModule;
I am then dispatching the Vuex action inside of a Vue component on mount:
<script>
import { mapState, mapActions } from 'vuex';
export default {
mounted() {
this.fetchCharacters()
.then(response => {
console.log('response', response);
// response is logging undefined here <----
})
.catch(error => {
console.log(error);
});
},
computed: mapState(['character']),
methods: mapActions('character', ['fetchCharacters'])
};
</script>
The console.log in modules/character.js logs the data as expected and then the response inside of the component logs undefined. I made sure to return the variable characters in the Vuex module. And I also made the Vuex action fetchCharacters async. So why is the response in the component returning undefined?
Thanks if you can help.
Change this:
async fetchCharacters({ commit }) {
await plainAxiosInstance
to this:
fetchCharacters({ commit }) {
return plainAxiosInstance
You can keep the async if you want but it won't make any difference.
In its present form the action will implicitly return a promise and that promise won't resolve until the request is complete. However there's nothing to tell it to resolve that promise to the desired value.
Instead of waiting for the promise inside the action you can just return that promise instead. Externally that won't make any difference as you'll just get back a promise either way but crucially that promise will resolve to the correct value once the request is complete.

Calling Axios API hit in a service file from Reducer Giving Error

In react I am trying to make a "Rest API call via Axios". I made a service file and then when reducer is trying to console.log the output of the service. it is giving error. Please help.
someReducer.js
import getItemsAPI from '../../services/service1';
...
case "GET_ITEM_LIST": {
let data = getItemsAPI.getItems();
console.log(data);
return {
...state,
items: data
}
}
service1.js
class getItemsAPI {
getItems() {
return this.axiosInstance
.get('https://jsonplaceholder.typicode.com/users/')
.then((response) => response.data);
}
}
export default getItemsAPI;
Error:
If you use a class, you must use the new keyword in order to create an instance. Then, you can use its methods:
import getItemsAPI from '../../services/service1';
const getItemsInstance = new getItemsApi();
...
case "GET_ITEM_LIST": {
let data = getItemsInstance.getItems();
console.log(data);
return {
...state,
items: data
}
}
You don't need to use a class in order to export a function. You can export the function itself (in this case, inside an object):
const getItemsAPI = {
getItems: () => {
return axiosInstance
.get('https://jsonplaceholder.typicode.com/users/')
.then((response) => response.data);
}
}
export default getItemsAPI;
If you use the code above, you don't need to create an instance. You can simply use the object (like your doing in the OP).
Just a note as well. getItems will return a Promise. In order to get data, you must await or resolve the Promise before reducing.

Rerun function on error and pass result up

I have separated my api call into three layers. The component, the repository, and the apihelper. I want the logic for refresh_tokens to be in apihelper.js. When I do this it seems like the apihelper runs again after getting the 401 response status but it never passes the data back up to the component. I know I could put the logic to rerun it in the component but that seems like it will end up being a lot of duplicate code as I add more calls. I feel like it's probably caused by my shallow understanding of javascript promises but I'm a javascript beginner.
Component
<script>
import breweryrepository from '#/repository/breweryrepository.js'
export default {
mounted() {
this._getTotalBreweries();
},
methods: {
_getTotalBreweries() {
breweryrepository.getTotalBreweries()
.then((response) => {
if(response.data)
{
this.totalNumberOfBreweries = response.data.totalBreweries;
}
})
}
},
data () {
return {
totalNumberOfBreweries: ''
}
}
}
</script>
Repository
import apihelper from '#/helpers/ApiHelper.js';
export default {
getTotalBreweries() {
return new Promise(function(resolve, reject) {
resolve(apihelper.apiCall('/brewery/totalnumber'));
});
}
}
Apihelper
import axios from 'axios';
var querystring = require('querystring');
import { store } from '../store/store.js';
import auth from '#/auth/auth.js'
export default {
apiCall(url) {
return axios.get(store.state.baseUrl + url, { 'headers': auth.getAuthHeader() })
.catch((error) => {
if(error.response.status == 401)
{
console.log("401 error, running refresh and apicall again");
auth.refreshToken();
this.apiCall(url);
}
})
}
}
Aaaaand I wasn't returning the call.
return this.apiCall(url);
Works now

Component in Vue.js server-side rendering

I am trying to make my Vue app have server-side rendering. I am using vue-server-renderer (https://www.npmjs.com/package/vue-server-renderer). Client-side rendering is working fine.
My app use vue-router and axios
Here is my server.js:
server.get('*', (request, response) => {
bundleRenderer.renderToString({ url: request.url }, (error, htmlPromise) => {
if (error) {
// Log the error in the console
console.error(error)
// Tell the client something went wrong
return response
.status(500)
.send(error)
}
response.send(layout.replace('<div id=app></div>', htmlPromise))
})
})
getInfo() is the method to fetch server data.
Here is getInfo():
export default {
methods: {
getInfo(api) {
return axios
.get(api || this.$route.params.path)
.then((data) => {
this.data = data
this.$set(this, 'isLoading', false)
})
},
},
}
My server entry is:
import { app, router, store } from './index'
export default context => {
let componentPromises = router.getMatchedComponents().filter((component) => {
return component.methods && component.methods.getInfo
}).map((component) => {
return component.methods.getInfo()
})
return Promise.all(componentPromises).then(() => {
return app
})
}
However, I soon realize that all the components from router.getMatchedComponents() does not have $route or $set. Therefore, the method getInfo() stops working.
The document from https://router.vuejs.org/en/api/router-instance.html is very short and does not provide much information:
router.getMatchedComponents()
Returns an Array of the components (definition/constructor, not
instances) matched by the current route. This is mostly used during
server-side rendering to perform data prefetching.
How can I fix the problem?
I have previously incurred into a similar problem and managed to successfully prefetch data by doing the following:
app.$router.onReady(() => {
const matchedComponents = app.$router.getMatchedComponents()
if (!matchedComponents.length) { /* ... */}
Promise.all(matchedComponents.map((Component: any) => {
if (Component.options.methods.asyncData) {
return Component.options.methods.asyncData({
store: app.$store,
route: app.$router.currentRoute
});
}
})).then(() => { /* your callback here ... */ });
}
According to vue ssr documentation (https://ssr.vuejs.org/en/data.html) the suggested way is to use a custom asyncData method in your component to perform data fetching rather than calling component methods directly:
export default {
asyncData ({ store, route }) {
// return the Promise from the action
return store.dispatch('fetchItem', route.params.id)
}
},

Categories