Partial observable handling in Angular 2 - angularjs

I have a component which a login form: loginForm which calls my api:
onLogin(): void {
let username = this.loginForm.value.username;
let password = this.loginForm.value.password;
this.api.login(username, password);
.map(res => res.json())
.subscribe(
this._processData,
);
}
There is a service which calls an api: loginService.
login(username: string, password: string): Subscription<any> {
let headers = new Headers();
headers.append("Content-Type", "application/json");
return this.http.put("https://myapi.com", JSON.stringify({ username: username, password: password}), {
headers: headers
})
.map(res => res.json())
.subscribe(
data => console.log(data),
err => console.log(err),
() => console.log("Done")
);
}
What I want is the error to be handled inside the loginService. But the result back to the onLogin method.
I tried a variety of mapping handlers (as shown) in the onLogin but could not get it to work. Any thoughts about what I might do wrong?

In fact, you define a callback within the subscribe method to handle error in the login method (the second parameter). If you want to let errors to be propagated into your onLogin method you need to remove this callback (and even the call of subscribe).
return this.http.put("https://myapi.com",
JSON.stringify({ username: username, password: password}), {
headers: headers
})
.map(res => res.json());
If you want to log the error and then propagate it you can leverage the Observable.throw method:
return this.http.put("https://myapi.com",
JSON.stringify({ username: username, password: password}), {
headers: headers
})
.map(res => res.json())
.catch(err => {
console.log(err);
return Observable.throw(err);
});
In this case you need to add a callback to handle the error in the onLogin method:
onLogin(): void {
let username = this.loginForm.value.username;
let password = this.loginForm.value.password;
this.api.login(username, password)
.subscribe(
(data) => {
// Success
this._processData(data)
},
(err) => {
// Error
}
);
}
You can notice that there is a catch operator on Observable to catch error and in the case of errors, the map operator isn't called.
These answers could give you more details:
Angular2 http observables - how to work with undefined type
Observable Map function not running (Angular2, http)
Hope it helps you,
Thierry

You can define your error handler in your service, and just pass the error from component where you subscribe.
// component
onLogin(): void {
let username = this.loginForm.value.username;
let password = this.loginForm.value.password;
this.api
.login(username, password)
.subscribe(
this._processData, // success
this.api.onError // error
// if above doesn't work this should:
// error => this.api.onError(error)
);
}
_processData(...data) { console.log(data); }
// service
login(username: string, password: string): Subscription<any> {
let headers = new Headers();
headers.append("Content-Type", "application/json");
return this.http
.put("https://myapi.com", JSON.stringify({ username: username, password: password}), { headers: headers })
.map(res => res.json())
}
onError(...args) { console.log(args); }

Related

Access-Control-Allow-Origin Request blocked from Localhost:3000 on firebase

I am getting an error on post method of axios for the code:
ERROR :
Access to XMLHttpRequest at 'https://identitytoolkit.googleapis.com/v1/signupNewUser?key=AIzaSyAHO' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
CODE:
const authData = {
email: email,
password: password,
returnSecureToken:true
}
axios.post('https://identitytoolkit.googleapis.com/v1/signupNewUser?key=AIzaSyAHOBs', authData)
.then( response => {
console.log(response);
dispatch(authSuccess(response.data));
})
.catch(err => {
console.log(err);
dispatch(authFail(err));
});
I was using different URL for post method, ie
https://identitytoolkit.googleapis.com/v1/signupNewUser?key=[API_KEY]
But the correct URL is same you posted and it even works without modification of headers :
https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]
code snippets :
export const auth = (email, password) => {
return (dispatch) => {
dispatch(authStart());
const authData = {
email: email,
password: password,
returnSecureToken: true,
};
const config = {
headers: {
"Content-Type": "application/json",
},
};
axios.post(
"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyAHOBsv31rdWYhCr15gRS727QuCnajk2CU",
authData,
config
)
.then((response) => {
console.log(response.data);
dispatch(authSuccess(response.data));
})
.catch((err) => {
console.log(err);
dispatch(authFail(err));
});
};
};
thank you so much
Happy coding :)
I am doing the same course and facing the same error.
after a long time, I found that the problem was where we call the function (Auth.js file).
while we handle the onSubmit method we call the onAuth method from mapDispatchToProps like this
const mapDispatchToProps = dispatch =>{
return {
onAuth: (email, pass) => dispatch(actions.auth(email,pass))
}
}
on the course video, the submit handler was like this. that was not correct and indicating an object instead of a string value.
submitHandler = (event) => {
event.preventDefault();
this.props.onAuth(this.state.controls.email, this.state.controls.password);
}
the correct handler would be like this ...
submitHandler = (event) => {
event.preventDefault();
this.props.onAuth(this.state.controls.email.value, this.state.controls.password.value);
}

