Return axios Promise through Vuex - promise

all!
I need to get axios Promise reject in my vue component using vuex.
I have serviceApi.js file:
export default {
postAddService(service) {
return axios.post('api/services', service);
}
}
my action in vuex:
actions: {
addService(state, service) {
state.commit('setServiceLoadStatus', 1);
ServicesAPI.postAddService(service)
.then( ({data}) => {
state.commit('setServiceLoadStatus', 2);
})
.catch(({response}) => {
state.commit('setServiceLoadStatus', 2);
console.log(response.data.message);
return Promise.reject(response); // <= can't catch this one
});
}
}
and in my vue component:
methods: {
addService() {
this.$store.dispatch('addService', this.service)
.then(() => {
this.forceLeave = true;
this.$router.push({name: 'services'});
this.$store.dispatch('snackbar/fire', {
text: 'New Service has been added',
color: 'success'
}).then()
})
.catch((err) => { // <== This never hapens
this.$store.dispatch('snackbar/fire', {
text: err.response.data.message || err.response.data.error,
color: 'error'
}).then();
});
}
When i use axios directly in my component all work well. I get both success and error messages.
But when i work using vuex i can't get error message in component, hoever in vuex action console.log prints what i need.
I'm always getting only successfull messages, even when bad things hapen on beckend.
How can i handle this situation using vuex ?

Wellcome to stackoverflow. One should not want to expect anything back from an action. After calling an action. Any response should be set/saved in the state via mutations. So rather have an error property on your state. Something like this should work
actions: {
async addService(state, service) {
try {
state.commit('setServiceLoadStatus', 1);
const result = await ServicesAPI.postAddService(service);
state.commit('setServiceLoadStatus', 2);
} catch (error) {
state.commit("error", "Could not add service");
state.commit('setServiceLoadStatus', 2);
console.log(response.data.message);
}
}
}
And in your component you can just have an alert that listens on your state.error
Edit: Only time you are going expect something back from an action is if you want to call other actions synchronously using async /await. In that case the result would be a Promise.

Related

error Policy in Apollo Client React does'nt work

I have aproblem when test Apollo.When I try query with apollo and graphql, i want response return error and partical data, so I set property errorPolicy:'all'. But its not work. I don't no why? Help please!
Here my code:
query { animal {
name
age }, school {
name
numberfd } } `
const { loading,data,error} = useQuery(GET_DASHBOARD_DATA, {
errorPolicy:'all',
onCompleted: (res) => {console.log("complete",res)},
onError : (res,data) => {console.log("ERRRR",res,data)},
})
and i want to receive:
{
error:[...], data:[animal:[...]] }
but its only response error.Here is Apollo's doc: https://www.apollographql.com/docs/react/data/error-handling/
onError type is onError?: (error: ApolloError) => void;. You don't have data inside onError callback.
After useQuery you can add:
console.log('data', data)
console.log('error', error)
I faced the same issue with errorPolicy: 'all', I only received the partial result inside onCompleted callback of useQuery, but no errors.
I created an ErrorLink like this:
private createErrorLink = () => {
return new ApolloLink((operation, forward) => {
return forward(operation).map((response) => {
// filter out errors you don't want to display
const errors = filterSomeErrors(response.errors);
if (errors && response?.data) {
response.data.errors = errors;
}
return response;
});
});
};
Now inside my onCompleted callback I get my data as well as errors. You will have to tweak your types a bit, because seems there is no errors field on response.data by default.
Mind that if you use onError from Apollo and return something from the link, it will retry your request containing errors!

Laravel + Vuejs have trouble with async problem

I have a async problem with my Vuejs/Laravel App.
If a user is connected i have in vuex : my state user and a state token. I set this token in the localStorage and with that i can relogin him if my user F5 (refresh page).
There is my store (namespaced:auth) :
export default {
namespaced: true,
state: {
token: null,
user: null
},
getters: {
authenticated(state){
return state.token && state.user;
},
user(state){
return state.user;
}
},
mutations: {
SET_TOKEN(state, token){
state.token = token;
},
SET_USER(state, data){
state.user = data;
},
},
actions: {
async login({ dispatch }, credentials){
let response = await axios.post(`/api/auth/connexion`, credentials);
dispatch('attempt', response.data.token);
}
,
async attempt({ commit, state }, token) {
if(token){
commit('SET_TOKEN', token);
}
if (!state.token){
return
}
try{
let response = await axios.get(`/api/auth/me`);
commit('SET_USER', response.data);
console.log('Done')
}
catch(err){
commit('SET_TOKEN', null);
commit('SET_USER', null);
}
}
}
}
My app.js, i do that for relog my current user :
store.dispatch('auth/attempt', localStorage.getItem('token'));
And for finish on route in router :
{
name: 'Login',
path: '/',
component: Login,
beforeEnter: (to, from, next) => {
console.log('Getter in router : ' + store.getters['auth/authenticated']);
if (!store.getters['auth/authenticated']){
next()
}else {
return next({
name: 'Home'
})
}
}
}
Where is the problem, if i call in a Home.vue my getter "authenticated" is work fine (true if user connect and false if not).
But in my router is take all time default value set in store (null). I know why is because if i reload my page for 0.5s the store state is null and after that the localStorage is learn and my user and token is created.
What i want (i think) is the router wait my actions attempt and after that he can do check.
I have follow a tutoriel https://www.youtube.com/watch?v=1YGWP-mj6nQ&list=PLfdtiltiRHWF1jqLcNO_2jWJXj9RuSDvY&index=6
And i really don't know what i need to do for my router work fine =(
Thanks a lot if someone can explain me that !
Update :
If i do that is work :
{
name: 'Login',
path: '/',
component: Login,
beforeEnter:async (to, from, next) => {
await store.dispatch('auth/attempt', localStorage.getItem('token'));
if (!store.getters['auth/authenticated']){
next()
}else {
return next({
name: 'Home'
})
}
}
But i want to understand what i really need to do because that is not a clear way is really trash.
Update2:
Maybe i found the best solution if someone have other way i take it.
{
name: 'Home',
path: '/accueil',
component: Home,
beforeEnter: (to, from, next) => {
store.dispatch('auth/attempt', localStorage.getItem('token')).then((response) => {
if (!store.getters['auth/authenticated']){
return next({
name: 'Login'
})
}else {
next();
}
});
}
}
I answer more clearly how easily wait the store !
Before in my app.js i have this :
store.dispatch('auth/attempt', localStorage.getItem('token'));
Is set for normaly every reload do that but if i put a console.log in, i see the router is execute and after the action 'attempt' is execute.
So delete this line in app.js and you can add a .then() after !
router.beforeEach((to, from, next) => {
store.dispatch('auth/attempt', localStorage.getItem('token')).then(() =>{
if (store.getters['auth/authenticated']){
next();
}else {
//something other
}
})
})
With that i have the same line code but i can say wait action "attempt" to my router.
I'm not sur is the best way obviously. But is work and i think is not a bad way !

Nuxt.js and Laravel Api - 422 Displaying Error instead of Forms

[Error][1]
Hi Team,
Whenever I am receiving the error return from laravel the nuxt.js project displays the error on the page instead the HTML/Forms. How can i handle this.
Here is my php code
return response()->json([
'errors' => [
'email' => ['Sorry we cant find you with those details.'],
],
], 422);
Javascript
async submit() {
await this.$auth.loginWith("local", {
data: this.form
})
In your JavaScript you need to wrap your await promise inside a try catch block. Here's a fix for your JS.
try {
await this.$auth.loginWith("local", {
data: this.form
})
} catch (e) {
return;
}
This is an old question at this point, but I thought I'd post the full code since I was pretty stumped and didn't find many great answers out there:
async handleSubmit() {
try {
const authResponse = await this.$auth.loginWith('local', {
data: this.formData
});
const { status, data } = authResponse;
if (status === 200)
this.createFlashAlert({ 'success': 'Login successful' });
} catch (error) {
if (error.response.status === 422)
this.createFlashAlert(error.response.data);
}
}
So the checklist:
Wrap the login call in a try/catch if you're using async await syntax (be sure to make it an async function i.e. async handleSubmit.
in the catch block, use the error.response object, this is an axios thing. With this you'll be able to access the response status and data.
If you log just the error object, it's not obvious that you can access the response within that error which is what had me stumped.

How to make the delay when a user check on Vue.js?

I have SPA application on Vue.js + Laravel. Authorization logic, completely delegated to Laravel app. However, i need check auth status, when routing has changed. I create small class, which responsible for it.
export default {
user: {
authenticated : false
},
check: function(context) {
context.$http.get('/api/v1/user').then((response) => {
if (response.body.user != null) {
this.user.authenticated = true
}
}, (response) =>{
console.log(response)
});
}
Within the component has a method that is called when a change url.
beforeRouteEnter (to, from, next) {
next(vm =>{
Auth.check(vm);
if (!Auth.user.authenticated) {
next({path:'/login'});
}
})
}
Function next() given Vue app instance, then check user object. If user false, next() call again for redirect to login page. All it works, but only when the page is already loaded. If i'll reload /account page, there is a redirect to /login page, because request to Api not completed yet, but code will continue execute. Any idea?
Quite simple to do, you need to make your code work asynchronously and hold routing before request is completed.
export default {
user: {
authenticated : false
},
check: function(context) {
return context.$http.get('/api/v1/user').then((response) => {
if (response.body.user != null) {
this.user.authenticated = true
}
}, (response) => {
console.log(response)
});
}
}
then
beforeRouteEnter (to, from, next) {
next(vm => {
Auth.check(vm).then(() => {
if (!Auth.user.authenticated) {
next({path:'/login'});
} else {
next()
}
}
})
}
Other pro tips
Display some loading indicator when loading so your application doesn't seem to freeze (you can use global router hooks for that)
If you are using vue-resource, consider using interceptors (perhaps in addition to the routing checks)
Consider using router.beforeEach so that you don't have to copy-paste beforeRouteEnter to every component
Done. Need to return promise like that
check: function(context) {
return context.$http.get('/api/v1/user').then((response) => {
if (response.body.user != null) {
this.user.authenticated = true
}
}, (response) =>{
console.log(response)
});
}
and then
beforeRouteEnter (to, from, next) {
Auth.check().then(()=>{
if(!Auth.user.authenticated)
next({path:'/login'})
else
next();
})
}

Make server validation using redux-form and Fetch API

How to make server-side validation using redux-form and Fetch API?
There are "Submit Validation" demo provided in the docs which says that recommended way to do server side validation is to return a promise from the onSubmit function. But where should I place that promise?
As I understood onSubmit function should be my action.
<form onSubmit={this.props.addWidget}>...
Where this.props.addWidget is actually my action, provided below.
import fetch from 'isomorphic-fetch';
...
function fetchAddWidget(widget, workspace) {
return dispatch => {
dispatch(requestAddWidget(widget, workspace));
return fetch.post(`/service/workspace/${workspace}/widget`, widget)
.then(parseJSON)
.then(json => {
dispatch(successAddWidget(json, workspace));
DataManager.handleSubscribes(json);
})
.catch(error => popupErrorMessages(error));
}
}
export function addWidget(data, workspace) {
return (dispatch, getState) => {
return dispatch(fetchAddWidget(data, workspace));
}
}
As you see I use fetch API. I expected that fetch will return promise and redux-form will catch it but that doesn't work. How to make it work with promise from example?
Also from the demo I can not understand what should be provided in this.props.handleSubmit function. Demo does not explain this part, as for me.
Here's my take on using fetch based on the example at http://erikras.github.io/redux-form/#/examples/submit-validation.
...but where should I place that promise?
...what should be provided in this.props.handleSubmit?
The detail is in the comments below; sorry that the code blocks require a bit of scrolling to read :/
components/submitValidation.js
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import { myHandleSubmit, show as showResults } from '../redux/modules/submission';
class SubmitValidationForm extends Component {
// the following three props are all provided by the reduxForm() wrapper / decorator
static propTypes = {
// the field names we passed in the wrapper;
// each field is now an object with properties:
// value, error, touched, dirty, etc
// and methods onFocus, onBlur, etc
fields: PropTypes.object.isRequired,
// handleSubmit is _how_ to handle submission:
// eg, preventDefault, validate, etc
// not _what_ constitutes or follows success or fail.. that's up to us
// I must pass a submit function to this form, but I can either:
// a) import or define a function in this component (see above), then:
// `<form onSubmit={ this.props.handleSubmit(myHandleSubmit) }>`, or
// b) pass that function to this component as
// `<SubmitValidationForm onSubmit={ myHandleSubmit } etc />`, then
// `<form onSubmit={this.props.handleSubmit}>`
handleSubmit: PropTypes.func.isRequired,
// redux-form listens for `reject({_error: 'my error'})`, we receive `this.props.error`
error: PropTypes.string
};
render() {
const { fields: { username, password }, error, handleSubmit } = this.props;
return (
<form onSubmit={ handleSubmit(myHandleSubmit) }>
<input type="text" {...username} />
{
// this can be read as "if touched and error, then render div"
username.touched && username.error && <div className="form-error">{ username.error }</div>
}
<input type="password" {...password} />
{ password.touched && password.error && <div className="form-error">{ password.error }</div> }
{
// this is the generic error, passed through as { _error: 'something wrong' }
error && <div className="text-center text-danger">{ error }</div>
}
// not sure why in the example #erikras uses
// `onClick={ handleSubmit }` here.. I suspect a typo.
// because I'm using `type="submit"` this button will trigger onSubmit
<button type="submit">Log In</button>
</form>
);
}
}
// this is the Higher Order Component I've been referring to
// as the wrapper, and it may also be written as a #decorator
export default reduxForm({
form: 'submitValidation',
fields: ['username', 'password'] // we send only field names here
})(SubmitValidationForm);
../redux/modules/submission.js
// (assume appropriate imports)
function postToApi(values) {
return fetch( API_ENDPOINT, {
credentials: 'include',
mode: 'cors',
method: 'post',
body: JSON.stringify({values}),
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': CSRF_TOKEN
}
}).then( response => Promise.all([ response, response.json()] ));
}
export const myHandleSubmit = (values, dispatch) => {
dispatch(startLoading());
return new Promise((resolve, reject) => {
// postToApi is a wrapper around fetch
postToApi(values)
.then(([ response, json ]) => {
dispatch(stopLoading());
// your statuses may be different, I only care about 202 and 400
if (response.status === 202) {
dispatch(showResults(values));
resolve();
}
else if (response.status === 400) {
// here I expect that the server will return the shape:
// {
// username: 'User does not exist',
// password: 'Wrong password',
// _error: 'Login failed!'
// }
reject(json.errors);
}
else {
// we're not sure what happened, but handle it:
// our Error will get passed straight to `.catch()`
throw(new Error('Something went horribly wrong!'));
}
})
.catch( error => {
// Otherwise unhandled server error
dispatch(stopLoading());
reject({ _error: error });
});
});
};
Please chime in with comments if I've missed something / misinterpreted, etc, and I'll amend :)
It turned out that there are undocumented property returnRejectedSubmitPromise which must be set to true.

Resources