I have setup a custom Vue directive for ajax forms, however I would like it to process a custom onSuccess call with the received data...
The directive looks like this:
Vue.directive('ajax', {
bind: function (el, binding, vnode) {
el.addEventListener(
'submit', function(e){
e.preventDefault();
let formData = new FormData(el);
let method = el.method.toLowerCase();
Vue.http[method](el.action, formData).then(response => { // success callback
data = response.data;
// Do a custom callback for binding.expression
}, response => {
// error callback
});
}
);
},
});
And im using it in various components, in this form:
<form method="POST" action="api/groups" v-ajax="customFunction"></form>
I would like the addGroup method called for the component in which the group is with data passed as a parameter...
Vue.component('x',{
methods: {
customFunction: function(data) : { }
}
});
In this way I would be able to turn any form into AJAX submit, with the possibility to process the data differently for each component. Is that possible?
Your directive needs multiple values a callback function and data and may add up in future. Assign your directive to object literal.
<form method="POST" action="api/groups" v-ajax="{successCb: customSuccessFunction, errorCb: customErrFunction, data: data}">
</form>
In your directive you can access them as below
Vue.directive('ajax', function (el, binding) {
console.log(binding.value.successCb.color) // => customSuccessFunction
console.log(binding.value.errorCb.text) // => customErrFunction
console.log(binding.value.data) // => data object
})
Related
i am using Vue 2 / nuxt to emit from a axios post* call which itself is called from a Buefy dialog confirm. The emit from this component will close the window / panel and then re-load the users.
If I call the axios request from the button, this works without any issues, but once being called from the dialog, it just don't work?
*most likely this will be updated to a delete request, just not gotten to that let
See code below:
removeUser() {
this.$buefy.dialog.confirm({
message: 'Continue on this task?',
onConfirm: () => {
this.removeUserFunc()
}
})
},
removeUserFunc() {
// console.log(that)
const that = this
// Build URL
const EndPoint = '/remove_user/' + this.id
this.$axios.post(EndPoint).then((res) => {
// User Remove Message
UserRemoved(this.$swal)
that.$parent.$emit('completed')
// console.log(this.$emit('complete'))
// // Emit 'completed' Message
console.log(that.$emit('completed'))
console.log(that)
}).catch((res) => {
console.log(res)
// Check For Errors
GeneralError(this.$swal)
})
}
I was thinking it was losing access to the correct this, so i was trying to pass that back in, but not sure that is the case?
I have also tried with await, while that sort of works? I think is firing the emit too fast, as it re-loads the users but it still includes the user that as just been deleted?
removeUser() {
this.$buefy.dialog.confirm({
message: 'Continue on this task?',
onConfirm: async() => {
this.removeUserFunc()
await this.$emit('completed')
}
})
},
The this keyword refers to the object the function belongs to, or the window object if the function belongs to no object.
Try to use .bind and use a ES5 function
removeUser() {
this.$buefy.dialog.confirm({
message: 'Continue on this task?',
onConfirm: function() {
this.removeUserFunc()
}.bind(this)
})
},
So I have implemented Echarts with a Vue application, on one of the charts, I am trying to get the item clicked and pass it back to the parent component that way I can do specific calculations to it.
The 'on click' method works and I can console.log('params') easily, however, trying to reach any other functions outside of it is not possible for some reason...
here is my code...
data() {
return {
myChart: null,
selectedState: {}
}
}.
mounted() {
this.myChart = echarts.init(document.getElementById("geoMap"))
this.myChart.on('click', function(params){
// It will run the console.log with correct info, but the
// method is not reachable...
console.log(params)
this.setSelectedState(params)
})
},
// Inside my vue script this is just a method to set the data for now...
methods: {
setSelectedState(params){
this.selectedState = params
},
}
any help would be nice!! thanks!
You're not in the Vue component context when listening to the chart event, so you have to change your callback function to an arrow one to access the component's this :
this.myChart.on('click', params => {
this.setSelectedState(params)
});
methods: {
setSelectedState(params) {
console.log(params);
this.selectedState = params
}
}
By the way, you should use ref instead of getting your div with document.getElementById to attach your chart :
<div ref="geoMap"></div>
this.myChart = echarts.init(this.$refs.geoMap);
I have a list of items, which I want to update when I update the array. This is my list of items:
<li v-for="page in pages" class="menu__item">
<a class="menu__link" #click="currentPage = page.id">{{ page.title }}</a>
</li>
I have a variable which is an array:
data: {
pages: [],
menuVisible: false
},
I have a watch set up which updates the variable when required:
watch: {
'menuVisible': function(val, oldVal) {
$.getJSON('/pages/json/list.json', function(json){
this.pages = json;
});
}
}
When "menuVisible" updates, it fires this, and the var changes, but the list does not.
To give a basic idea of what I am trying to achieve; I have a list of pages. A new page is created via a ajax submit. When the user opens the menu with the list of pages, it updates menuVisible, which in turn updates the variable. I want it to update the list of pages, but it does not.
What am I missing?
I think this is just a scope issue, this.pages is inside an anonomous function where the context of this is the function itself, not the Vue instance. To avoid this you can either use an arrow function if you are able to us ES6 (it does require something like babel to compile for use in a browser):
watch: {
'menuVisible': function(val, oldVal) {
$.getJSON('/pages/json/list.json', (json) => {
this.pages = json;
});
}
}
Or you just need to set a variable pointing to this outside the ajax request:
watch: {
'menuVisible': function(val, oldVal) {
var self = this; // set self to 'this'
$.getJSON('/pages/json/list.json', function(json){
self.pages = json;
});
}
}
You have an issue with the binding of this in your JSON response handler.
this.pages inside function(json) {..} is not the same as this.pages of Vue component. function() {..} creates a separate context inside the curly braces. Therefore, your pages array is not getting updated.
You can also verify the above using Vue-devtools
To resolve this bug, please change your watch function as follows:
watch: {
'menuVisible': function(val, oldVal) {
$.getJSON('/pages/json/list.json', json => {
this.pages = json;
});
}
}
In line 3 of the above function, you can notice that now it uses something called arrow function. The arrow function syntax ensures that your this in the outerscope (Vue component) is the same as the this of your JSON response handler function.
Alternatively you may also do var that = this outside json response handler, and set that.pages inside the JSON response handler. Ref: What does 'var that = this;' mean in JavaScript?
I am writing a fontend web app using dojo that does a lot of calls to rest endpoints using xhr. I would like to have a place to store configurations for things like endpoint locations and html tag references. I thought I would use an xhr call to a json file to do this, but I am having trouble getting my functions to trigger in the right order/at all. Below is my main js file which has an init() function that I am passing as the callback to my conf initializer ("ebs/conf") module, also below. I have used the Chrome debugger to set breakpoints within my conf.get() method, and it looks as though it never gets called.
Can someone give me some advice please?
Main JS File:
// module requirements
require([ "dojo/dom", "dojo/on", "ebs/prices", "ebs/cart", "ebs/conf",
"dojo/ready" ], function(dom, on, prices, cart, conf, ready) {
ready(function() {
conf.get("/js/config.json", init());
function init(config) {
on(dom.byId("height"), "keyup", function(event) {
prices.calculate(config);
});
on(dom.byId("width"), "keyup", function(event) {
prices.calculate(config);
});
on(dom.byId("qty"), "keyup", function(event) {
prices.calculate(config);
});
on(dom.byId("grills"), "change", function(event) {
prices.calculate(config);
});
cart.putSampleCart();
cart.load(config);
}
});
});
And here is my 'conf' module ("ebs/conf"):
define(["dojo/json", "dojo/request/xhr"], function(json, xhr) {
return {
get : function(file, callback) {
// Create config object from json config file
var config = null;
xhr(file, {
handleAs : "json"
}).then(function(config) {
callback(config);
}, function(error) {
console.error(error);
return error;
});
}
}
});
Your are not passing the function as the callback. You are executing it and passing the result as the second argument.
conf.get("/js/config.json", init());
should be
conf.get("/js/config.json", init);
I have Shell html like this
<div data-bind="compose: { model: 'ui/user/viewmodels/header', view: 'infoveave/user/views/header'}"></div>
<div class="container-fluid page-host">
<!--ko compose: {
model: router.activeItem,
afterCompose: router.afterCompose,
transition:'entrance'
}--><!--/ko-->
</div>
and shell js like
define(function(require) {
var router = require('durandal/plugins/router'),
system = require('durandal/system'),
return {
router: router,
activate: function () {
var self = this;
router.mapAuto('ui/user/viewmodels');
system.log('Sheel Activate Called');
return router.activate('dashboard');
}
};
});
the problem is the activate function on the header doesn't get called but the one on the dashboard gets called, I have to fetch some ajax content in the header and bind it, How can i achieve this
I want to keep this logic separate as i don't want my shell to have this logic
for reference my header (for simplification i have converted my complex model to a simple observable
define(function (require) {
var router = require('durandal/plugins/router'),
system = require('durandal/system');
this.userInfo = ko.observable('');
return {
router: router,
activate: function () {
system.log('Got Called Now');
//do some ajax stuff here and update userinfo
}
};
});
the simplest form of header html is
<span class="dropdown-notif" data-bind="text: userInfo"></span>
I don't think you are activating the header view model.
Try adding 'activate:true' to the header view compose binding like this:
<div data-bind="compose: {
model: 'ui/user/viewmodels/header',
view: 'infoveave/user/views/header',
activate: true}">
</div>
May this be your problem?:
"Activator callbacks are not executed unless an activator is present or activate:true is set on the compose binding."
Source: http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks/
Do you see any errors in the console window of your browser? (Hit F12 in Chrome and then click the console tab).
I suspect you will have some errors that are stopping the view model from activating. Otherwise, you could try these changes:
shell:
define(function(require) {
var router = require('durandal/plugins/router'),
system = require('durandal/system'); // note the semi colon here
return {
router: router,
activate: function () {
var self = this;
router.mapAuto('ui/user/viewmodels');
// the line below to map the route to your view model may be required
router.mapRoute('dashboard');
system.log('Sheel Activate Called');
return router.activate('dashboard');
};
};
});
header view model:
define(function (require) {
var router = require('durandal/plugins/router'),
system = require('durandal/system');
var userInfo = ko.observable('');
return {
router: router,
userInfo: userInfo, // putting the property here should make it visible to the binding in the view
activate: function () {
system.log('Got Called Now');
//do some ajax stuff here and update userinfo
}
};
});