How to send token through headers by using axios post method in react

In my react app i am using axios to perform the REST api requests.
But it's unable to send the Authorization header with the request.
Here is my code:
This is authentication.js
async login(data) {
try {
const res = await axios.post(`'http://localhost:5000'/api/login`, data);
this.subject.next(true);
return res;
} catch (error) {
throw error;
}
}
This is login.js
async handleSubmit(e) {
e.preventDefault();
try {
const res = await auth.login(this.state.data);
tokenService.saveToken(res.data.token);
this.setState({});
swal({
title: "Good job!",
text: "Login successfully!",
icon: "success",
});
}
catch (error) {
swal({
title: "incorrect or password!",
text: "Login failed!",
icon: "error",
});
}
}
You can use Axios to create an instance of it with the headers passed to it save in local storage. Then, use that instance to further make requests. In this way, you don't to include it in every request.
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
headers: {'Authorization': bearer <TOKEN_FROM_LOCALSTORAGE>}
});
Use the instance to make request
instance.get("users")
.then(res => {
console.log(res);
console.log(res.data);
})
You can use this instance and customize it according to your instance so that code won't repeat. For further reference
Store it in localstorage and then concatenate it with 'Bearer'
let bearer = 'Bearer ' + JSON.parse(localStorage.getItem('token'));
//payload is the data which you're trying to send to the api endpoint
axios({
method: 'post',
url: '/api-endpoint',
headers: {
Authorization: bearer
},
data: payload
})
.then(response => response.json())
.then(json => json)
.catch(error => {
throw error;
});
check if the user is authenticated to use the Get or Post requests made by them
isAuthenticated() {
const token = localStorage.getItem('token');
}
Use the token to make the post request
axios({
method: 'post',
url: ''http://localhost:5000'/api/login',
{ headers: {"authorization" : token} }
data: payload
}),
.then(response => response.json())
.then(json => json)
.catch(error => {
throw error;
});
Handle your login
async handleSubmit(e) {
e.preventDefault();
try {
const res = await auth.login(this.state.data);
tokenService.saveToken(res.data.token);
this.setState({});
swal({
title: "Good job!",
text: "Login successfully!",
icon: "success",
});
}
catch (error) {
swal({
title: "incorrect or password!",
text: "Login failed!",
icon: "error",
});
}
}
Why you don't use axios interceptors like this:
axiosInstance.interceptors.request.use(
config => {
config.headers.authorization = localStorage.getItem("token");
return config;
},
error => Promise.reject(error)
);
Or declared on https://github.com/axios/axios/issues/1383

Send parameter not in URL to API

