Unable to return promise values within jQUERY DataTables api column definitions using render function - datatables

I'm trying to call a function within the render columnDefs of my jQuery Datatables api. The data is returned in the console but using a return statement doesn't print the data on the table. There is also a data table warning that pops up before the table is rendered because it looks like no data is being returned.
However, when I use the second snippet to return 'nothing' - all is well and I don't receive the datatable warning.
How do I return the values from my promise?
{
"targets": [5],
"className": 'text-center',
"render": function (data, type, row, meta) {
//the if statement prevents the function from being called multiple times
if (type === 'display') {
//this if statement prevents undefined from being returned
if (typeof getJSON == 'function') {
getJSON(row.ID).then(function (val) {
if (val) {
//I see these values printed on the console for each row
console.log(JSON.stringify(val))
//but if I add return statement here data is still not printed on table
return JSON.stringify(val);
}
else {
return 'not available'
}
});
}
}
}
},
This works no errors:
{
"targets": [5],
"className": 'text-center',
"render": function (data, type, row, meta) {
return 'nothing'
}
},
The promise:
async function getJSON(deviceID) {
return new Promise((resolve, reject) => {
var data = JSON.stringify({
'deviceID': deviceID
});
$.ajax({
type: "POST",
url: MYURL,
contentType: "application/json; charset=utf-8",
data: data,
dataType: "json",
success: function (results) {
resolve(results.data)
},
error: function (error) {
reject(error)
},
})
})
}
This prevents the datatable warning but no values are rendered on the table. However, the console log is still showing the values for each row. Although, I think this defeats the purpose of using a promise:
{
"targets": [5],
"className": 'text-center',
"render": function (data, type, row, meta) {
//declaring returnValue here prevents datatable warning.
let returnValue = '';
//the if statement prevents the function from being called multiple times
if (type === 'display') {
//this if statement prevents undefined from being returned
if (typeof getJSON == 'function') {
getJSON(row.ID).then(function (val) {
if (val) {
//I see these values printed on the console for each row
returnValue = (JSON.stringify(val))
console.log(returnValue)
}
else {
returnValue = 'not available'
}
});
}
}
return returnValue;
}
},

Related

VueJS array returns length = 0

