VUEJS: Component to update a users communication preference - javascript

I just need some help identifying what I am missing here. Just can't seem to send the correct data through:
Parent with the CommunicationPreference component:
<CommunicationPreference
v-for="(communication, index) in communicationPreference"
:key="index"
:consent="communication.consent"
:name="communication.name"
#update="updateConsent(consent)"
/>
METHOD
methods: {
async updateConsent(consent) {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent })
},
},
CommunicationPrefernce.vue
<Button
class="mr-4"
:text="YES"
:type="consent === true ? 'primary' : 'secondary'"
#clicked="updateConsent(true)"
/>
<Button
:text="NO"
:type="consent !== true ? 'primary' : 'secondary'"
#clicked="updateConsent(false)"
/>
PROPS:
props: {
type: {
type: String,
default: '',
},
name: {
type: String,
default: '',
},
consent: {
type: Boolean,
default: true,
},
},
METHOD:
updateConsent(consent) {
this.$emit('update', consent)
},
STORE:
async updateCommunicationPreferences({ commit, state }, payload) {
const { consent } = payload
const { communicationTypeName } = state.communicationTypeName
try {
const response = await this.$axios.put(`/communication-consent/${communicationTypeName}`, consent)
const { data: updatedCommunicationPreferences } = response.data
commit('SET_UPDATED_COMMUNICATION_PREFERENCES', updatedCommunicationPreferences)
} catch (error) {
commit('ADD_ERROR', { id: 'updateCommunicationPreferences', error }, { root: true })
}
},
Attached is the UI I am working towards for reference. the idea is each time the user selects either YES or NO the selection is updated and reflected on the UI
Here is my Swagger doc:

I assume that you have a mapped getter for communicationPreference prop, so that this is correct.
I also assume that your #clicked event prop is proper provided the implementation of Button.vue.
So try to change #update="updateConsent(consent)" to #update="updateConsent"
Right now it seems to me that you are making a small mistake between a function call and declaration. Having it such as #update="updateConsent" will trigger updateConsent method, and the function declaration:
async updateConsent(consent) {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent })
},
will take care of getting the consent you pass in your event trigger.

Related

Xstate machine - Load feature flags

