using return of async function in view() - mithril.js

I previously had a simple component that looked something like this, but previously the getIsLoggedIn method was synchronous. We recently had to change that method to async, so I tried adding the await keyword to it and changing the view() to be async view(), but this doesnt seem to work:
const welcomePageComponent = {
messages: {
msg1: 'message if logged in',
msg2: 'message if not logged in',
},
view({ state }) {
let isLoggedIn = getIsloggedIn();
let myMsg = isLoggedIn ? this.messages.msg1 : this.messages.msg2;
return m('#welcome', [
m('.semi-trans-blk-panel', [
m('.welcome-wrapper', [
m('h4.welcomeMsg', [
m('br'), myMsg
])
])
])
]);
}
}
How can I go about calling an async function and using its return value inside my view()?

The problem is that the application view must be computed synchronously on each draw; if a promise returned by a view were to trigger a new view computation on resolution, the next computation would still resolve to a promise, so you'd be stuck in a loop.
The solution is to instantiate an extra data point to track whether the promise has resolved, and an oninit lifecycle method to call the promise and change the relevant data once, on initialisation, and then redraw. Finally, we modify the view function to return nothing unless the authentication data is available.
const welcomePageComponent = {
messages: {
msg1: 'message if logged in',
msg2: 'message if not logged in',
},
isLoggedIn: undefined,
isResolved: false,
async oninit({ state }) {
state.isLoggedIn = await getIsLoggedIn();
state.isResolved = true;
},
view({ state }) {
const myMsg = isLoggedIn ? state.messages.msg1 : state.messages.msg2;
return (
state.isLoggedIn &&
m('#welcome', [
m('.semi-trans-blk-panel', [
m('.welcome-wrapper', [
m('h4.welcomeMsg', [
m('br'), myMsg
])
])
])
])
)
}
}

Related

How Do I Update A Database Column In Shopware Via The Administration

I have created an administration page in the Administration and I want to update information in the database when the page is loaded (in Vue js term CREATED). My code below does not do anything and I can not find any error. Help fix my code and how do I get errors from Shopware Administration.
const { Component, Mixin } = Shopware;
import template from './store-settings-page.html.twig'
Component.register('store-settings-page', {
template,
inject: [
'repositoryFactory'
],
metaInfo() {
return {
title: this.$createTitle()
};
},
data: function () {
return {
entity: undefined,
entityId: '4e2891496c4e4587a3a7efe587fc8c80',
}
},
computed: {
storeKeysRepository() {
return this.repositoryFactory.create('store_keys');
},
},
created() {
this.storeKeysRepository
.get(this.entityId, Shopware.Context.api)
.then(entity => {
this.entity = entity;
});
/* const repository = this.storeKeysRepository();
this.entity = repository.create(Shopware.Context.api);
this.entity.name = 'Diekedie';
repository.save(this.entity, Shopware.Context.api);
*/
// a function which is called over the ui
this.entity.name = 'updated';
// sends the request immediately
this.storeKeysRepository
.save(this.entity, Shopware.Context.api)
.then(() => {
// the entity is stateless, the data has be fetched from the server, if required
this.storeKeysRepository
.get(this.entityId, Shopware.Context.api)
.then(entity => {
this.entity = entity;
});
});
},
});
Looks like you're not awaiting the fetch request meaning your entity would still be undefined when it reaches the save call on the repository.
You should move the save call inside the chained method of the first request. Also unless you have some fields which are indexed or computed server side, you might not need to refetch the entity after the successful save call.
this.storeKeysRepository.get(this.entityId, Shopware.Context.api).then((entity) => {
this.entity = entity;
this.entity.name = 'updated';
this.storeKeysRepository.save(this.entity, Shopware.Context.api);
});

Multiple commits in vuex action not triggering mutation