I send a request to my API with advancedFetch. This works, however, I want to know if it is possible to send a parameter without defining it in the URL. Are there any smart ways of doing this?
I tried researching it but I'm not sure I'm using the right keywords for what I'm trying to do.
This is where I set off my request (the value is from a modal input field):
setNewUserName(userName) {
this.setState({newUserName: userName});
advancedFetch('/api/users', {
method: 'POST',
body: JSON.stringify({}),
credentials: 'include',
// I've tried sending the param here
userName: userName,
headers: {
'Content-Type': 'application/json'
}
})
.then(() => {
this.loadUsers();
})
.catch(err => {
//error handling
});
}
In my controller I defined the route and implemented function like this:
index.create = (req, res, next) => {
let userName = req.params.userName;
console.log(userName);
user
.create(userName)
.then((response) => {
res.send(response);
})
.catch((err) => {
next(err);
});
};
router.post('/users', index.create);
And then in my service.js I write the data to my database:
create: function(userName){
userName = userName
return query(`INSERT INTO ${tableName} (user) VALUES (?, ?)`, [1, userName])
.catch(err => {
app.logger.error('[Users] failed to create a user', err.message);
return Promise.reject(new Error('failed to create user'));
});
},
I always get an undefined userName, do I have to create a route with the value at the end?
You're receiving userName as undefined, because you're sending the request with JSON-encoded data, rather than URL parameters and the same logic doesn't apply for that case.
Luckily there's an easy way to solve your problem, using expressjs body-parser package. It's very easy to use.
This is how you initialize it:
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
// parse application/json
app.use(bodyParser.json())
And this is how you would read "userName" in your router function:
index.create = (req, res, next) => {
let userName = req.body.userName;
console.log(userName);
user
.create(userName)
.then((response) => {
res.send(response);
})
.catch((err) => {
next(err);
});
};
And btw, when you're calling advancedFetch, you should actually be doing this:
body: JSON.stringify({newUserName: userName}),
Yes, you can actually do it this way:
In the frontend call you can send the parameter through the body like so
body: JSON.stringify({userName: userName})
And then in your controller what you want to do is to directly access the paramter from the body:
let userName = req.body.userName;
And now it's not undefined anymore :)

How to retrieve data from promise object in React JS

I am trying to get data from an API. But the fetch result is returned as promise object. I want to return the contents from this promise to invoke react action.
let loginData = fetch(loginURL, { method : 'POST', headers : headerParams,
body: bodyParams })
.then((response) => response.json())
.then(data => {
return data['retrieve-agent'];
});
console.log('loginData ===>', loginData.agent);
return {
type: 'GET_AGENT_DETAILS',
payload: loginData
}
Make use of async-await to get the result without using a promise or else you would need to resolve the promise from the function
async fetchFunction() {
let loginData = await fetch(loginURL, { method : 'POST', headers : headerParams,
body: bodyParams })
.then((response) => response.json())
.then(data => {
return data['retrieve-agent'];
});
console.log('loginData ===>', loginData.agent);
return {
type: 'GET_AGENT_DETAILS',
payload: loginData
}
}

history.push not working in fetch callback

