I'm trying to get data out of response but I can't seem to get the result I want.
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.accessToken = res.authResponse.accessToken;
this.expiresIn = res.authResponse.expiresIn;
this.signedRequest = res.authResponse.signedRequest;
this.userId = res.authResponse.userID;
console.log('Logged In', res, this.accessToken); //works without problem
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
console.log(this.accessToken) //printing 'undefined'
}
Here within then => { }, console.log seems to print the data in res without any problem. I can see data I want but when I console.log outside of then =>{ }, it's giving me undefined.
what am I doing wrong? I need to use data inside response and pass them to other component but I can't seem to figure out what I'm doing wrong.
Can anyone help me? Thanks
This is the expected behavior actually.
this.fb.login() is a Promise. This means that the value of the result/response (res) will not readily be available right when it is called, but it 'promises' that it will have a value once some action is taken or a response is returned and 'then' it will do something. In this case that action would be connecting to the Facebook API and having data returned. This is just like Ajax in jQuery if you have experience with that, Promises are a more evolved version of callbacks.
What is happening is that you function is being executed in this order:
this.fb.login() is called. Doesn't have a value yet so it allows the script to continue.
console.log() is called.
this.fb.login's value is returned and the then() closure is executed.
If you want to know when the value is return or perform a specific action once it is returned you can call a function within .then() or look into observables (RxJS) to notify other parts of your application that login was successful (or wasn't).
Observables Example
Here is one example on Observables, however, I would do more research as there are multiple Subjects to select from, all which have slightly different behavior. Also, this kind of pattern works better in Angular2+ if this is performed in a service, that way other components will be able to access the information provided by Facebook.
import { AsyncSubject } from 'rxjs/AsyncSubject';
// ...
response: AsyncSubject<any> = new AsyncSubject();
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.response.next(res);
this.response.complete();
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
}
You then retrieve the data from within response like this:
this.response.subscribe(result => {
console.log(result);
})
Pass Data Example
Since you already have a function in the service designed to receive the data, this may be a wiser implementation in your case.
facebookLogin(): void {
this.fb.login()
.then((res: LoginResponse) => {
this.user_service.passData(res.authResponse.accessToken);
this.router.navigate(['../other-register']);
})
.catch(this.handleError);
}
Related
I'm having trouble finding any resources for this. All I need is help in the right direction to get something tangible to use in some HTML. So basically when I call console.log(keys) I don't get the json objects in my console like I'm used to. Project specifically requests it be done this way via fetch.
function getFromSWAPI() {
fetch("https://swapi.dev/api/people/1")
.then(function (response) {
return response.json()
})
.then(function(data){
updateInfo(data)
})
.catch(function(err) {
console.warn(err)
})
}
const updateInfo = responseJSON => {
console.log(responseJSON.data)
let keys = Object.keys(responseJSON.data)
console.log(keys)
}
So basically there is a minor bug in your code,
In this line
{...}
.then(function (response) {
return response.json()
})
{...}
you have already returned the json and thus you are accessing it in the next line
{...}
.then(function (data) {
updateInfo(data)
})
{...}
As the returned data is itself the real data, you don't have to reaccess it with the data property here
{...}
const updateInfo = responseJSON => {
console.log(responseJSON.data)
let keys = Object.keys(responseJSON.data)
console.log(keys)
}
The finished code will look like this -
function getFromSWAPI() {
fetch("https://swapi.dev/api/people/1")
.then(function (response) {
return response.json()
})
.then(function(data){
updateInfo(data)
})
.catch(function(err) {
console.warn(err)
})
}
const updateInfo = responseJSON => {
console.log(responseJSON)
let keys = Object.keys(responseJSON)
console.log(keys)
}
Alternatively, I would suggest you use async/await if you are using fetch inside a function.
There are several things wrong with this code; only one of them is the direct cause of the trouble you're having with logging, but they will all need to be fixed before this will work.
First: why doesn't console.log work / why are you getting TS error "Cannot convert undefined or null to object at Function.keys"?
Because responseJSON does not have a property named "data", because the API response does not include a property named "data". I suspect that name appears here because you're confused by what HTTP responses are and how Promises work. So let's start at the beginning.
fetch returns an HTTP Response object that contains deep within it some text. You access that text by doing:
let text = await response.text()
Or, if you're using chained .then handlers (as in your sample), you do this:
.then((response) => {
return response.text()
})
If you know that the text is valid JSON (and you do), then you can extract the text and parse it into a value with a single operation: response.json() (which is what you are doing).
This returns a Promise that contains the value encoded by the JSON. If the value is an object, it'll have the properties described by that JSON.
What properties will your object have? Open that URL in your browser, and you'll see them: "birth_year", "created", "edited", "eye_color", "films", "gender", "hair_color", "height", "homeworld", "mass", "name", "skin_color", "species", "starships", "url", "vehicles".
"data" is not on that list.
This is what you are doing with the info from the response:
.then(function (response) {
return response.json()
})
.then(function(data){
updateInfo(data)
})
Thus, you are passing the parsed value directly to the updateInfo function.
The updateInfo function then tries to log the .data property on the received value (which does not exist). That is, .data is undefined.
It is not an error to log an undefined object. But Object.keys will throw if provided with undefined:
Object.keys(undefined)
//> Uncaught TypeError: can't convert undefined to object
The fix is to remove .data from both the log statement and the Object.keys call.
Second: that should have been your first debugging step. Even without understanding all the above: if operations are failing on datapoints buried within a value, the most obvious first debugging step is to "take a step back" and try to examine the variable you're working with.
To illustrate: if console.log(myThing.items[0].parent.nodeName) fails, you should immediately try console.log(myThing) -- this allows you to inspect the myThing variable so you can manually verify whether the path you're accessing is legitimate. One of the most common mistakes made by devs of all experience levels is that they put in a bad data path, simply because to err is human. (Typescript will help you notice this while you're writing code, but you must learn how to trace problems without the help of a tool, or you will always need that tool.)
Third: As I mentioned in the first draft of this post, you're missing some return statements.
Most importantly, your getFromSWAPI function does not return anything. You have to return the fetch if you want callers to receive the data.
function getFromSWAPI() {
return fetch("https://swapi.dev/api/people/1")
// ...rest of chained thens
Also, you need to return something inside the .then handler where you're calling updateInfo. What you return depends on what the purpose of updateInfo is:
if updateInfo is supposed to modify the raw API data for the benefit of downstream code, then you should return the result of calling it:
.then(function(data){
return updateInfo(data)
})
if updateInfo is supposed to cause some kind of side-effect (like updating a local cache with the raw data, or firing an event, etc), then you may want to "bypass" the function: call it, but forward the original value to downstream code:
.then(function(data){
updateInfo(data) // could do _anything_ with data
return data // passes the original data onward
})
Unsolicited code review
You're defining one function using the function keyword, and defining the other as a const arrow function:
function getFromSWAPI() { /* stuff */ }
const updateInfo = responseJSON => { /* stuff */ }
Both patterns work fine, but you should try to be consistent. My advice: if you're still learning JS, prefer the function keyword, because it's more explicit.
Prefer to use async/await instead of chained handlers
You can define functions as async and then await only the specific operations that are asynchronous. In your case, updateInfo does not appear to do any async work, so it kind of sucks that it has to live inside this three-part chained promise. I'd go with this:
async function getFromSWAPI() {
let response = await fetch("https://swapi.dev/api/people/1")
let data = await response.json()
updateInfo(data)
return data
}
I am working on an angular project that uses leaflet framework for implementing geocoder. My current requirement is to use geocoder, retrieve the lat,lng from the results and emit them to the parent component which would then trigger an event in the parent component.
But the issue remains that I get the results in an async function and I am not able to emit from inside it, or get the data outside to emit it.
This is the geocoder code I am using:
let grocoder = L.Control.geocoder({
placeholder:"Search",
geocoder: this.mapGeocoder
})
.on('markgeocoder', this.getGeo);
Now I plan on using the value that I get in this.getGeo and send it to the parent component.
FYI sendData is the #output() variable of type emitter.
getGeo(res){
var lat = res.geocode.center.lat
this.sendData.emit(lat);
}
I am pretty unfamiliar with dealing with promises etc. So is there any way I can do this. I know it has something to do with resolving the promise but I am not able to understand how to implement it.
Any help is appreciated, Thanks!!
So if i have understood your question correctly, you are trying to emit an #Output value in an async function. I dont really know what geocoder or so is but with a little research i found an example and i am going to clarify that as good as possible.
public getAddress(query: string) {
return new Promise((resolve, reject) => {
this.geocoder.geocode({ searchText: query }, result => {
if(result.Response.View.length > 0) {
if(result.Response.View[0].Result.length > 0) {
resolve(result.Response.View[0].Result);
} else {
reject({ message: "no results found" });
}
} else {
reject({ message: "no results found" });
}
}, error => {
reject(error);
});
});}
This function returns a promise, like the name says that's an operation which promises you to return a value after something is completed, the thing with an API call as an example is that it needs some time to call the endpoint and receive a response. You can either sleep your program and await it or define what should happen after the process is completed and let the program continue on.
resolve() just fulfills the promise and lets the program excecute the function defined in the then block.
what reject() does should be clear, it just says something went wrong an that can then be catched by the catch block in the then function block.
So now you have defined a promised action and what the promise it self is, now you want to tell the program what to do when the promise is fullfilled, you can then it in your program.
see this :
this.getAddress(this.yourQuery).then(result => {
this.locations = <Array<any>>result;
}, error => {
console.error(error);
});
this then calls the function and lets you program move on, after the promise is fullfilled it will return to the then function block and do whatever you have specified in this case this.locations is assigned the value of the result.
you can then emit the Output value in the then function block because thats not on an async thread anymore. (you cannot bind an angular emitter value in asynchronous function or on a async thread)
i hope that helped to code itself is from : https://developer.here.com/blog/using-the-here-geocoder-api-for-javascript-in-an-angular-application
like i said i don't know the api and the usecase but i hope that explanation clarified how to define a promise and await it.
just ask if you have questions, i am not a pro with that to i cant just explain the surface functionality.
i can also recommend this video : https://www.youtube.com/watch?v=vn3tm0quoqE&t=194s. he does great short little videos for such things.
hope that has helped.
I recently started migrating things from jQ to a more structured framework being VueJS, and I love it!
Conceptually, Vuex has been a bit of a paradigm shift for me, but I'm confident I know what its all about now, and totally get it! But there exist a few little grey areas, mostly from an implementation standpoint.
This one I feel is good by design, but don't know if it contradicts the Vuex cycle of uni-directional data flow.
Basically, is it considered good practice to return a promise(-like) object from an action? I treat these as async wrappers, with states of failure and the like, so seems like a good fit to return a promise. Contrarily mutators just change things, and are the pure structures within a store/module.
actions in Vuex are asynchronous. The only way to let the calling function (initiator of action) to know that an action is complete - is by returning a Promise and resolving it later.
Here is an example: myAction returns a Promise, makes a http call and resolves or rejects the Promise later - all asynchronously
actions: {
myAction(context, data) {
return new Promise((resolve, reject) => {
// Do something here... lets say, a http call using vue-resource
this.$http("/api/something").then(response => {
// http success, call the mutator and change something in state
resolve(response); // Let the calling function know that http is done. You may send some data back
}, error => {
// http failed, let the calling function know that action did not work out
reject(error);
})
})
}
}
Now, when your Vue component initiates myAction, it will get this Promise object and can know whether it succeeded or not. Here is some sample code for the Vue component:
export default {
mounted: function() {
// This component just got created. Lets fetch some data here using an action
this.$store.dispatch("myAction").then(response => {
console.log("Got some data, now lets show something in this component")
}, error => {
console.error("Got nothing from server. Prompt user to check internet connection and try again")
})
}
}
As you can see above, it is highly beneficial for actions to return a Promise. Otherwise there is no way for the action initiator to know what is happening and when things are stable enough to show something on the user interface.
And a last note regarding mutators - as you rightly pointed out, they are synchronous. They change stuff in the state, and are usually called from actions. There is no need to mix Promises with mutators, as the actions handle that part.
Edit: My views on the Vuex cycle of uni-directional data flow:
If you access data like this.$store.state["your data key"] in your components, then the data flow is uni-directional.
The promise from action is only to let the component know that action is complete.
The component may either take data from promise resolve function in the above example (not uni-directional, therefore not recommended), or directly from $store.state["your data key"] which is unidirectional and follows the vuex data lifecycle.
The above paragraph assumes your mutator uses Vue.set(state, "your data key", http_data), once the http call is completed in your action.
Just for an information on a closed topic:
you don’t have to create a promise, axios returns one itself:
Ref: https://forum.vuejs.org/t/how-to-resolve-a-promise-object-in-a-vuex-action-and-redirect-to-another-route/18254/4
Example:
export const loginForm = ({ commit }, data) => {
return axios
.post('http://localhost:8000/api/login', data)
.then((response) => {
commit('logUserIn', response.data);
})
.catch((error) => {
commit('unAuthorisedUser', { error:error.response.data });
})
}
Another example:
addEmployee({ commit, state }) {
return insertEmployee(state.employee)
.then(result => {
commit('setEmployee', result.data);
return result.data; // resolve
})
.catch(err => {
throw err.response.data; // reject
})
}
Another example with async-await
async getUser({ commit }) {
try {
const currentUser = await axios.get('/user/current')
commit('setUser', currentUser)
return currentUser
} catch (err) {
commit('setUser', null)
throw 'Unable to fetch current user'
}
},
Actions
ADD_PRODUCT : (context,product) => {
return Axios.post(uri, product).then((response) => {
if (response.status === 'success') {
context.commit('SET_PRODUCT',response.data.data)
}
return response.data
});
});
Component
this.$store.dispatch('ADD_PRODUCT',data).then((res) => {
if (res.status === 'success') {
// write your success actions here....
} else {
// write your error actions here...
}
})
TL:DR; return promises from you actions only when necessary, but DRY chaining the same actions.
For a long time I also though that returning actions contradicts the Vuex cycle of uni-directional data flow.
But, there are EDGE CASES where returning a promise from your actions might be "necessary".
Imagine a situation where an action can be triggered from 2 different components, and each handles the failure case differently.
In that case, one would need to pass the caller component as a parameter to set different flags in the store.
Dumb example
Page where the user can edit the username in navbar and in /profile page (which contains the navbar). Both trigger an action "change username", which is asynchronous.
If the promise fails, the page should only display an error in the component the user was trying to change the username from.
Of course it is a dumb example, but I don't see a way to solve this issue without duplicating code and making the same call in 2 different actions.
actions.js
const axios = require('axios');
const types = require('./types');
export const actions = {
GET_CONTENT({commit}){
axios.get(`${URL}`)
.then(doc =>{
const content = doc.data;
commit(types.SET_CONTENT , content);
setTimeout(() =>{
commit(types.IS_LOADING , false);
} , 1000);
}).catch(err =>{
console.log(err);
});
},
}
home.vue
<script>
import {value , onCreated} from "vue-function-api";
import {useState, useStore} from "#u3u/vue-hooks";
export default {
name: 'home',
setup(){
const store = useStore();
const state = {
...useState(["content" , "isLoading"])
};
onCreated(() =>{
store.value.dispatch("GET_CONTENT" );
});
return{
...state,
}
}
};
</script>
I have a file where I code my whole connection with the REST service, and it works.
From another file, I am executing the following lines (everything works)
this.baseService.getCars(ID)
.subscribe(cars=> this.cars= cars);
To access to the values of the response I was using HTML. For example: *ngIf="cars"
Now, I would like to access by Javascript to the variable doing this:
this.baseService.getCars(ID)
.subscribe(cars=> this.cars= cars);
console.log(this.cars)
but I get undefined but I can access by HTML. I know that it is a stu**d question, but how should I do it? Which variable does contain the variable?
The execution order of those lines of code is not what you think it is.
To see cars in console, change your function to this:
this.baseService.getCars(ID)
.subscribe(cars=>{
this.cars= cars;
console.log(this.cars);
});
You need to place the console.log inside subscribe
this.baseService.getCars(ID)
.subscribe(
cars=> {
this.cars= cars;
console.log(this.cars);
},
error => {
console.log(error);
}
);
Subscribe is asynchronous, like a Promise, but isn't a Promise so, when you execute the code, the subscribe is fired, then the console log. But When the console.log is executing, subscribe is running yet, so that's why you get undefined.
You can do the console.log inside the callback function in subscribe
this.baseService
.getCars(ID)
.subscribe(cars=> {
this.cars = cars
console.log(this.cars)
});
Another solution is to use async/await. You can't use async/await directly with subscribe, because IT'S NOT A PROMISE. Fortunately Observers can be converted to a Promise.
So, in you service you can return a promise, like this:
getCars() {
// your service stuff
return this.api.get(url).toPromise().then( res => res.data); // This is the important part.
}
Then, in your component, call it with async/await:
async yourFunction() {
this.cars = await this.baseService.getCars(ID);
console.log(this.cars);
}
Now you can log this.cars after the getCars()
Hope this helps you.
I have a service in an Angular2 project that takes some parameters and returns a value list to populate drop down menus on a form. When the form component initializes, I need to call the same service multiple times with different parameters to define a number of different dropdown menus, however if I call them all, the last one called clobbers the previous ones, presumably because the subsequent calls are overriding or cancelling the previous fetches.
I've split each of the calls into their own function, but I need a way to call each function in series so that the second doesn't get called until after the first completes. Each function works on its own, however if I call more than one, only the last one succeeds, and the first fail with errors (as the service itself terminates the current fetch when its called with new parameters before finishing).
this.fetchValueListOne();
this.fetchValueListTwo();
this.fetchValueListThree();
I was trying to make this work with promises, but wound up in scoping hell pretty quickly with having to pass the services I wanted to access into the functions and then not being able to get the resulting data back out again - each service call takes three parameters and then sets a specific this.valueList[] variable defined in the component and used on the form.
I also tried creating a list of the functions as variables and then iterating over them, however I ran into the same scoping issues as with promises.
The service returns an Observable, the functions subscribe to that Observable, retrieve the data and assign it to a array variable in the component that a dropdown value list is bound to.
The functions look like this:
fetchValueListOne() {
this.dataSvc.getValueList('Val-List-One', this.stateSvc.currentContext, this.stateSvc.currentLanguageCode)
.map(response => response.json())
.subscribe(
data => {
this.valListOne = data;
},
err => console.log('Error', err),
() => {
console.log('this.valListOne', this.valListOne);
}
);
}
SrAxi pointed me in the right direction, and ultimately I solved the problem as follows where Promises turned out to be the best solution, specifically the Promise / .then mechanism solved the problem nicely.
fetchValueList(listCode): Promise<any> {
return this.dataSvc.getValueList(listCode, this.stateSvc.currentContext, this.stateSvc.currentLanguageCode)
.map(response => response.json())
.toPromise();
}
initializeDropDowns() {
this.fetchValueList('First-Val-List')
.then(data => {
this.firstValList = data;
return this.fetchValueList('Second-Val-List')
}).then(data => {
this.secondValList = data;
return this.fetchValueList('Third-Val-List')
}).then(data => {
this.thirdValList = data;
}) }
I defined the functions in the component, and then called initializeDropDowns() in ngOnInit.
The fetchValueList function returns a Promise, so the first call passes the first listCode and when the Promise resolves, the return value is in the data variable in the .then block where we can assign it to the this.firstValList variable. As the function has returned data, we know the service has finished and it's safe to call again with the second listCode, the return value is in the data variable in the next .then block and we assign it to the this.secondValList variable.
We can chain this as many times as required to populate all the variables, and on the last code block we simply omit the return statement and the block terminates.
This is a very specific use case where we have a single service that needs to be called multiple times as the component initializes, and where the service has to complete its fetch and return a value before it can be called again, but in this case, the Promise / .then method was ideal.
Call the functions when you received the data. Such as:
this.fetchValueListOne().subscribe((firstData) => {
this.fetchValueListTwo(firstData);
// Do something with firstData
}
);
this.fetchValueListTwo().subscribe((secondData) => {
this.fetchValueListThree(secondData);
// Do something with secondData
}
);
this.fetchValueListThree().subscribe((thirdData) => {
// Do something with thirdData
}
);
And declare these functions as Observable, such as:
public fetchValueListOne(): Observable<any> { // Get firstData }
public fetchValueListTwo(): Observable<any> { // Get secondData}
public fetchValueListThree(): Observable<any> { // Get thirdData}
This way you will be certain that when you call a function you have the data from the previous one.