Learning VueJS and trying to do a simple API call on component load to put a list of repos onto my page. When I call and set the this.repos from the created() method, no problem. But if I set it as a method and then call it from this.getRepos nothing happens. No error, nothing. What am I missing about VueJS?
This works:
data: () => ({
msg: 'Github Repos',
ok: 'Im practically giving away these repos',
repos: [],
}),
methods: {
},
async created() {
const repos = await axios.get('https://api.github.com/orgs/octokit/repos');
this.repos = repos.data.map(repo =>
`<div class="box"><a href="${repo.html_url}">
${repo.name}
</div>`,
);
},
This DOES NOT work:
data: () => ({
msg: 'Github Repos',
ok: 'Im practically giving away these repos',
repos: [],
}),
methods: {
getRepos: async () => {
const repos = await axios.get('https://api.github.com/orgs/octokit/repos');
this.repos = repos.data.map(repo =>
`<div class="box"><a href="${repo.html_url}">
${repo.name}
</div>`,
);
},
},
created() {
this.getRepos();
},
Any ideas? Thanks!
It's simply because you used arrow functions here so that this.repos's this is bound to window object. Changing async () => {} to async function() {} will help you overcome it.
See demo
Note that you should not use an arrow function to define a method (e.g. plus: () => this.a++). The reason is arrow functions bind the parent context, so this will not be the Vue instance as you expect and this.a will be undefined.
reference
Another way to do an Axios call with Vue using then() method:
demo
created() {
axios.get('https://api.github.com/orgs/octokit/repos', {
params: {
type: 'all',
},
})
.then((res) => {
console.log('Success Response', res.data);
res.data.forEach((repo) => {
this.repos.push({ name: repo.name, url: repo.html_url, language: repo.language });
});
})
.catch((err) => {
console.log('Error', err);
});
},
Related
I am new to writing test with async/await in angular.
I have the following code. The service method is an async method. The test fails saying component.options.length is 0.
Can anyone please help me how to fix the error so the options has got the value i set in spy?
Thanks
spec.ts
spySideNavService = jasmine.createSpyObj('SideNavService', [], {
setOrgUserDetails: () => {},
loadMenus: () =>
[
{
id: 'my-menu',
label: 'My Menu',
icon: 'far fa-envelope fa-2x',
url: 'url'
}
] as NavOption[]
});
describe('ngOnInit', () => {
it('should add navigation options', () => {
expect(component.options.length).toBeGreaterThan(0);
});
});
component:
ngOnInit(): void {
this.options = await this.sideNavService.loadMenus();
}
SideNavService:
async loadMenus(): Promise<NavOption[]> {
//logic
}
Tried answer given below but still not working:
describe('ngOnInit', () => {
it('should add navigation options', fakeAsync(() => {
// !! call tick(); to tell the test to resolve all promises
// before coming to my expect line
tick();
expect(component.options.length).toBeGreaterThan(0);
}));
});
You need to use fakeAsync/tick to control promises.
// !! add fakeAsync
it('should add navigation options', fakeAsync(() => {
// !! call tick(); to tell the test to resolve all promises
// before coming to my expect line
// !! call ngOnInit
component.ngOnInit();
console.log(component.options);
tick();
console.log(component.options);
expect(component.options.length).toBeGreaterThan(0);
}));
Before, the test would go to the await line and go back to the test for the expect because the await is saying to do this later. Now with the tick, we are saying if they are any promises created, resolve them before moving forward.
Also, I think you're missing a Promise.resolve on loadMenus.
loadMenus: () => Promise.resolve(
[
{
id: 'my-menu',
label: 'My Menu',
icon: 'far fa-envelope fa-2x',
url: 'url'
}
] as NavOption[])
I am thinking the Promise.resolve is required so it can be awaited.
edit
I don't think the done callback will help you.
You can try using await fixture.whenStable() to wait for the promise(s). Try this:
describe('ngOnInit', () => {
it('should add navigation options', async () => {
component.ngOnInit();
await fixture.whenStable();
expect(component.options.length).toBeGreaterThan(0);
});
});
I'm trying to get the data from database using an API, but there are no output on my vue controller.
Am I doing this right?
I think I'm assigning the scheduleList the wrong way.
I'm very new to vue.js and API, I want to know what I'm doing wrong here.
Controller
public function schedules(){
return Schedule::all();
}
api.php
Route::get('schedules', 'CalendarController#schedules');
Vue Component
<script>
import axios from 'axios'
export default {
data() {
return {
schedules: [],
scheduleList: [
{
id: schedules.id,
title: schedules.title,
category: schedules.category,
start: schedules.start,
end: schedules.end
},
],
};
},
methods: {
loadSchedules() {
axios.get('/api/schedules')
.then((response) => {
this.schedules = response.data;
})
}
},
mounted() {
this.loadSchedules();
}
};
</script>
<style>
</style>
The issue is in your data option because you're referencing schedules which is undefined, I'm sure that you're meaning this.schedules but doing that will not solve the issue because at first rendering this.schedules is an empty array, another problem that you're referencing at as object in scheduleList items using schedules.id, if the schedules property is an array i recommend the following solution :
<script>
import axios from 'axios'
export default {
data() {
return {
schedules: [],
scheduleList: [],
};
},
methods: {
loadSchedules() {
axios.get('/api/schedules')
.then((response) => {
this.schedules = response.data;
let schedule=this.schedules[0]
this.scheduleList.push({
id: schedule.id,
title: schedule.title,
category: schedule.category,
start: schedule.start,
end: schedule.end
})
})
}
},
mounted() {
this.loadSchedules();
}
};
</script>
always catch errors if you do promises.
loadSchedules() {
axios.get('/api/schedules')
.then((response) => {
this.schedules = response.data;
})
.catch(error => {
console.log(error)
})
inside your error you can better see whats going wrong.
other way is the "network" tab in your browser where you can trace your api request
I have the following action and test case - when I run this test(jest) - I am seeing TypeError: Cannot read property 'data' of undefined in action creator, not sure what is missing here? I am providing mockData that is expected. is it because there is an async nested here? but i am using `.then but it still fails.
Action creator:
export const getUser = ({
uname,
apiendpoint,
}) => {
const arguments = {};
return async (dispatch) => {
await axiosHelper({ ---> this will return axios.get
arguments,
path: `${apiendpoint}/${uname}`,
dispatch,
}).then(async ({ data, headers }) => { -- getting error at this line.
dispatch({ type: GET_USER, payload: data });
dispatch({ type: GET_NUMBEROFUSERS, payload: headers });
});
};
};
Test:
describe('Get User Action', () => {
let store;
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
beforeEach(() => {
store = mockStore({
data: [],
});
});
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
})
const arguments = {
uname: 'user123',
apiendpoint: 'test',
};
const url = 'https://www.localhost.com/blah/blah';
it('should get a User', () => {
fetchMock
.getOnce(url, {
data: mockData, -->external mock js file with user data {}
headers: {
'content-type': 'application/json'
}
});
const expectedActions = [
{
type: 'GET_USER',
data: mockData
},
{ type: 'GET_NUMBEROFUSERS' }
];
return store.dispatch(actions.getUser(arguments)).then(() => {
expect(store.getActions()).toEqual(expectedActions);
});
});
You are using await AND then on the same function (axiosHelper for example).
This is wrong usage and will lead to many errors of undefined.
You either use a callback-function or a .then() or an await but not 2 or all of them.
I recommend to watch some tutorials/explanations about async/await because it's really important to understand what a Promise is.
What's happening in your cas is that axiosHelper is executed 2 times, because if it's finished the then-part will fire up but at the exactly same time (because it's async) the await finishes and code-execution continues the parent flow. This brings up race-conditions and, as i said, will lead to undefined because you are executing the same logic twice or more.
Ok I'm a beginner at VueJS and I'm just trying to do a simple XHR call and bind the json data response to my variable...
I have a component App.vue and this part of the template I want to show the results of the json. bpi is the name of the variable
<div id="simulationPoints">
<h2 className="logTitle">Full Log:</h2>
{{ bpi }}
</div>
then my script
export default {
name: 'App',
data: () => ({
bpi: []
}),
mounted: () => {
axios.get(`https://api.coindesk.com/v1/bpi/historical/close.jsonp?start=2011-01-01&end=2018-02-01`)
.then(response => {
this.bpi = response.data.bpi
})
.catch(e => {
this.errors.push(e)
})
}
}
This doesn't seem to work. I'm using Axiom to fetch the data and assign the response, and this is how all the examples I found online did it, but the array object I have is still empty and it doesn't render on the page. I don't know whats the issue here? A Vue expert please help :)
There are sorts of problem in your code.
First, don't use arrow function on options property or callback since arrow functions are bound to the parent context, this will not be the Vue instance as you’d expect.
Second, use return statement in your data function.
Third, use created hook for inserting data after instance is created. mounted hook is called for mutation after DOM is rendered.
export default {
name: 'App',
data: function() {
return {
bpi: []
}
},
created() {
axios.get(`https://api.coindesk.com/v1/bpi/historical/close.jsonp?start=2011-01-01&end=2018-02-01`)
.then(response => {
this.bpi = response.data.bpi
})
.catch(e => {
this.errors.push(e)
})
}
}
I am writing an ngrx effect and trying to test it. However, the effect calls a service that calls an API that will require authentication. As a result, I am trying to create a spy in Jasmine to handle returning the data. This is my first time using ngrx effects, so I am really unsure where to put different parts of the code. Nothing I have done is allowing this test to run correctly.
The effect is a very simple one as follows:
#Effect() itemSelected: Observable<Action> = this.d.pessimisticUpdate('ITEM_SELECTED', {
run: (action: ItemSelected) => {
return this.myService.getItemById(action.payload).map((res) => ({
type: 'ITEM_INFO_RETURNED',
payload: res
}));
},
onError: (a: ItemSelected, error) => {
console.error('Error', error);
}
});
constructor(private d: DataPersistence<ItemState>, private myService: MyService) {
// add auth headers here
}
My test is currently written as follows:
describe('ItemEffects', () => {
let actions: Observable<any>;
let effects: ItemEffects;
let myService = jasmine.createSpyObj('MyService', ['getItemById']);
let item1: Item = {id: 1, name: 'Item 1'};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({}),
],
providers: [
ItemEffects,
DataPersistence,
provideMockActions(() => actions),
{
provide: MyService,
useValue: myService
}
],
});
effects = TestBed.get(ItemEffects);
});
describe('someEffect', () => {
it('should work', async () => {
myService.getItemById.and.callFake(function (id) {
return items.find((r) => r.id === id);
});
actions = hot('-a-|', { a:{ type:'ITEM_INFO_RETURNED', payload:1}});
expect(effects.itemSelected).toEqual(
{ type: 'ITEM_INFO_RETURNED', payload: { item1 } }
);
});
});
});
This is still attempting to use the production MyService (requiring authentication). If I move the myService override out of the provider and into the actual test,
TestBed.overrideProvider(MyService, { useValue: myService });
I get an error that it cannot read the property "itemSelected" of undefined, which would be when I am calling the effects at the very end of the test.
I am really new to ngrx, as well as to TestBed. Is there somewhere else I should be defining this Jasmine spy? Should I be using something other than createSpyOn for this?
Thanks in advance!