What we need to do: We need to feature flag a few things in our current state machine
My ideal solution: Always load, no matter what state is, all feature flags and assign them to the state machine context
Attempts: Tried using async actions and invoke services, however, I cannot find a way to always run either of them
This basically my state machine and how I envisioned loading feature flag. However, the invoke.src function just gets called for the first time when I'm first loading the state machine.
Every time that I hydrate the state machine and the state machine is in one of the state, for example create, the invoke.src function does not get called therefore no FF is loaded into the context
const stateMachine = createStateMachine({
id: 'state-machine',
invoke: {
src: async () => {
return await featureFlagService.load();
},
onDone: {
actions: assign(_context, event) => ({ featureFlagEvaluations: event.data }),
}
},
states: {
'create': { ... },
'retrieve': { ... },
}
});
Does anyone have any idea of how to implement such use case?
You should use the actor model approach. Each time when you need to refresh/fetch the FF you should spawn FF-machine and on done call parentSend() message which will update the context to your main SM(state-machine)
const stateMachine = createStateMachine({
id: 'state-machine',
invoke: {
src: async () => {
return await featureFlagService.load();
},
onDone: [{
actions: assign({
ffActorRef: () => spawn(featureFlagMachine, 'ffActor'),
}),
}],
states: {
'create': { ... },
'retrieve': { ... },
},
on:{
REFRESH_FEATURE_FLAG : [{
actions: assign(_context, event) => ({ featureFlagEvaluations: event.data }),
}]
}
});
const featureFlagMachine = createStateMachine({
id: 'ff-machine',
initial: 'retrieve',
invoke: {
src: async () => {
return await featureFlagService.load();
},
onDone: [{
actions: ['notifyRefresh']
}],
states: {
'create': { ... },
'retrieve': { ... },
},
},
{
actions: {
notifyRefresh: sendParent((ctx, event) => {
return {
type: 'REFRESH_FEATURE_FLAG',
data: { featureFlagEvaluations: event.data },
};
}),
},
}
}
);

Vue component using axios to update a preference

I am trying to update a preference, example UI attached. The default is yes but a user should have the option to select no. I know I am a little way off but I just need some help identifying where I am going wrong, any help would be really appreciated.
Parent:
<CommunicationPreference
v-for="(communication, index) in communicationPreferenceType"
:key="index + communication.name"
:consent="communication.consent"
:name="communication.name"
#accept-consent="acceptConsent"
#decline-consent="declineConsent"
/>
methods: {
async acceptConsent() {
await this.$store.dispatch('account/updateCommunicationPreferences')
},
async declineConsent() {
await this.$store.dispatch('account/updateCommunicationPreferences')
},
}
CommunicationPreference.vue component:
<Button
:text="Yes"
:type="consent === true ? 'primary' : 'secondary'"
#clicked="acceptConsent"
/>
<Button
:text="No"
:type="consent !== true ? 'primary' : 'secondary'"
#clicked="declineConsent"
/>
methods: {
acceptConsent(consent) {
this.$emit('accept', consent === true)
},
declineConsent(consent) {
this.$emit('decline', consent === false)
},
},
Store:
async updateCommunicationPreferences({ commit, state }) {
const { communicationTypeName } = state.communicationTypeName
if (!communicationTypeName) {
return
}
try {
const response = await this.$axios.put(`/communication-consent/${communicationTypeName}`)
const { data: updatedCommunicationPreferences } = response.data
commit('SET_UPDATED_COMMUNICATION_PREFERENCES', updatedCommunicationPreferences)
} catch (error) {
commit('ADD_ERROR', { id: 'updateCommunicationPreferences', error }, { root: true })
}
},
As mentioned in the comments, the name of the method called is incorrect.
As mentioned by #qimolin, the values related to each option are not being passed to the function that saves it, this can be done by passing a value at calling the action.
methods: {
async acceptConsent() {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent: true })
},
async declineConsent() {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent: false })
}
or even that simplified with a single method
<CommunicationPreference
v-for="(communication, index) in communicationPreferenceType"
:key="index + communication.name"
:consent="communication.consent"
:name="communication.name"
#accept-consent="acceptConsent(true)"
#decline-consent="declineConsent(false)"
/>
methods: {
async updateConsent(consent) {
await this.$store.dispatch('account/updateCommunicationPreferences', { consent })
}
}
and that parameter must be captured on action
async updateCommunicationPreferences({ commit, state }, payload) {
const { consent } = payload // true or false. This is the value selected by the user.
const { communicationTypeName } = state.communicationTypeName
if (!communicationTypeName) {
return
}
try {
const response = await this.$axios.put(`/communication-consent/${communicationTypeName}`)
const { data: updatedCommunicationPreferences } = response.data
commit('SET_UPDATED_COMMUNICATION_PREFERENCES', updatedCommunicationPreferences)
} catch (error) {
commit('ADD_ERROR', { id: 'updateCommunicationPreferences', error }, { root: true })
}
},

How can use computed for v-model?

I got data from API using computed. "UserModule.userInfo.usrEmail" is the state in my vuex. Like below:
data() {
return {
vModel: {
email: {
value: "",
},
}
}
}
computed: {
email:{
get: function(){
return UserModule.userInfo ? UserModule.userInfo.usrEmail : "";
},
set : function(email){
this.vModel.email.value = email
}
},
}
And then show it to user like below:
<input v-model="email"></input>
User can edit email and also cancel their edit process and return to their previous data but in edit everything was correct but when i want to cancel this process my previous data did not show in the input and i saw my new data which is not correct i want to cancel it. This is my cancel method on input:
resetInput(input) {
this.vModel.email.value = this.email
},
"this.email" refer to my computed which is get data from API.
How can i write this cancel process correctly and see my previous data in input tag?
so you can use this solution:
data() {
return {
useGet :{
email: true,
},
}
}
in your method:
resetInput(input) {
this.useGet.email = true
},
and in your computed:
email: {
get: function () {
if (this.useGet.email) {
return UserModule.userInfo ? UserModule.userInfo.usrEmail : ""
}
return ""
},
set: function (email) {
this.useGet.email = false
}
},
this is because of your UserModule.userInfo.usrEmail.
this state does not update.
your get in computed will be work when your UserModule.userInfo.usrEmail changes.

Cancel/Abort Axios Post on Vue Component

