Vue: Component method from vuex module? - javascript

I'm using namespaced modules for state management in vuex, I try to keep all my actions mutations inside my modules as this helps me keep most of my code in the same place (modules acting like classes or similar) hoever there's an issue, I'd like to fire a component method to clear a form when a vuex action is successfull (that is the axios request gets an OK/200 response) but sadly I can't fire a methos from vuex module into my component ( there's no this inisde module).
I also tried adding a .then to my action call but it fires right after I call the action...
I guess I could move the action into the component itself but I'd rather not, what do you guys suggest?
My component:
stripeSourceHandler: function(sourceId)
{
if(this.customerSources.length == 0)
{
console.log('createSourceAndCustomer');
this.createSourceAndCustomer({ id: sourceId });
}
else
{
console.log('addSource');
this.addSource({ id: sourceId }).then(alert('Form cleared')); //this fires right after calling addSource
};
},
My module action:
addSource({ commit }, sourceId)
{
commit('Loader/SET_LOADER', { status:1, message: 'Procesando...' }, { root: true });
axios.post('/stripe/add-source', sourceId)
.then((response) => {
commit('Loader/SET_LOADER', { status:2, message: response.data.message }, { root: true });
commit('ADD_SOURCE', response.data.customer);
//I can't clear component form from this...
},
(error) => {
commit('Loader/SET_LOADER', { status:3, errors: error, message: 'Error al añadir el pago.' }, { root: true });
});
},

Two issues:
You need to return the promise from the action so that you can use .then() to schedule code to be executed once the action has completed (this code being whatever you need to do to clear the form).
.then() takes one (or two) functions as parameters which will be called once the promise has resolved, instead you're just calling alert() immediately.
It'll be something like:
Component method
stripeSourceHandler(sourceId) {
if (this.customerSources.length == 0) {
this.createSourceAndCustomer({ id: sourceId });
} else {
this.addSource({ id: sourceId }).then(() => {
// Clear the form here
alert('Form cleared');
});
}
}
Vuex action
addSource({ commit }, sourceId) {
commit('Loader/SET_LOADER', { status:1, message: 'Procesando...' }, { root: true });
// Return the promise here
return axios.post('/stripe/add-source', sourceId)
.then(response => {
commit('Loader/SET_LOADER', { status:2, message: response.data.message }, { root: true });
commit('ADD_SOURCE', response.data.customer);
}, error => {
commit('Loader/SET_LOADER', { status:3, errors: error, message: 'Error al añadir el pago.' }, { root: true });
});
}

Related

How to get the confirmation service of primeng wait for the approval or denial of the user?

How can I make the primng confirmation service behave equal to the browser native confirm?
When a user clicks on a button, I need under certain conditions to request their confirmation to proceed, in case of denying it, exit the method.
The following is an example using the primeng confirmation service. The problem is that the last line of the method that calls openFile is executed without waiting for user approval.
confirm(): void {
if (this.condition) {
this.confirmationService.confirm({
message: 'Are you sure that you want to proceed?',
header: 'Confirmation',
icon: 'pi pi-exclamation-triangle',
accept: () => {
this.openFile();
this.messageService.add({
severity: 'info',
summary: 'Confirmed',
detail: 'You have accepted'
});
return;
},
reject: () => {
return;
}
});
}
this.openFile();
}
On the other hand by implementing the same logic using the browser native confirmation. It works as expected, so that in the proper condition it will wait for the user's confirmation
confirm1(): void {
if (this.condition) {
const result = confirm('Are you sure that you want to proceed?');
if (result) {
this.openFile();
return;
} else {
return;
}
}
this.openFile();
}
The openFile methodo has a simple console log
openFile(): void {
console.log('Some file');
}
How to get the confirmation service of primeng wait for the approval or denial of the user?
You can interact with an example of the behavior described in this repository https://github.com/ilmoralito/sample-primeng-confirmation-service-vs-native-confirmation
you can create a service as layer above primeng confirm service then use a promise as return type of custom confirm method that call primeng confirm service , the promise resolve as true for accept and false in case of reject.
confirm service
#Injectable({
providedIn: "root"
})
export class ConfirmService {
constructor(private confirmationService: ConfirmationService) {}
confirm({
message = "Are you sure that you want to proceed?",
header = "Confirmation",
icon = "pi pi-exclamation-triangle"
} = {}): Promise<boolean> {
return new Promise(resolve => {
console.log(
this.confirmationService.confirm({
message,
header,
icon,
accept: () => {
resolve(true);
},
reject: () => {
resolve(false);
}
})
);
});
}
}
we can use async/await because confirm method return promise
export class AppComponent {
msgs: Message[] = [];
constructor(private confirmService: ConfirmService) {}
async confirm() {
if (await this.confirmService.confirm())
this.msgs = [
{ severity: "info", summary: "Confirmed", detail: "You have accepted" }
];
else {
this.msgs = [
{ severity: "info", summary: "Rejected", detail: "You have rejected" }
];
}
}
}
stackblitz demo 🚀