I have created an array in my VueComponent.
When I run a console.log(myArray.length) it says "0", however if I run a console.log(myArray) it shows that the array has the expected data. Check the screenshot from console below. The first part shows myArray, the second is myArray.length (circled in red)
See screenshot
Here is my current code:
Vue.component('invoice-archive', {
data: function () {
return {
invoices: [],
}
},
created() {
this.myUpdateMethod();
},
methods:{
myUpdateMethod: function(){
var $this = this;
let data = { 'id': installationId };
this.getAjax('myUrlHere', data).then(function (result) {
if(!result.length ) return; // <-- This was the problem
$this.invoices.push(JSON.parse(result));
console.log($this.invoices); // This shows the expected content of my array
console.log($this.invoices.length); // This shows 0
});
},
getAjax(url, data, success) {
return new Promise(function (resolve, reject) {
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
xhr.onload = function () {
resolve(this.responseText);
}
xhr.onerror = reject;
xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', "application/json;charset=UTF-8");
xhr.send(JSON.stringify(data));
});
},
});
That is because when you are resolving the promise with this.responseText, you are passing a string into it. You will need to convert the response to JSON first, i.e.:
resolve(JSON.parse(this.responseText));
Since you're using VueJS, you might want to consider using axios instead of rolling your own AJAX request handler: https://v2.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html

How to access local component variable from a callback in vue?

I am trying to set my components variable using an api rest command. I wanted to handle all responses through a function in its own file called handleResponse() which is below.
// api/tools/index.js
function handleResponse (promise, cb, cbError) {
var cbErrorRun = (cbError && typeof cb === "function")
promise.then(function (response) {
if (!response.error) {
cb(response)
}
else if (cbErrorRun) {
cbError(response)
}
}).catch(function (error) {
console.log(error)
if (cbErrorRun) {
var responseError = {
"status": 404,
"error": true,
"message": error.toString()
}
cbError(responseError)
}
})
}
export {handleResponse}
In my component file I have this
.... More above....
<script>
import { fetchStock } from '#/api/stock'
export default {
data () {
return {
stock: {},
tabs: [
{
title: 'Info',
id: 'info'
},
{
title: 'Listings',
id: 'listings'
},
{
title: 'Company',
id: 'company'
}
],
}
},
validate ({params}) {
return /^\d+$/.test(params.id)
},
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
fetchStock(
params,
function(response) { //on successful data retrieval
this.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}
}
</script>
The current code gives me this error: "Uncaught (in promise) TypeError: Cannot set property 'stock' of undefinedAc". I think this happens because I no longer have access to 'this' within the callback I pass in the fetchStock function. How would I fix this without changing the current handleResponse layout.
You can try this trick
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
var self = this;
fetchStock(
params,
function(response) { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}
You can either use an arrow function for you callback since arrow functions maintain and use the this of their containing scope:
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
fetchStock(
params,
(response) => { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
(responseError) => { //on error
console.log(responseError)
}
)
}
Or you can assign const vm = this n the beginning of your method before the callbacks like so.
vm stands for "View Model"
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
const vm = this;
fetchStock(
params,
function(response) { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}
I advise using the const as opposed to var in the vm declaration to make it obvious the value of vm is a constant.

Vee-validate (VueJS) - evaluating a condition asynchronously

Can I make a custom validation rule that returns true/false based on a AJAX request? the problem is that the validate call has finished running when the AJAX call completes.
Do I need to have the rule set/unset a boolean variable based on which the field is valid/invalid?
const isValidNameRule = {
getMessage(field)
{
return "The name must be unique."
},
validate(validatingName)
{
var formData = new FormData();
formData.append("validatingName", validatingName);
this.$http.post("/api/isValid?name=" + validatingName, formData)
.then(function (response) {
// success
return true;
}, function (response) {
// error
return false;
});
}
};
Didn't know how to work with Promises.
Eventually got it working by extending one of the official samples:
const customRule = {
getMessage(field, params, data) {
return (data && data.message) || 'Something went wrong';
},
validate(aValue) {
return new Promise(resolve => {
var formData = new FormData();
formData.append("nameFilter", aValue);
$.ajax({
type: "POST",
url: url,
data: {
action: "validate",
value: aValue,
}
}).done(function (data) {
if (!ok)
{
resolve({
valid: false,
data: {message: "Condition not met"}
});
}
else
{
resolve({
valid: !! aValue,
data: undefined
});
}
});
});
}
};

return object store value by calling dojo module

I was hoping to get a value (an object store) calling my dojo module, but I keep getting undefined :
module:
define(['dojo/store/Memory', 'dojo/_base/xhr', "dojo/data/ObjectStore"],
function (Memory, xhr, ObjectStore) {
var oReachStore;
return {
Reaches: function (url) {
xhr.get({//get data from database
url: url,
//url: url,
handleAs: "json",
load: function (result) {
var ReachData = result.GetReachesResult;
var ReachStore = new Memory({ data: ReachData, idProperty: "label" });
oReachStore = new ObjectStore({ objectStore: ReachStore });
},
error: function (err) { }
});
},
GetReaches: function () {
return oReachStore;
}
}
});
calls to module:
Data.Reaches(dataServiceUrl);//set the reach object store
ReachData = Data.GetReaches();//get the reach object store, but is always undefined
Like you probably noticed by now (by reading your answer), is that you're using an asynchronous lookup (the XMLHttpRequest is asynchronous in this case), but you're relying on that store, before it might be set.
A possible solution is the use of promises/deferreds. I don't know which Dojo version you're using, but in Dojo < 1.8 you could use the dojo/_base/Deferred module and since 1.8 you can use the dojo/Deferred module. Syntax is slightly different, but the concept is the same.
First you change the oReachStore to:
var oReachStore = new Deferred();
Then, inside your Reaches function you don't replace the oReachStore, but you use the Deferred::resolve() function, for example:
return {
Reaches: function (url) {
xhr.get({//get data from database
url: url,
//url: url,
handleAs: "json",
load: function (result) {
var ReachData = result.GetReachesResult;
var ReachStore = new Memory({ data: ReachData, idProperty: "label" });
oReachStore.resolve(ew ObjectStore({ objectStore: ReachStore })); // Notice the difference
},
error: function (err) { }
});
},
GetReaches: function () {
return oReachStore;
}
}
Then in your code you could use:
Data.Reaches(dataServiceUrl);//set the reach object store
Data.GetReaches().then(function(ReachData) {
console.log(ReachData); // No longer undefined
});
So now the ReachData won't return undefined, because you're waiting until it is resolved.
Deferreds are actually a common pattern in the JavaScript world and is in fact a more solid API compared to defining your own callbacks. For example, if you would get an error in your XHR request, you could use:
error: function(err) {
oReachStore.reject(err);
}
A simple example (I mocked the asynchronous request by using setTimeout()) can be found on JSFiddle: http://jsfiddle.net/86x9n/
I needed to use a callback function for the function GetReach. The following modified code worked:
Module:
define(['dojo/store/Memory', 'dojo/_base/xhr', "dojo/data/ObjectStore"],
function (Memory, xhr, ObjectStore) {
return {
GetReach: function (url, callback) {
xhr.get({//get data from database
url: url,
//url: url,
handleAs: "json",
load: function (result) {
var ReachData = result.GetReachesResult;
var ReachStore = new Memory({ data: ReachData, idProperty: "label" });
var oReachStore = new ObjectStore({ objectStore: ReachStore });
callback(oReachStore);
},
error: function (err) { }
});
}
}
});
call from main page:
// ....
Data.GetReach(dataServiceUrl, SetReach);
function SetReach(data) {
//set data for the dropdown
ddReach.setStore(data);
}

How to pass Client Side data to Server Side using Ember.js

I'm studying Ember.js myself and I'm stuck with a problem I'm creating a sample app and I need to send the client side values to Server Side but I dont know how to do that I know the traditional way like the below code
function create() {
var data = {
'EmailID': $('#emailid').val(),
'password': $('#password').val()
}
$.ajax({
url: '/EmberNew/Home/Create',
type: 'POST',
data:data,
success: function (response) {
alert("hi");
}
});
return false;
}
but In Ember i dont Know How to do that my current code is given below
//Application
App = Em.Application.create();
//Model
App.Users = Em.Object.extend({
name: null,
password:null
});
//View
App.UserTextField = Em.TextField.extend({
insertNew: function () {
App.alertController.alertDetails();
}
});
App.PassTextField = Em.TextField.extend({
insertNew: function () {
App.alertController.alertDetails();
}
});
//controller
App.AlertController = Em.ObjectController.extend({
content: [],
username: '',
password: '',
alertDetails: function () {
var me = this;
var username = me.get("username");
var password = me.get("password");
alert('The User Name Is' + 'username' + 'And Password Is' + 'password');
}
});
App.alertController = App.AlertController.create();
I got the textbox values from alertDetails function and how can I pass them to server side
App.Record = Ember.Object.extend({
name: '',
other: ''
}).reopenClass({
records: [],
find: function() {
var self = this;
$.ajax({
url: "/api/records/",
type: "GET",
cache: false,
dataType: "json",
beforeSend: function() {
//if you want to call this often and need to clear + reload it
return self.records.clear();
},
success: function(results) {
var result;
for (_i = 0, _len = results.length; _i < _len; _i++) {
result = results[_i];
self.records.push(self.records.addObject(App.Record.create(result)));
}
},
error: function() {
return alert("error: failed to load the records");
}
});
return this.records;
}
});
Now that you have your model setup, you can call it from your route model hook
App.RecordsRoute = Ember.Route.extend({
model: function() {
return App.Record.find();
}
});
The find method returns an empty array right away, your template is then bound to it. When the ajax call returns w/ success and you update that array the handlebars template will update it for you w/out any DOM or jQuery glue code