I'm attempting to set a loading status on a component which will display a loading graphic while the request is being processed. I have this working with a call to an API, returning a promise from a fetch to a REST interface. However, when attempting this with a simple call to a service it only seems to call the last mutation
In the vuex store:
var initialState = {
status: { loading: false },
all_clients: [],
current_client: {
loaded: false,
}
}
export const fund = {
namespaced: true,
state: initialState,
actions: {
getClientOverview({ commit }, client_group_id) {
commit('loadingRequest');
clientService.getClientOverview(client_group_id)
.then(
clientInfo => {
commit('loadingSuccess')
},
error => {
commit('loadingError', error);
}
)
}
},
mutations: {
// Mutations to set loading and error statuses
loadingRequest(state) {
state.status = { loading: true };
},
loadingSuccess(state) {
state.status = { loading: false };
},
loadingError(state,error) {
state.status = { loading: false };
store.dispatch('alert/error', "Error Loading Client: " + error)
},
}
and in the corresponding client service that is called:
function getClientOverview(client_group_id) {
return new Promise((resolve,reject) => {
try {
// Note here that client.state.all_clients is an array of objects already set in the store
let clientDetail = client.state.all_clients.filter(function(item) {
return item.client_group_id === client_group_id;
});
// THIS PAUSE CODE JUST ADDED TO SLOW THINGS DOWN A BIT ON FRONT END
// SO I CAN SEE IF MUTATIONS ARE WORKING CORRECTLY
var d = new Date();
var d2 = null;
do { d2 = new Date(); }
while(d2-d < 2000);
// TO HERE
if(clientDetail.length == 0){
reject("Client Information not found")
}
resolve(clientDetail[0])
}catch(error){
reject(error)
}
})
}
A client is being returned as expected, as a resolved promise, but I only see 'loading' being set to false on the front end, it doesn't set to true while the service is running its process.
If I remove the commit('loadingSuccess') line then I can see the original commit being called as expected and loading being set to true - it seems as though only one commit is being called per action
Any help with this would be gratefully appreciated!
Thanks

Is way i can react to an asynchronous action in vuex in a vue template

I want to display a loading effect in a vue template whilst an asynchronous action in vuex is still running. But the loading effect doesn't seem to work. How do I fix it?. Is there any better way I can achieve this?
This is how I defined the action:
actions: {
signIn({ state }, user) {
auth()
.signInWithEmailAndPassword(userInfo.email, userInfo.password)
.then(result => {
return result
})
},
},
This how defined the dispatch in vue template method:
let loader = this.$loader.show()
this.$store.dispatch('signIn', this.user).then(() => {
loader.hide()
})
I expected the loader to start when the action begins and end when the action ends but it starts and ends almost instantly.
Just add return statement, that returns a Promise so you can then it in your component.
actions: {
signIn({ state }, user) {
return auth()
.signInWithEmailAndPassword(userInfo.email, userInfo.password)
.then(result => {
return result
})
},
},

VueJS data doesnt change on URL change

My problem is that when I go from one user page to another user page the info in component still remains from first user. So if I go from /user/username1 to /user/username2 info remains from username1. How can I fix this ? This is my code:
UserProfile.vue
mounted() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
And this is from actions.js file to get user:
const getUserProfile = async ({
commit
}) => {
try {
const response = await API.get('/user/' + router.currentRoute.params.username);
if (response.status === 200 && response.data.user) {
const data = {
success: true,
user: response.data.user,
invites: response.data.invites
}
return data;
} else {
return console.log('Something went wrong.');
}
} catch (error) {
console.log(error);
}
};
Should I add watch maybe instead of mounted to keep track of username change in url ?
You can use watch with the immediate property, you can then remove the code in mounted as the watch handler will be called instead.
watch: {
'$route.params.username': {
handler: function() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
deep: true,
immediate: true,
},
}
Your page is loaded before the data is retrieved it seems, you need put a "loading" property in the data and have a v-if="!loading" for your component then it will only render once the display is updated. Personally I would avoid watch if I can it is not great for performance of for fine grained handling.
Yes you should add wach on statement that contain user info.(you may have a problem to watch on object, so you can save user info in json, but im not sure). When user changing - call action, after recived response call mutation that should change a state, then watch this state.
And you might use better syntax to receive data from store. That is really bad idea call dispatch directly from your mouted hook, use vuex documentation to make your code better.

Call a function after state changes

I'm building a React Native app and when one button is pressed I want to call two functions. The first one will make a get call and set the state loading: true, the second one will show a popup with the result of that get call.
I am calling the second function only if loading === false but it is executed immediately after the first one before the state can change, because loading is false by default. I can resolve this with setTimeout but I was wondering if there was a cleaner way to do this.
onPress() {
this.props.getUsers();
setTimeout(() => {
if (this.props.loading === false) {
this.props.popUpVisible();
}
}, 1000);
}
You can create callback function for that
getUsers = (callback) => {
//do whatever you want
//when it's done
callback();
}
In onPress function
onPress = () => {
this.props.getUsers(() => {
if (this.props.loading === false) {
this.props.popUpVisible();
}
});
}
setState Function can take two param:
setState(updater, callback)
setState({loading:true},() => {
//this fires once state.loading === true
})
Use getDerivedStateFromProps. It always fire when component's props change.
Below is the example.
class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}