Running a callback in a Vue component and waiting for result before changing state

I have a Vue component called aui-button that is used like:
<aui-button classes="btn-primary test-class" :callback="test">Test Button</aui-button>
I'm trying to design the component to be able to run any given callback without the callback requiring any structural changes or special return values; basically, I want to just give it some code, and have it run it. I pass the callback as a prop and call it. Easy.
props: ['classes', 'callback'],
methods: {
runCallback() {
this.callback();
}
}
What I'm stuck on is how to change the state data of the button based on the callback given those constraints.
data: function() {
return {
loading: false
}
},
How can I change the value of a specific data value on the component after callback execution given that I'd like to have zero say over what the callback argument is doing? Ideally these have a wide array of uses, from API calls to simple value changes.
Here's another answer in response to your comment. If you really wanted to implement it that way then there's nothing stopping you.
The problem is that the callback will be asynchronous (otherwise, what's the point of setting a loading state), so the component needs to be told when the asynchronous callback has completed.
You could define your callback functions so that they take a done() callback.
AuiButton.vue
props: ['classes', 'callback'],
data() { return { loading: false}},
methods: {
runCallback() {
this.loading = true;
this.callback(this.doneCallback);
},
doneCallback() {
this.loading = false;
},
},
Parent Component
<aui-button classes="btn-primary test-class" :callback="test">Test Button</aui-button>
...
methods: {
test(doneCallback) {
doSometingAsync(someData, (error, result) => {
doneCallback();
// do something with result..
})
},
Perhaps a better solution is to use events, but give the click event a done callback:
AuiButton.vue
props: ['classes'], // don't need to pass the callback as a prop anymore.
data() { return { loading: false}},
methods: {
runCallback() {
this.loading = true;
this.$emit('click', this.doneCallback);
},
doneCallback() {
this.loading = false;
},
},
Parent Component
<aui-button classes="btn-primary test-class" #click="test">Test Button</aui-button>
...
methods: {
test(doneCallback) {
doSometingAsync(someData, (error, result) => {
doneCallback();
// do something with result..
})
},
Rather than pass a callback to your button component, it is recommended practice to make your component $emit an event to the parent. The parent can then run whatever code it wants in the event handler.
Then, you should pass a loading prop to your component to control its loading state. e.g:
AuiButton.vue
<button :classes="classes" :disabled="loading" #click="$emit('click') ...>
...
props: {
classes: String,
loading: {
type: Boolean,
default: false
},
Parent component
<aui-button classes="btn-primary test-class" :loading="loading" #click="buttonClick">
Test Button
</aui-button>
...
data() { return {
loading: false,
} },
methods: {
buttonClick() {
this.loading = true;
doSometingAsync(someData, (error, result) => {
this.loading = false;
// do something with result..
})
},
Now your button component doesn't need to do anything clever - it's told whether it should be in the loading state and it tells its parent whenever it is clicked.

Vue.js: How do you call component specific methods outside of component?

The goal here is to use OpenTok WebRTC platform for a video chat web app built with vue. Here is an example of OpenTok in Vue.
I'm getting confused on where to call specific methods for the publisher because it is nested within a session.vue component. I want to call methods such as:
1) Stop publishing
2) Change the publisher to source from a video instead of webcam
This is the web API.
Should I write methods that affect the publisher in the child component publisher.vue or should I do that in the parent component which calls publisher?
Publisher.vue
<template>
<div> </div>
</template>
<script>
import OT from "#opentok/client";
export default {
name: "publisher",
props: {
session: {
type: OT.Session,
required: false
},
rlive: Boolean,
opts: {
type: Object,
required: false
}
},
mounted: function() {
// console.log(this.rlive)
console.log("publisher created")
console.log(this.session)
const publisher = OT.initPublisher(this.$el, this.opts, err => {
if (err) {
this.$emit("error", err);
} else {
this.$emit("publisherCompleted");
}
});
this.$emit("publisherCreated", publisher);
const publish = () => {
this.session.publish(publisher, err => {
if (err) {
this.$emit("error", err);
} else {
this.$emit("publisherConnected", publisher);
}
});
};
if (this.session && this.session.isConnected()) {
// console.log("this.session && this.session.isConnected()")
publish();
// console.log(publisher)
}
if (this.session) {
console.log("this.session")
console.log(publisher)
this.session.on("sessionConnected", publish);
}
if(!this.session){
console.log("!this.session")
}
},
beforeDestroy: function(){
console.log("before destroy");
console.log(publisher)
console.log(publish)
//here is where I want to KEEEEEL!
// console.log(publisher)
// console.log(this.session)
}
};
</script>

Notify showing nothing on vuex subscribe

I'm trying to show a notification when I try to create a user in my pwa. to do that I'm subscribing the mutation that set the notification and then calling the notify but nothing is showed and no errors on console.
That's what I'm trying to do:
export default {
...,
mounted: function() {
var self = this
this.$store.subscribe(function(mutation, state) {
if (mutation === 'usuario/setError') {
self.$q.notify({
message: state.usuario.error.mensagem,
timeout: 3000,
type: state.usuario.error.sucesso ? 'positive' : 'negative',
position: 'top'
})
}
})
}
}
I tried import Notify from qusar and call Notify.create but without success.
I found a solution, instead use subscribe I could use watch from vue and watch the changes on a computed. This way:
export default {
...,
computed: {
error: function() {
return this.$store.state.usuario.error
}
},
watch: {
error: function(newError, oldError) {
console.log(newError)
this.$q.notify({
message: newError.mensagem,
timeout: 3000,
type: newError.sucesso ? 'positive' : 'negative',
position: 'top'
})
}
},
...
}
try if (mutation.type === 'usuario/setError'), if that doesn't work console log mutation to see what the type is and use that.

Jasmine, React & AJAX: Unit testing function within a function

Here's the code I would like to test. Specifically, I want to spy on a utility called Linkvalidation.validate to make sure that it is called when handleSave() is called.
This code lives in a component called the CondensedFormModal:
handleSave() {
LinkValidation.validate(this.state.url)
.then((response) => {
if (response.success) {
this.setState({
message: ''
});
}
})
.fail((error) => {
if (error && error.message && error.message.match(/invalid internal link/)) {
this.setState({
message: 'The URL is an internal link. Please use a public link.'
});
} else {
this.setState({
message: 'The URL is invalid.'
});
}
});
Here is the LinkValidation.validate utility I'm using in the handleSave function above:
define([
'module/api-call'
], function(
ApiCall
) {
'use strict';
// Calls validation API
function validate(link) {
var deferred = $.Deferred();
ApiCall.apiCall(
'/url/check',
{ link: link },
'POST',
function(data) {
if (data.success === true) {
deferred.resolve(data);
} else {
// This link is not valid
deferred.reject(data);
}
},
function() {
deferred.reject();
}
);
return deferred;
}
return {
validate: validate
};
});
Here is my test file--
Import statement:
import { validate } from 'modules/link-validation.js';
Test:
describe('when the URL is an internal link', () => {
it('displays a unique error message', (done) => {
let modal = shallowInstance(<CondensedFormModal />);
modal.setState({
url: 'https://internal.example.com'
});
let x = jasmine.createSpy('validate').and.returnValue({
message: "invalid internal link",
success: false,
url: 'https://example.com'
});
modal.handleSave();
_.defer(() => {
expect(x).toHaveBeenCalled();
done();
});
});
});
When I run this test, it consistently fails with the message "Expected spy validate to have been called."
After looking at the Jasmine docs (https://jasmine.github.io/2.1/introduction) and various other Stack Overflow questions (Unit test with spy is failing. Says spy was never called , Jasmine test case error 'Spy to have been called' , etc.) I'm unable to make this work. I've also tried callFake and callThrough instead of returnValue.
Any ideas on how to spy on LinkValidation.validate to assure that it was called?
This line:
let x = jasmine.createSpy('validate')
creates new spy function (it doesn't spy on existing validate function) and handleSave function is not aware of it. So it's not called at all.
You have to set spy on function that is actually called in your component. Since your CondensedFormModal uses LinkValidation module (which I assume is imported in component file) you have to set spy on validate function from imported module which is actually used by component. So I'd suggest something like this:
In CondensedFormModal constructor set LinkValidation as component property to make it easily accessible in tests:
this.LinkValidation = LinkValidation;
In handleSave use validate function like this:
this.LinkValidation.validate(this.state.url);
And finally in test set spy on component validate method:
describe('when the URL is an internal link', () => {
it('displays a unique error message', (done) => {
let modal = shallowInstance(<CondensedFormModal />);
...
spyOn(modal.LinkValidation, 'validate').and.returnValue({
message: "invalid internal link",
success: false,
url: 'https://dash.vagrant.local.rf29.net/shopping/new'
});
modal.handleSave();
_.defer(() => {
expect(modal.LinkValidation.validate).toHaveBeenCalled();
done();
});
});
});

Categories