I am working on simple react js application where I am authenticating user and if he/she has successfully logged in I am trying to redirect to home page but I am in some strange situation. Please help me through the below code.
Below is the code for a function fetchAPI to call to server with some input params
function fetchAPI(methodType, url, data, callback){
fetch(url,{
method: methodType,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => callback(data) )
.catch(error => callback(data));
}
Now I am calling it this way
fetchAPI("POST", Constants.LOGIN, data, function(callback) {
if(callback.status == 200) {
console.log(callback.message);
this.props.history.push("/home");
}else if( typeof callback.status != "undefined"){
alertModal("Alert", callback.message);
}
});
The problem with this is that its not redirecting to /home as mention in response condition but only prints success message.
But when I directly use the fetch api like below code it redirects me to /home
can anybody help me out why is this happening with me ??
fetch(Constants.LOGIN, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
if (data.status == 200) {
this.props.history.push("/home");
} else if (typeof data.status != "undefined") {
alertModal("Alert", data.message);
}
})
.catch(error => callback(data));
OK, forget about callbacks, I've been there, no more CALLBACK HELL.
Use promises always, and you can simplify everything by using async/await:
async function fetchAPI(methodType, url, data){
try {
let result = await fetch(url, {
method: methodType,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}); // wait until request is done
let responseOK = response && response.ok;
if (responseOK) {
let data = await response.json();
// do something with data
return data;
} else {
return response;
}
} catch (error) {
// log your error, you can also return it to handle it in your calling function
}
}
in your React Component:
async someFunction(){
let result = await fetchAPI("POST", Constants.LOGIN, data); // wait for the fetch to complete
if (!result.error){
// get whatever you need from 'result'
this.props.history.push("/home");
} else {
// show error from 'result.error'
}
}
Now your code looks more readable!
The errors in fetch are in either result.error or result.statusText, I stopped using fetch a long time ago, switched to Axios. Have a look at my answer on some differences between the 2 Here.
EDIT BASED ON YOUR RESPONSE
Ok, based on the code you posted:
import React from "react";
import Constants from "../Constants.jsx";
import { withRouter } from "react-router-dom";
class Login extends React.Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
errors: []
};
}
showValidationErr(elm, msg) {
this.setState(prevState => ({
errors: [...prevState.errors, { elm, msg }]
}));
}
clearValidationErr(elm) {
this.setState(prevState => {
let newArr = [];
for (let err of prevState.errors) {
if (elm != err.elm) {
newArr.push(err);
}
}
return { errors: newArr };
});
}
onEmailChange(e) {
this.setState({ email: e.target.value });
this.clearValidationErr("email");
}
onPasswordChange(e) {
this.setState({ password: e.target.value });
this.clearValidationErr("password");
}
submitLogin(e) {
e.preventDefault();
const { email, password } = this.state;
if (email == "") {
this.showValidationErr("email", "Email field cannot be empty");
}
if (password == "") {
this.showValidationErr("password", "Password field cannot be empty");
}
if (email != "" && password != "") {
var data = {
username: this.state.email,
password: this.state.password
};
// I added function keyword between the below line
async function someFunction(){
let result = await fetchAPI("POST", Constants.LOGIN, data); // wait for the fetch to complete
if (!result.error){
this.props.history.push("/home"); // Here is the error
} else {
// show error from 'result.error'
}
}
someFunction();
}
}
render() { ......................
####-----This is function definition------####
async function fetchAPI(methodType, url, data){
try {
let response = await fetch(url, {
method: methodType,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}); // wait until request is done
let responseOK = response && response.ok;
if (responseOK) {
let data = await response.json();
// do something with data
return data;
} else {
return response;
}
} catch (error) {
return error;
// log your error, you can also return it to handle it in your calling function
}
}
This is the idea, you should make async the function that is calling the API. In your example, your function submitLogin has to be async since it will call an async function inside. As long as you call an async function, the caller MUST be async, or handle the promise accordingly. This is how it should be:
async submitLogin(e) {
e.preventDefault();
const { email, password } = this.state;
if (email == "") {
this.showValidationErr("email", "Email field cannot be empty");
}
if (password == "") {
this.showValidationErr("password", "Password field cannot be empty");
}
if (email != "" && password != "") {
var data = {
username: this.state.email,
password: this.state.password
};
let result = await fetchAPI("POST", Constants.LOGIN, data); // wait for the fetch to complete
if (!result.error) {
this.props.history.push("/home"); // Here is the error
} else {
// show error from 'result.error'
}
}
If the function is correctly bound in the constructor, you won't have any problems with this. It seems that you are not binding the submitLogin function in the constructor, which will give you problems with the context of this. This is how it should be bound:
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
errors: []
};
// bind all functions used in render
this.submitLogin = this.submitLogin.bind(this);
}
Have a look at this article to learn more about the problem with the context of this.
Now, based on the code you provided, seems to me that you are in uncharted territory. If you think that you are finding the routing hard or the async/await is not clear, I suggest you don't use them, and first master the React basics (the syntax problem you are having is an example, you shouldn't have put that function there, also the binding issue with this).
Have a read at this post for example, to have a general idea and I also suggest you try other more simple examples before using async, fetch, or routing. When you get the React lifecycle clear, you can continue from there, and use async functions, and then routers.
I also suggest you follow the examples in the Official docs and also have a look at this post to have a better understanding of async/await.
These suggestions are of course given so that you get to master React with clear fundamentals, and in the future don't have any problems with the basics! :)

Resources