I need to get data with axios and send them to my calendar with the 'splitDays' table and I have to change the variable name of my data to put "class" and "label"
I can recover my data but when I leave the axios I go to undefined
data() {
return {
splitDays:[], // :splitDays
};
},
mounted() {
axios
.get(`${process.env.*****}/users?role=***&active=***`)
.then(response => ( this.users = response.data,
console.log(this.users)
))
console.log(this.users)
/*
for (let splitDayIndex in mySplitDays){
let splitDay= mySplitDays[splitDayIndex]
splitDay.class = splitDay.lastname
splitDay.label = splitDay.lastname
mySplitDays[splitDayIndex]=splitDay
}
*/
},
I'm not sure I understand what you mean by when I leave the axios I go to undefined, but your second console.log(...) will be executed before your axios call finishes. Try with:
data() {
return {
splitDays:[], // :splitDays
};
},
async mounted() {
let response = await axios
.get(`${process.env.AFFECTIT_API}/users?role=Collaborateur&active=1`)
this.users = response.data
console.log(this.users)
/*
for (let splitDayIndex in mySplitDays){
let splitDay= mySplitDays[splitDayIndex]
splitDay.class = splitDay.lastname
splitDay.label = splitDay.lastname
mySplitDays[splitDayIndex]=splitDay
}
*/
},
Related
I have an endpoint that returns an object. The object is:
{
"id": "669f8",
"creation_date": "2022-01-13 10:33:06.046652+01:00",
"case_type": "Summary",
"process_types": "",
"case_id": "ad23423s",
"current_stage": "",
"current_stage_name": "",
"consolidation": "None",
"last_change_date": "2022-01-14 14:35:17.563449+01:00",
"status": 1,
"assign": "Yes"
}
I want to display it in my React project.
Firstly I want to take object fields, and then the values.
I wrote this code:
class DetailsPage extends React.Component<DetailProps> {
async getData() {
const data = await Dependencies.backend.getList(this.props.url);
if (data) {
return data;
}
return [];
}
render() {
const data = this.getData()
const fields: string[][] = []
const test: any[] = []
data.then((val: any) =>
fields.push(Object.keys(val))
// console.log(val)
) .catch(err => console.log("There was an error:" + err))
console.log("testeststt")
console.log(fields)
return (
<div>
<h1>Hoi!</h1>
</div>
);
}
}
The console.log(fields) returns
[]
And when I open the array it shows
But I can't get the 0th row.
How can I get and push another array or something?
The reason for this is that the render function only renders out the UI. It is not supposed to make asynchronous stuff. Since, you need to invoke the API call when the component mounts, you can do it in componentDidMount and when you have data, update it in state.
This way, on the initial render, you will have 0 items in the data. When you update the state, it will re-render the component and then you will have items in your data.
You can do something like the following:
componentDidMount() {
this.getData()
}
async getData() {
const data = await Dependencies.backend.getList(this.props.url);
this.setState({ data: data ?? [] })
}
render() {
const { data } = this.state;
console.log(data)
}
I have external .js file created exactly for fetching data from backend based on website locale. Here is the code:
import { ref } from "vue";
export function fetchData(section, key) {
// GET request using fetch with error handling and headers
const headers = {
method: "GET",
headers: { "Content-Type": "application/json" },
};
const fetchedData = ref(null);
fetch(
"http://localhost:4000/api/" + section + "/?api-key=" + key,
headers
)
.then(async (response) => {
const data = await response.json();
// check for error response
if (!response.ok) {
// get error message from body or default to response statusText
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
fetchedData.value = data;
})
.catch((error) => {
console.error("There was an error!", error);
return error;
});
return fetchedData;
}
And this is code from .vue file where I calling fetchData function:
<script setup>
import { fetchData } from "../../utils/universal-fetch";
import { ref, watch } from "vue";
import { useStore } from "../../stores/language.js";
import { useI18n } from "vue-i18n";
import AOS from "aos";
const store = useStore();
const { locale } = useI18n({ useScope: "global" });
const fetchedData = ref(fetchData("homeFirstSection", store.getLanguage));
AOS.init();
watch(
() => locale.value,
() => {
fetchedData.value = fetchData("homeFirstSection", store.getLanguage);
}
);
</script>
When page is created/refreshed, fetchData function fetch data from backend correctly. The problem which I'm trying to solve is that, when I change a locale, watcher automatically detects that, locale was changed and variable fetchedData should be updated based on choosen locale.
Problem
Thanks!
I found a problem. Here is code:
export function async fetchData(section, key) { // Added async
// GET request using fetch with error handling and headers
const headers = {
method: "GET",
headers: { "Content-Type": "application/json" },
};
let fetchedData = null;
await fetch( // Added await
"http://localhost:4000/api/" + section + "/?api-key=" + key,
headers
)
.then(async (response) => {
const data = await response.json();
// check for error response
if (!response.ok) {
// get error message from body or default to response statusText
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
fetchedData = data;
})
.catch((error) => {
console.error("There was an error!", error);
return error;
});
return fetchedData;
}
In my external .js file I was missing one more async await.
<script setup>
import { fetchData } from "../../utils/universal-fetch";
import { ref, watch } from "vue";
import { useStore } from "../../stores/language.js";
import { useI18n } from "vue-i18n";
import AOS from "aos";
const store = useStore();
const { locale } = useI18n({ useScope: "global" });
const fetchedData = ref(null);
fetchData("agreementsFirstSection", store.getLanguage).then(
(data) => (fetchedData.value = data)
); // Added .then
AOS.init();
watch(
() => locale.value,
() => {
fetchData("agreementsFirstSection", store.getLanguage).then(
(data) => (fetchedData.value = data)
); // Added .then instead of directly assign
}
);
</script>
And in .vue file I was missing .then insted of directly assigning value to variable. I added comments to code to compare changes before and after.
Problem solved
I am trying to test an axios request, and I need to use an auth token in order to access the endpoint, however my test fails because I am getting "Bearer null" and inputting this into my headers.Authorization. Here is my actual code below
File I'm testing:
this.$axios.get(url, { headers: { Authorization: `Bearer ${localStorage.getItem("access-token")}` } })
.then((response) => {
this.loading = true;
// Get latest barcode created and default it to our "from" input
this.barcodeFrom = response.data.data[response.data.data.length - 1]['i_end_uid'] + 1;
this.barcodeTo = this.barcodeFrom + 1;
this.barcodeRanges = response.data.data;
// Here we add to the data array to make printed barcodes more obvious for the user
this.barcodeRanges.map(item => item['range'] = `${item['i_start_uid']} - ${item['i_end_uid']}`);
// Make newest barcodes appear at the top
this.barcodeRanges.sort((a, b) => new Date(b['created_at']) - new Date(a['created_at']));
})
.catch((error) => {
console.log('Barcode retrieval error:', error);
this.barcodeFrom === 0 ? null : this.snackbarError = true;
})
.finally(() => {
// Edge case when there's no barcode records
this.barcodeFrom === 0 ? this.barcodeTo = 1 : null;
this.loading = false
});
console.log('bcr', this.barcodeRanges);
Test file:
import Vuetify from "vuetify";
import Vuex from "vuex";
import { createLocalVue, shallowMount } from "#vue/test-utils";
import VueMobileDetection from "vue-mobile-detection";
import axios from 'axios';
import index from "#/pages/barcode_logs/index";
describe('/pages/barcode_logs/index.vue', () => {
// Initialize our 3rd party stuff
const localVue = createLocalVue();
localVue.use(Vuetify);
localVue.use(Vuex);
localVue.use(axios);
localVue.use(VueMobileDetection);
// Initialize store
let store;
// Create store
store = new Vuex.Store({
modules: {
core: {
state: {
labgroup:{
current: {
id: 1
}
}
}
}
}
});
// Set-up wrapper options
const wrapperOptions = {
localVue,
store,
mocks: {
$axios: {
get: jest.fn(() => Promise.resolve({ data: {} }))
}
}
};
// Prep spies for our component methods we want to validate
const spycreateBarcodes = jest.spyOn(index.methods, 'createBarcodes');
const createdHook = jest.spyOn(index, 'created');
// Mount the component we're testing
const wrapper = shallowMount(index, wrapperOptions);
test('if barcode logs were retrieved', () => {
expect(createdHook).toHaveBeenCalled();
expect(wrapper.vm.barcodeRanges).toHaveLength(11);
});
});
How do I mock or get the actual auth token in to work in my test?
const setItem = jest.spyOn(Storage.prototype, 'setItem')
const getItem = jest.spyOn(Storage.prototype, 'getItem')
expect(setItem).toHaveBeenCalled()
expect(getItem).toHaveBeenCalled()
You can try to mock localStorage before creating instance of a wrapper like this:
global.localStorage = {
state: {
'access-token': 'superHashedString'
},
setItem (key, item) {
this.state[key] = item
},
getItem (key) {
return this.state[key]
}
}
You can also spy on localStorage functions to check what arguments they were called with:
jest.spyOn(global.localStorage, 'setItem')
jest.spyOn(global.localStorage, 'getItem')
OR
You can delete localVue.use(axios) to let your $axios mock work correctly.
This
mocks: {
$axios: {
get: jest.fn(() => Promise.resolve({ data: {} }))
}
}
is not working because of that
localVue.use(axios)
I am having troubles updating the DOM after fetching from an API.
My object is fetching the data correctly but the DOM is being rendered before and it won't update after receiving the API Data, I can't seem to understand why is not updating itself.
Here is my code:
<template>
<div>
<h1>Weather</h1>
{{ weather }}
</div>
</template>
<script>
export default {
name: 'Weather',
data() {
return {
weather : {},
}
},
created() {
this.getWeather()
},
methods: {
async getWeather() {
let self = this;
try {
const response = await fetch('https://api.weatherbit.io/v2.0/current?city=Berlin&country=DE&key=KEY');
const myJson = await response.json();
self.weather.temp = myJson.data[0].temp;
self.weather.sensation = myJson.data[0].app_temp;
self.weather.description = myJson.data[0].weather.description;
} catch (error) {
console.error(error);
}
}
</script>
You should assign the response value to the weather property directly like this.
methods: {
async getWeather() {
let self = this;
try {
const response = await fetch('https://api.weatherbit.io/v2.0/current?city=Berlin&country=DE&key=dcbea1b771ab41f09cd6b138d8cd50c2');
const myJson = await response.json();
self.weather = myJson.data[0].temp;
console.log(self.weather);
} catch (error) {
console.error(error);
}
}
}
Here is the working example.
https://jsfiddle.net/srfpw785/
I think you should insert your logic inside mounted() , not in created() , this should fix your problem with rendering.
<template>
<div>
<h1>Weather</h1>
{{ weather }}
</div>
</template>
<script>
export default {
name: 'Weather',
data() {
return {
weather : {},
}
},
mounted() {
this.getWeather()
},
methods: {
async getWeather() {
let self = this;
try {
const response = await fetch('https://api.weatherbit.io/v2.0/current?city=Berlin&country=DE&key=dcbea1b771ab41f09cd6b138d8cd50c2');
const myJson = await response.json();
self.weather.temp = myJson.data[0].temp;
self.weather.sensation = myJson.data[0].app_temp;
self.weather.description = myJson.data[0].weather.description;
} catch (error) {
console.error(error);
}
}
</script>
These are the steps in Vue lifecycle :
beforCreate,
created,
beforeMount,
mounted,
beforeUpdate,
updated,
beforeDestroy,
destroyed
Hope this will help you to understand Vue lifecycle :)
I have setup vuex and i would like to later fetch the data and update my form model but this fails
In my vuex
//state
const state = {
profile: [],
}
//getter
const getters = {
profileDetails: state => state.profile,
}
//the actions
const actions = {
getProfileDetails ({ commit }) {
axios.get('/my-profile-details')
.then((response) => {
let data = response.data;
commit(types.RECEIVED_USERS, {data});
},
);
}
}
const mutations = {
[types.RECEIVED_USERS] (state, { data }) {
state.profile = data;
state.dataloaded = true;
},
}
Now in my vue js file
export default{
data: () => ({
profile_form:{
nickname:'',
first_name:'',
last_name:'',
email:''
}
}),
computed:{
...mapGetters({
user: 'profileDetails',
}),
},
methods:{
setUpDetails(){
this.profile_form.email = this.user.email; //the value is always undefined
}
},
mounted(){
this.$store.dispatch('getProfileDetails').then(
(res)=>{
console.log(res); //this is undefined
this.setUpDetails(); ///this is never executed
}
);
this.setUpDetails(); //tried adding it here
}
By checking with the vue developer tools i can see that the vuex has data but my component cant fetch the data in vuex after calling the dispatch in the action to fetch the data.
Where am i going wrong.
Nb: AM using the data to update a form like this
<input v-model="profile_form.email" >
Your mounted method expects a return (res) from getProfileDetails, but the action isn't returning anything, so you could simply try
const actions = {
getProfileDetails ({ commit }) {
return axios.get('/my-profile-details')
.then((response) => {
let data = response.data;
commit(types.RECEIVED_USERS, {data});
return data // put value into promise
},
);
}
}
However, it's more usual to commit to store from within the action (which you are doing) and let the component get the new values from a getter (which you have) - i.e one-way-data-flow.
This is how I'd set it up.
data: () => ({
profile_form:{
nickname:'',
first_name:'',
last_name:'',
email:''
}
}),
mounted(){
this.$store.dispatch('getProfileDetails')
}
computed: {
...mapGetters({
user: 'profileDetails',
}),
}
watch: {
user (profileData){
this.profile_form = Object.assign({}, profileData);
}
},
methods:{
submit(){
this.$store.commit('submituser', this.profile_form)
}
},