I would like to take a reponse from post metho and use this value in subscribe method.
In comments I wrote also information about my question.
My code
login(): void {
this.authService.login(this.model)
.subscribe(next => {
//here I wanted to use received value
this.alertifyService.success('success');
}, error => {
this.alertifyService.error('error');
}
);
}
and
login(model: any) {
return this.http.post(this.baseUrl + 'login', model)
.pipe(map((response: any) => {
// how to send this reponse to my subscribe method ?
const user = response;
if (user) {
localStorage.setItem('token', user.token);
this.decodedToken = this.jwtHelper.decodeToken(user.token);
console.log(this.decodedToken);
}
}
));
}
you just have to return the value from map
login(model: any) {
return this.http.post(this.baseUrl + 'login', model)
.pipe(map((response: any) => {
const user = response;
if (user) {
localStorage.setItem('token', user.token);
this.decodedToken = this.jwtHelper.decodeToken(user.token);
return this.decodedToken;
}
}
));
}
and then use it like this:
authService.login(/**/).subscribe(token => /*do something with the token*/)
Related
I want to create a method that returns an auth header from a token stored in the local storage so it can be called from many ajax, like this:
const fetchUserInfo = (username) => {
const requestString = apiBaseURL + 'access/user_info/'
const form = {username: username}
return axios.post(requestString, form, getAuthHeader())
.then((Response) => {
return {
userInfo: Response.data,
message: null
}
})
.catch((Error) => {
return getErrorMessage();
});
}
In this case, getAuthHeader() is a method that must return a proper authorization header from a token stored in the local storage, so it must use asyncStorage in order to retrieve such token.
This is the code for getAuthHeader:
export const getAuthHeader = async () => {
return await AsyncStorage.getItem('token')
.then ((token) => {
return {
headers: {'Authorization' : 'Token ' + token}
}
})
.catch ((error) => null)
}
The problem is that getAuthHeader() is not returning a header but some other object. I believe I am messing up with the asynchronous nature of asyncStorage, but I can't figure out how to get the value I need.
Yes, you are using async/await and .then (native) together. async/await already handles the .then syntax for you without you having to do it. So you do not need both together it's 1 or the other. Both examples below.
Async/Await:
export const getAuthHeader = async () => {
try {
const token = await AsyncStorage.getItem('token')
if (!token) { return null }
return {
headers: {'Authorization' : 'Token ' + token}
}
} catch(error) { return null }
}
Native
export const getAuthHeader = () => {
return AsyncStorage.getItem('token')
.then((token) => {
if (!token) { return null }
return {
headers: {'Authorization' : 'Token ' + token}
}
})
.catch ((error) => null)
}
also you need to await getAuthHeader in your first method else it will never resolve that value:
const fetchUserInfo = async (username) => {
const requestString = apiBaseURL + 'access/user_info/'
const form = {username: username}
try {
const response = await axios.post(requestString, form, await getAuthHeader())
return {
userInfo: response.data,
message: null
}
} catch(error) {
return getErrorMessage();
}
}
I'm developing a ReactJS app.
I'm getting the following error "TypeError: undefined is not an object (evaluating '_this.props.auth(values.username, values.password).then')".
When the "return new Promise" is outside the "then" it works properly. Nonetheless, I want to return the promise after only the two first "then"s.
Sample of loginActions.js
export const auth = (username, password) => dispatch => {
fetch('http://localhost/webservices/login', {
method: 'post',
body: JSON.stringify({ username, password })
})
.then(res => {
if(res.ok) {
console.log("Succeeded.", res);
return res.json();
} else {
console.log("Failed.", res);
return res.json();
}
})
.then(json => {
if (json.token) {
auth_status.value = true;
return auth_status.value;
} else {
auth_status.value = false;
return auth_status.value;
}
})
.then(function(res){
return new Promise((resolve, reject) => {
dispatch({
type: VERIFY_AUTH,
payload: res
});
resolve();
})
})
.catch(err => {
console.error(err);
});
};
Sample of login.js
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log("Received values of form: ", values);
this.props.auth(values.username, values.password).then(() => {
if (this.props.auth_status === true) {
message.success("Welcome!", 3);
this.setState({
redirect: true
});
} else {
message.error("The username and password combination is incorrect", 3);
}
})
.catch(err => {
console.error(err);
});
}
});
};
Your action auth is not returning anything. The return statements in the asynchronous handlers do not return for the action itself.
You need to return a Promise in your auth() action that you resolve yourself in the third then:
export const auth = (username, password) => dispatch => {
// instantly return a new promise that
// can be resolved/rejected in one of the handlers
return new Promise((resolve, reject) => {
fetch('http://localhost/webservices/login', {
method: 'post',
body: JSON.stringify({
username,
password
})
}).then(res => {
if (res.ok) return res.json();
// your probably also want to reject here
// to handle the failing of the action
reject();
}).then(json => {
if (json.token) {
auth_status.value = true;
return auth_status.value;
} else {
auth_status.value = false;
return auth_status.value;
}
}).then(res => {
dispatch({
type: VERIFY_AUTH,
payload: res
});
// resolve the original promise here
resolve();
}).catch(err => console.error(err));
});
};
I want to send this.data as a parameter for the post request but when I put a console.log(this.data) before the return statement, it returns both token and regNo are null values but inside the then method of storage get, console.log(this.data) gives the correct value. What is going wrong here?
import { Injectable } from "#angular/core";
import { Http } from '#angular/http';
import { Storage } from '#ionic/storage';
import 'rxjs/add/operator/toPromise';
import { DiaryModel } from './diary.model';
#Injectable()
export class DiaryService {
constructor(public http: Http, public storage: Storage) {}
data: any = {token: null, regNo: null};
getData(): Promise<DiaryModel> {
this.storage.get('regNo').then((val) => {
console.log(val);
this.data.regNo = val;
this.storage.get('user').then((val2) => {
console.log(val2);
this.data.token = val2.token;
});
});
return this.http.post("http://www.mysite.xyz/services/service.php", this.data)
.toPromise()
.then(response => response.json() as DiaryModel)
.catch(this.handleError);
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}
}
Ionic works as a non blocking I/O model. In your code return statement will run before getting the value from storage methods. You have to make the storage methods and return statement synchronous so return statement wait until the storage method resolves the value.
getData(): Promise<DiaryModel> {
return new Promise((resolve) =>{
data: any = {token: null, regNo: null};
this.storage.get('regNo').then((val) => {
console.log(val);
this.data.regNo = val;
this.storage.get('user').then((val2) => {
console.log(val2);
this.data.token = val2.token;
});
});
resolve(data);
});
}
returnData(): Promise<any> {
this.Data().then(res => {
return this.http.post("http://www.mysite.xyz/services/service.php", this.data)
.toPromise()
.then(response => response.json() as DiaryModel)
.catch(this.handleError);
}
}
Then you can call the returnData() method to get the return statement.
You need to chain the promises to get data sequentially. Since they are asynchronous.
Your http request is being sent before storage returns value.
I would do :
getData(): Promise<DiaryModel> {
let regPromise = this.storage.get('regNo');
let tokenPromise = this.storage.get('user');
return Promise.all([regPromise,tokenPromise]).then(values=>{
this.data.regNo=values[0];
this.data.token = values[1].token;
return this.http.post("http://www.mysite.xyz/services/service.php", this.data)
.toPromise()
.then(response => response.json() as DiaryModel)
.catch(this.handleError);
})
I have a method in my component that uses observable to listen to first event and has a timeout of 15s. The method calls a nested observable that should throw an error if the parameter is null or empty. I use Observable.throw() but the error only propagates after the timeout/15s.
this.signUp(this.user)
.first()
.timeout(15000)
.subscribe((authResponse) => {
console.log("next fired");
dialogs.alert("Next: " + authResponse);
}, (error) => {
//fired only after 15s when mobile is empty
console.log("error fired");
console.log(JSON.stringify(error));
}, () => {
console.log("completed fired");
});
signUp()
public signUp(user: User): Observable<AuthResponse> {
let observable = new Subject<AuthResponse>();
this.isUserExisting(user.mobile)
.first()
.subscribe((isUserExisting) => {
if (isUserExisting) {
console.log("User already exists");
observable.next(AuthResponse.USER_EXISTING);
} else {
console.log("User does not exist");
this.saveUser(user).first().subscribe(() => {
observable.next(AuthResponse.SUCCESS);
})
}
}, (error) => {
return Observable.throw(error);
})
return observable;
}
public isUserExisting(mobile: string): Observable<boolean> {
let observable = new Subject<boolean>();
if (!mobile) {
console.log("empty mobile");
return Observable.throw(new Error("Mobile number cannot be empty"));
}
firebase.query(() => { }, "/users",
{
singleEvent: true,
orderBy: {
type: firebase.QueryOrderByType.CHILD,
value: "mobile"
},
range: {
type: firebase.QueryRangeType.EQUAL_TO,
value: mobile
}
}
).then((result) => {
console.log("Checking for user success: ");
console.log(JSON.stringify(result));
observable.next(result.value != null);
});
return observable;
}
Update:
saveUser()
public saveUser(user: User) {
return Observable.defer(() => firebase.push('/users', user)
.then((result) => {
console.log("Created user record with key: " + result.key);
console.log("Dumping result:");
console.log(JSON.stringify(result));
})
.catch((error) => {
console.log("Error while saving user: " + error);
})
)
}
It is a bad practice to create your own subject on which you will emit observable values which have been emitted by an internal subscribed observable. This leads to memory leaks because you have no way to unsubscribe from your internal subscription.
public signUp(user: User): Observable<AuthResponse> {
return this.isUserExisting(user.mobile)
.flatMap(isExistingUser => {
if (isExistingUser) {
console.log('user already exists: ' + isUserExisting);
return Rx.Obserable.of(AuthResponse.USER_EXISTING);
}
return saveUser(user).map(result => AuthResponse.SUCCESS)
})
.catch(err => {
console.log(`error during signup of user: ${user.mobile})`);
return Rx.Observable.of(AuthResponse.FAILED_TO_SIGNUP);
})
.first();
}
Without the subscription in your signUp function your code can unsubscribe from it and errors are propagated without any work on your side. This is also the reason your timeout is hit instead of your mobile number check; you forgot to propagate the error. Instead you tried to return the Rx.Observable.throw from within your subscribeOnError callback, which has a signature of onError(error):void
A good practice is to keep your Rx functions lazy; only execute the code inside when somebody subscribes to it. This helps in reducing hard to trace errors. In your case using the firebase.query() (which returns a promise) you can use .defer() to wait with executing the promise until somebody subscribes to your isUserExisting function.
public isUserExisting(mobile: string): Observable<boolean> {
if (!mobile) {
console.log("empty mobile");
return Observable.throw(new Error("Mobile number cannot be empty"));
}
return Rx.Observable.defer(() => firebase.query(() => { }, "/users",
{
singleEvent: true,
orderBy: {
type: firebase.QueryOrderByType.CHILD,
value: "mobile"
},
range: {
type: firebase.QueryRangeType.EQUAL_TO,
value: mobile
}
}
))
.do(firebaseResult => console.log('Checking for user success: ' + JSON.stringify(firebaseResult)))
.filter(firebaseResult => firebaseResult.value != null);
}
public saveUser(user: User) {
return Observable.defer(() => firebase.push('/users', user))
.do(result => console.log(`created user with key ${result.key}`))
}
Trying to figure out some basic authentication with Angular 2. I want my component to check for a token and if it exists navigate to the proper location, but if it doesn't display the error message returned by the service. Here is the the service call:
this._loginService.login(loginQuery)
.subscribe(
(token: any) => this._router.navigate( ['User', { username: user }] ),
(data) => { this.errorMessage = data.ErrorMessage; }
)
Here is the code for the service:
return this._http.post('serivcelink,
body,
{headers:headers})
.map((res : any) => {
let data = res.json();
this.token = data.TeamMember.UserName;
localStorage.setItem('token', this.token);
});
I get the token behavior I want except that I don't get access to the response object in the component, and as such can't display the error message.
How do can I check for the token and get access to the response data?
You can use an if statement to check if your token exists, else display an error message:
this._loginService.login(loginQuery)
.subscribe(
(data: any) => {
if (localStorage.getItem('token')) {
this._router.navigate( ['User', { username: user }] )
} else {
this.errorMessage = data.ErrorMessage;
}
}
)
You need to return something within the map callback, i.e. this.token in your case:
return this._http.post('serivcelink,
body,
{headers:headers})
.map((res : any) => {
let data = res.json();
this.token = data.TeamMember.UserName;
localStorage.setItem('token', this.token);
return this.token; // <-------
});
What you return will be receive as parameter into the callback of the subscribe method.