Fetch data before rendering components in Vuejs - vue.js

I am using Vuejs with Vuex and I have actions that are used to fetch data from the database, I want the fetch to be before rendering the components depended on this data. So where can I put these actions in my code ?

Mounted / BeforeMounted
Created
On Vuejs 3 you have onMounted
https://vuejs.org/api/

You can use your action (ex: getData) on onMounted hook
and you can use async await to be sure that your action call is done before moving to the next step
here is an exemple (i used axios just for the exemple you are free to use any methode to get your data) :
<template>
<h1>My component</h1>
</template>
<script>
import axios from 'axios'
export default {
name: 'MyComponent',
setup() {
const baseUrl = 'your-api-base-url'
const apiClient = axios.create({
baseURL: baseUrl,
withCredentials: false,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
})
const getData = async() => {
try {
return await apiClient.get('/api-path', {}) // here i call my
api to get data you can call your function
} catch (error) {
throw new Error(error)
}
}
onMounted(() => {
getData()
})
}
}
</script>

Related

How can i passing response of axios to variable outside the axios

I wan't to fill the variable outside the axios in array type and then export the array to another file.
navigation.js
import axios from 'axios'
/* eslint-disable */
let menus
axios.get('https://api.link/', {
headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` }
})
.then(res => {
menus = res.data
})
/* eslint-enable */
export default menus
index.js
import navMenu from './navigation'
// Array of sections
export default [...navMenu]
api results
[
{
"Oid": "3b05b576-fa95-11eb-84d0-00163e01a21d",
"Name": "Menu",
"Icon": "BookIcon",
"Submenu": [
{
"Oid": "3b05b576-fa95-11eb-84d0-00163e013r46",
"Name": "Submenu",
"Link": "link/to/page",
"Icon": "BookOpenIcon"
}
]
}
]
or maybe give me the solution to export array from api to index.js
I would suggest to wrap your axios call as a function and use its return value rather than exporting the menu variable.
async function makeRequest() {
const response = await axios.get('https://api.link/', {
headers: { Authorization: `Bearer ${localStorage.getItem('accessToken')}` }
})
return response.data
}
and you can call that function as follows:
makeRequest().then((data) => {
//do something with data
}
I am not sure what you are trying to achieve, but there is an example of what I suggested:
App.js
import getDataFromServer from "./navigation";
import { useState } from "react";
function App() {
const [myState, setMyState] = useState([]);
const callServer = async () => {
let result = await getDataFromServer();
setMyState(result.data);
console.log(myState);
};
const listItems = myState.map((item) => <li>{item["title"]}</li>);
return (
<div>
<button onClick={callServer}>Call Server</button>
<ul>{listItems}</ul>
</div>
);
}
export default App;
In another module e.g. (navigation.js)
import axios from "axios";
const url = "https://jsonplaceholder.typicode.com/posts";
export default async function getDataFromServer() {
const result = await axios.get(url);
return result;
}
The example uses sample API from jsonplaceholder site:
const url = "https://jsonplaceholder.typicode.com/posts";
The bottom line, your alternatives are:
If your data reside in an external module, you can follow the
intuition I suggested above.
If you are passing data from a parent component to a child component, you can use props.
If you are passing data from a child component to a parent component, you can use callbacks.
Or you can look into useContext or look into other state
containers (redux) to see how you can share data between the different
components in an app.

Axios mixin call order in Vue

I'm trying to create a Vue mixin which would handle my Axios calls (I need to do a lot of similar calls from different components).
Mixin code is like this:
import axios from "axios";
const restService = {
methods: {
executeRequest( url, payload ) {
axios
.post(url, JSON.stringify(payload), {
headers: { 'Content-Type': 'application/json' }
})
.then(response => {
return response.data;
})
}
}
}
export default restService
Which I then call from my component like this:
import restService from "#/services/RestService";
export default {
//...
mixins: [restService],
methods: {
saveChanges() {
//...
let response = this.executeAuthorizedRequest( changeData );
this.$notify({
//some logic that depends on my response object
});
}
}
}
The problem is that .then() from the mixin is for some reason called later than my this.notify() from component which leads to an error.
Is there a way to make sure that .then() is called before proceeding? The same axios code works as expected when written in component method itself.
I'm relatively new to JS frameworks so maybe I don't understand something obvious.
Axios calls are asynchronous. So the response may not be returned before this.notify(). You may consider rewriting the axios calls with async/await or Promise to make sure the response is returned. Here is an example with async/await:
import axios from "axios";
const restService = {
methods: {
executeRequest(url, payload) {
return axios
.post(url, JSON.stringify(payload), {
headers: {
'Content-Type': 'application/json'
}
})
}
}
}
export default restService
And use the call like:
import restService from "#/services/RestService";
export default {
//...
mixins: [restService],
methods: {
async saveChanges() {
//...
let response = await this.executeAuthorizedRequest( changeData );
this.$notify({
// do anything here with response.data
});
}
}
}
Try out to use async/await when you call the method and inside the method just return the request :
import axios from "axios";
const restService = {
methods: {
executeRequest( url, payload ) {
return axios.post(url, JSON.stringify(payload), {
headers: { 'Content-Type': 'application/json' }
})
}
}
}
export default restService
then call it like :
import restService from "#/services/RestService";
export default {
//...
mixins: [restService],
methods: {
async saveChanges() {
//...
let response = await this.executeAuthorizedRequest( changeData );
this.$notify({
//some logic that depends on my response object
});
}
}
}

Why do I have TypeError: Cannot read property 'protocol' of undefined using axios with vue?

I'm trying to do a simple get request to my backend server with axios. I'm getting this stacktrace in the console:
TypeError: Cannot read property 'protocol' of undefined
at isURLSameOrigin (isURLSameOrigin.js?3934:57)
at dispatchXhrRequest (xhr.js?b50d:109)
at new Promise (<anonymous>)
at xhrAdapter (xhr.js?b50d:12)
at dispatchRequest (dispatchRequest.js?5270:52)
Here are the code files: https://gist.github.com/DavidMarcu/68f6bd20fa1e21568464f10e2a2baa6a
Code:
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import bookService from '#/service/BookService.js';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
books: []
},
mutations: {
SET_BOOKS(books) {
this.state.books = books
}
},
actions: {
fetchBooks(context) {
bookService.getAllBooks()
.then(response => {
console.log(response)
context.commit('SET_BOOKS', response.data)
})
.catch(error => {
console.log(error)
})
}
},
modules: {
}
})
Home.vue(view component) - template is irrelevant
<script>
import BookElement from "#/components/BookElement.vue";
export default {
components: {
BookElement
},
created() {
this.$store.dispatch("fetchBooks");
},
computed: {
books() {
return this.$store.state.books;
}
}
};
</script>
<style scoped>
</style>
BookService.js
import axios from 'axios';
const apiClient = axios.create({
baseUrl: `http://localhost:9001/books`,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
const bookService = {
getAllBooks() {
return apiClient.get()
}
}
export default bookService
I tried the plugin solution(vue-axios), but I don't understand all that hassle just for making a get request that is not even in a .vue file, but in a .js file which I import in my component. The axios package is indeed installed. What I'm expecting is to see in the Home component in the vue devtools that in the books computed property I have the response body.
Edit: I added the code in the question as well.
You didn't pass url to get:
Change it like this:
return apiClient.get('/')

Vue Axios local stored token is undefined

In Vue I am building a small Userprofile page. It is build on token-authentication using Axios. When mounting this page the token is undefined.
with login a token is placed in the localStorage.
The Axios Get request is build outside the Vue component
Api.js
import axios from 'axios'
export default () => {
return axios.create({
baseURL: `http://localhost:8081/`
})
}
Get request
import Api from '#/services/Api'
let config = {
headers: {
'Content-Type': 'application/json',
'Authorization': localStorage.getItem('token')
}
}
export default {
index () {
return Api().get('user/profile', config)
}
}
Vue
<script>
export default {
data: () => ({
user: {}
}),
mounted () {
this.user = UserProfileService.index
console.log(UserProfileService.config)
}
}
</script>
There are many advices and I tried them all. With tics, with commas etc. Who sees the big picture?
Use a request interceptor to set the Authorization header:
// Api.js
export default () => {
const instance = axios.create({
baseURL: `http://localhost:8081/`
})
instance.interceptors.request.use(function (config) {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
}, function (error) {
// Do something with request error
return Promise.reject(error)
})
return instance
}
I added the code from digital drifter, and solved the problem (with help) with changing the mounted function in Vue.
mounted () {
console.log('mounted')
UserProfileService.index()
.then((res) => {
this.user = res.data
})
}

Set axios authorization header depends on store changes

I am new to Vue (I am a react person) and I am having this problem.
axios.js
import store from '../../store/index';
import axios from 'axios'
const API_URL = process.env.API_URL;
const token = store.getters.auth.token;
export default axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
}
})
store/index.js
import auth from './modules/auth'
Vue.use(Vuex);
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
state: {},
getters : {},
mutations: {},
actions:{},
modules: {
auth
},
strict: debug,
})
modules/auth
import { AUTH_SUCCESS, AUTH_GUEST } from '../actions/auth'
import axios from '../../util/axios/axios'
import Vue from "vue";
const state = {
token: localStorage.token || '',
};
const getters = {
token: state => state.token
};
const actions = {
[AUTH_GUEST]: async ({commit}) => {
await axios.post('auth/register',)
.then(response => {
commit(AUTH_SUCCESS, response);
})
.catch(error => {
console.log(error);
});
},
};
const mutations = {
[AUTH_SUCCESS]: (state, resp) => {
state.token = resp.data.token;
},
}
export default {
state,
getters,
actions,
mutations,
}
when trying to get the store from store/index it returns undefined.
probably the axios has been called before the store has been initialized.
but how can I deal with it?
the flow of the app is.
user register->get token->update store with this token->add to the axios header.
so for now on, all calls to the api will have the token provided.
First of all, you should be careful with Vue's reactivity caveats which affect Vuex aswell. In your case, you're adding a new property inside an object in a mutation.
Back to the main issue, your axios.js file is being executed before the Store instance is built, that's why you cannot access to it and you get undefined.
What I'd do is:
axios.js
import axios from 'axios';
const API_URL = process.env.API_URL;
export default (store) => axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${store.getters.auth.token}`
}
});
and then in your main file, where you have the main Vue instantiation I'd just run the function there, exporting the return of that function.