I have got a Vue Component which has a list of values, when you select these values this changed the selected array, which in tern is posted to an endpoint.
I have an issue if the user spam clicks these values, as an individual post is created for each change, I want it so that if the user selects another item then the currently pending post is cancelled, so then the new value is posted and updates the endpoint with both the selected items.
However i'm having an issue with aborting the current axios request, I have provided the code below. There are no errors, the request simply doesn't cancel.
export default {
props: {
endpoint: {
default: '',
type: String
},
parameters: {
default: null,
type: Object
}
},
data: () => ({
loaded: false,
selected: [],
save: [],
data: [],
cancel: undefined
}),
methods: {
update() {
const self = this;
let params = this.parameters;
params.data = this.selected;
this.$root.$emit('saving', {
id: this._uid,
saving: true
});
if (self.cancel !== undefined) {
console.log('cancel');
this.cancel();
}
window.axios.post(this.endpoint + '/save', params, {
cancelToken: new window.axios.CancelToken(function executor(c) {
self.cancel = c;
})
}).then(() => {
this.$nextTick(() => {
this.loaded = true;
this.$root.$emit('saving', {
id: this._uid,
saving: false
});
});
}).catch(function (thrown) {
if (window.axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
}
});
}
}
}
I have got a global instance of Axios created on my Vue Application.

vue mapGetters not getting on time

I'm using vuex to manage the state in my application and doing one way binding with my form.
<script>
import { mapGetters } from 'vuex'
import store from 'vuex-store'
import DataWidget from '../../../../uiComponents/widget'
export default {
data () {
return {
isEdit: false,
msg: {
id: 0,
content: '',
isEnabled: false
}
}
},
components: {
DataWidget
},
computed: mapGetters({
messageId: 'messageId',
messageContent: 'messageContent',
isMessageEnabled: 'isMessageEnabled',
isMessageValid: 'isMessageValid'
}),
methods: {
onSave () {
store.dispatch('saveMessage', this.msg, { root: true })
if (this.isMessageValid) {
this.isEdit = !this.isEdit
}
}
},
created () {
this.msg.id = this.messageId
this.msg.content = this.messageContent
this.msg.isEnabled = this.isMessageEnabled
}
}
</script>
<b-form-textarea id="content" v-model="msg.content" :rows="3" required aria-required="true" maxlength="250"></b-form-textarea>
On load, the values on created() are not binded until I perform an action on the page or refresh the page.
I have tried mounted () hooked same thing.
My Vuex store (Message Module) looks like this:
const state = {
messageId: 0,
messageContent: '',
isMessageEnabled: false,
isMessageValid: true
}
const getters = {
messageId: state => state.messageId,
messageContent: state => state.messageContent,
isMessageEnabled: state => state.isMessageEnabled,
isMessageValid: state => state.isMessageValid
}
const actions = {
getMessage ({commit, rootGetters}) {
api.fetch('api/Preference/Message', rootGetters.token)
.then((data) => {
commit(types.MESSAGE_LOAD, data)
})
}
}
const mutations = {
[types.MESSAGE_LOAD] (state, payload) {
state.messageId = payload ? payload.id : 0
state.messageContent = payload ? payload.content : ''
state.isMessageEnabled = payload ? payload.enabled : false
}
}
export default {
state,
getters,
actions,
mutations
}
and I have a global action (action.js) the gets multiple data:
export const loadSetting = ({ commit, rootGetters }) => {
api.fetchAsync('api/Preference/all', rootGetters.token)
.then((data) => {
commit(types.MESSAGE_LOAD, data.message)
commit(types.HELPDESK_LOAD, data.helpDesk)
commit(types.VOLUME_LOAD, data.volumes)
commit(types.DOWNLOAD_LOAD, data.downloadService)
})
}
My api call:
async fetchAsync (url, token = '') {
let data = await axios.get(HOST + url, {
headers: {
'Authorization': 'bearer ' + token
}
})
return data
}
The problem is your'e calling an async method in Vuex but in the created method, you're treating it like a sync operation and expect to get a value.
You need to use the computed properties you created since they are reactive and will update on every change. In order to make the computed writeable change it to be like this:
computed: {
...mapGetters({
messageId: 'messageId',
isMessageEnabled: 'isMessageEnabled',
isMessageValid: 'isMessageValid'
}),
messageContent(){
get () {
return this.$store.getters.messageContent
},
set (value) {
//this is just an example, you can do other things here
this.$store.commit('updateMessage', value)
}
}
}
And change the html to use messageContent:
<b-form-textarea id="content" v-model="messageContent" :rows="3" required aria-required="true" maxlength="250"></b-form-textarea>
For more info refer to this: https://vuex.vuejs.org/en/forms.html

Categories