I'm currently working on this function, and the return value returns with undefined. I'm not sure if it is my lack of understanding how javascript operates, or it requires some tricky voodoo to make my code work.
var fs = require('fs');
var _ = require('underscore');
function eventValue(current, choice) {
var output = [];
if (current != null) {
var json;
fs.readFile('./json.json', 'utf8', function(err, data) {
if (err) {
throw err
}
json = JSON.parse(data);
// filter by ID
var filtered = _.filter(json, {
'ID': current
});
//console.log(filtered);
// Get ID of Object
var ID = _.pluck(filtered, 'ID');
// Get Next value of object
var NEXT = _.pluck(filtered, 'next');
// get nested 'choice's 'next' value
var collect = _.pluck(_.filter(
_.flatten(
_.pluck(filtered, 'choice')), {
'choice': choice
}), 'next');
var stringID = String(ID);
var stringNext = String(NEXT + collect);
output = [stringID, stringNext];
return output;
})
} else console.log("[[error]] please populate eventValue()");
};
var a = eventValue("001001A01B01");
console.log(a);
You are trying to use value returned from asynchronous function callback which you can not.
Refer: How to get returned value by function with callback inside
Related
Original code from the Point of Sale module
In the point_of_sale module there is a list of objects as the following
module.PosModel = Backbone.Model.extend({
models: {
// [...]
{
model: 'pos.session',
fields: ['id', 'journal_ids','name','user_id','config_id','start_at','stop_at','sequence_number','login_number'],
domain: function(self){ return [['state','=','opened'],['user_id','=',self.session.uid]]; },
loaded: function(self,pos_sessions){
self.pos_session = pos_sessions[0];
var orders = self.db.get_orders();
for (var i = 0; i < orders.length; i++) {
self.pos_session.sequence_number = Math.max(self.pos_session.sequence_number, orders[i].data.sequence_number+1);
}
},
},
{
model: 'product.product',
fields: ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code',
'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
'product_tmpl_id'],
domain: [['sale_ok','=',true],['available_in_pos','=',true]],
context: function(self){ return { pricelist: self.pricelist.id, display_default_code: false }; },
loaded: function(self, products){
self.db.add_products(products);
},
// [...]
}
And then the information of the data is loaded like this
load_server_data: function(){
var self = this;
var loaded = new $.Deferred();
var progress = 0;
var progress_step = 1.0 / self.models.length;
var tmp = {}; // this is used to share a temporary state between models loaders
function load_model(index){
if(index >= self.models.length){
loaded.resolve();
}else{
var model = self.models[index];
self.pos_widget.loading_message(_t('Loading')+' '+(model.label || model.model || ''), progress);
var fields = typeof model.fields === 'function' ? model.fields(self,tmp) : model.fields;
var domain = typeof model.domain === 'function' ? model.domain(self,tmp) : model.domain;
var context = typeof model.context === 'function' ? model.context(self,tmp) : model.context;
var ids = typeof model.ids === 'function' ? model.ids(self,tmp) : model.ids;
progress += progress_step;
if( model.model ){
if (model.ids) {
var records = new instance.web.Model(model.model).call('read',[ids,fields],context);
} else {
var records = new instance.web.Model(model.model).query(fields).filter(domain).context(context).all()
}
// [...]
What I have tried. First try
So, I would like to change the domain field of the product.product model. I am trying with this
if (typeof jQuery === 'undefined') { throw new Error('Product multi POS needs jQuery'); }
+function ($) {
'use strict';
openerp.pos_product_multi_shop = function(instance, module) {
var PosModelParent = instance.point_of_sale.PosModel;
instance.point_of_sale.PosModel = instance.point_of_sale.PosModel.extend({
load_server_data: function(){
console.log('-- LOAD SERVER DATA');
var self = this;
self.models.forEach(function(elem) {
if (elem.model == 'product.product') {
// return [['id', 'in', [2]]]; // if I return this domain it works well
domain_loaded = function() {
return new instance.web.Model('product.product').call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
)
}
elem.domain = $.when(domain_loaded);
}
})
var loaded = PosModelParent.prototype.load_server_data.apply(this, arguments);
return loaded;
},
});
}
}(jQuery);
If I return a domain directly it works. But if I replace it with a function that calls a python function with call, the domain is not loaded well: [['sale_ok','=',true],['available_in_pos','=',true]]. I've tried with $.when and without it and it does not work.
In addition elem.domain must be a function because self.pos_session only exists when all the previous model information is executed.
Second try
I have tried this following code as well:
if (elem.model == 'product.product') {
// return [['id', 'in', [2]]]; // if I return the domain like this it works
console.log('>> OLD DOMAIN')
console.log(elem.domain);
elem.domain = function() {
console.log('>>> PRODUCT SESSION');
console.log(self.pos_session);
var product_product_obj = new instance.web.Model('product.product');
return product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
)
}
console.log('>> NEW DOMAIN')
console.log(elem.domain);
}
So first '>> OLD DOMAIN' is printed, then '>> NEW DOMAIN' and, at last '>>> PRODUCT SESSION' is printed. So the function is executed. But the the domains is not being returned well.
Third try. With "then"
And I cannot use then because I need to do the variable assignation. But on the other hand the assignation is well done becase when I print the new domain the function appears in the log.
Even if I use then I am getting the result well from python
var domain_return = product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
).then(function(result) {
console.log('>> RESULT: ');
console.log(result)
});
I also tried with other promise, but I get a pending result that is ignored and all the products are shown
elem.domain = function() {
return new Promise(function next(resolve, reject) {
console.log('>>> PRODUCT SESSION');
console.log(self.pos_session);
var product_product_obj = new instance.web.Model('product.product');
var domain_return = product_product_obj.call(
'get_available_in_pos_ids',
[self.pos_session.config_id[0]],
).then(function(result) {
console.log('>> RETURN: ');
console.log(result);
resolve(result);
});
console.log('>> DOMAIN RETURN: ');
console.log(domain_return);
});
}
The rest of the domains of the object are calculated without calling python functions. So I can't copy an example from other place
So, is there a way to avoid the pending result? I cannot use async/await yet.
Maybe to make it syncronous will help but I know this should be avoided
Finally I found a workaround overriding the loaded function where all the products are already loaded
var PosModelParent = instance.point_of_sale.PosModel;
instance.point_of_sale.PosModel = instance.point_of_sale.PosModel.extend({
load_server_data: function(){
let self = this;
self.models.forEach(function(elem) {
if (elem.model == 'product.product') {
elem.fields = ['display_name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code',
'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description',
'product_tmpl_id', 'available_in_pos_ids'];
elem.loaded = function(self, products){
console.log('>> PRODUCTS: ');
console.log(products);
var shop_id = self.pos_session.config_id[0];
var new_products = [];
products.forEach(function(prod) {
if (prod.available_in_pos_ids.includes(shop_id)) {
new_products.push(prod);
}
})
self.db.add_products(new_products);
}
}
})
var loaded = PosModelParent.prototype.load_server_data.apply(this, arguments);
return loaded;
},
});
setInterval(function(){
for(i=1;i<=len;i++)
{
plug[i].getSysInfo().then(data => {
var json = JSON.stringify(data,undefined,2);
var obj = JSON.parse(json);
var _macadd = obj.mac;
plug[i].getConsumption().then(data => {
var json = JSON.stringify(data,undefined,2);
var obj = JSON.parse(json);
var _current = obj.current;
var _voltage = obj.voltage;
var _power = (obj.power).toPrecision(5);
var _total = obj.total;
var _err_code = obj.err_code;
console.log(_power);
//console.log(_macadd);
//console.log(voltage);
if(_power > 1800)
{
_power = 0.00;
}
// var fs = require('fs');
// fs.writeFile("emeter.json", json);
var sql1 = "INSERT INTO emeter SET ? ";
var post1 = { macaddress: _macadd,current: _current,voltage: _voltage,power: _power, total: _total,err_code: _err_code};
var tmp = JSON.stringify(post1,undefined,2);
console.log(tmp);
con.query(sql1, post1, function (err, result) {
if (err) throw err;
console.log("inserted");
});
});
});
}
},30000);
While I run this, I am getting below error
Unhandled promise rejection Type error:
Cannot read property of 'getconsumption' of undefined.
The above code worked well when I replace the index with one value.To retrieve just one value it is working. When it is in For loop then the error message is returned. Please help me in solving this.
You're trying to use a for loop index value inside an asynchronous callback. The problem is that the .then() handlers are called AFTER the for loop has already finished and thus the for loop index is at its terminal value (beyond the end of the array) when you try to use it.
There are many ways to fix this. One simple way would be to change this:
for(i=1;i<=len;i++)
to this:
for(let i=1;i<=len;i++)
Using ES6 let creates a new unique variable for each execution of the for loop so that each one maintains its own copy of the index, even for the duration of the asynchronous calls within that for loop block.
In ES5 (where you don't have let), you could change your loop to use .forEach():
plug.forEach(function(val, index) {
// use val and index here rather than plug[i]
});
This also creates a new val and index variable for each invocation of the loop.
I've been trying to code up a search engine using angular js, but I can't copy one array to another. When I initiate the the code (in the service.FoundItems in the q.all then function) new array(foundArray) shows up as an empty array. I searched up how to copy one array to another and tried that method as you can see, but it isn't working. Please help, here is the code, and thank you.
P.S. if you need the html please tell me.
(function () {
'use strict';
angular.module('narrowDownMenuApp', [])
.controller('narrowItDownController', narrowItDownController)
.service('MenuSearchService', MenuSearchService)
.directive('searchResult', searchResultDirective);
function searchResultDirective() {
var ddo = {
templateUrl: 'searchResult.html',
scope: {
items: '<'
},
};
return ddo
}
narrowItDownController.$inject = ['MenuSearchService'];
function narrowItDownController(MenuSearchService) {
var menu = this;
menu.input = "";
menu.displayResult = [];
menu.searchX = function(name) {
menu.displayResult = MenuSearchService.FoundItems(menu.input, name);
console.log(menu.displayResult);
};
}
MenuSearchService.$inject = ['$http', '$q'];
function MenuSearchService($http, $q) {
var service = this;
service.getMatchedMenuItems = function(name, searchTerm) {
var deferred = $q.defer();
var foundItems = [];
var result = $http({
method: "GET",
url: ('https://davids-restaurant.herokuapp.com/menu_items.json'),
params: {
category: name
}
}).then(function (result) {
var items = result.data;
for (var i = 0; i < items.menu_items.length; i++) {
if (searchTerm === ""){
deferred.reject("Please enter search term");
i = items.menu_items.length;
}
else if (items.menu_items[i].name.toLowerCase().indexOf(searchTerm.toLowerCase()) ==! -1){
foundItems.push(items.menu_items[i].name)
deferred.resolve(foundItems);
}else {
console.log("doesn't match search");
}
}
});
return deferred.promise;
};
service.FoundItems = function (searchTerm, name) {
var searchResult = service.getMatchedMenuItems(name, searchTerm);
var foundArray = [];
$q.all([searchResult])
.then(function (foundItems) {
foundArray = foundItems[0].slice(0);
foundArray.reverse();
})
.catch(function (errorResponse) {
foundArray.push(errorResponse);
});
console.log(foundArray);
return foundArray;
};
};
})();
If the goal of the service.FoundItems function is to return a reference to an array that is later populated with results from the server, use angular.copy to copy the new array from the server to the existing array:
service.FoundItems = function (searchTerm, name) {
var foundArray = [];
var searchPromise = service.getMatchedMenuItems(name, searchTerm);
foundArray.$promise = searchPromise
.then(function (foundItems) {
angular.copy(foundItems, foundArray);
foundArray.reverse();
return foundArray;
})
.catch(function (errorResponse) {
return $q.reject(errorResponse);
})
.finally(function() {
console.log(foundArray);
});
return foundArray;
};
I recommend that the promise be attached to the array reference as a property named $promise so that it can be used to chain functions that depend on results from the server.
Frankly I don't recommend designing services that return array references that are later populated with results. If you insist on designing it that way, this is how it is done.
I tried the $promise thing that you recommended. I was wondering how you would get the value from it ie the array.
In the controller, use the .then method of the $promise to see the final value of the array:
narrowItDownController.$inject = ['MenuSearchService'];
function narrowItDownController(MenuSearchService) {
var menu = this;
menu.input = "";
menu.displayResult = [];
menu.searchX = function(name) {
menu.displayResult = MenuSearchService.FoundItems(menu.input, name);
̶c̶o̶n̶s̶o̶l̶e̶.̶l̶o̶g̶(̶m̶e̶n̶u̶.̶d̶i̶s̶p̶l̶a̶y̶R̶e̶s̶u̶l̶t̶)̶;̶
menu.displayResult.$promise
.then(function(foundArray) {
console.log(foundArray);
console.log(menu.displayResult);
}).catch(function(errorResponse) {
console.log("ERROR");
console.log(errorResponse);
});
};
}
To see the final result, the console.log needs to be moved inside the .then block of the promise.
Titus is right. The function always immediately returns the initial value of foundArray which is an empty array. The promise is executed asynchronously so by the time you are trying to change foundArray it is too late. You need to return the promise itself and then using .then() to retrieve the value just like you are currently doing inside the method.
From just quickly looking at your code I think you made have a simple error in there. Are you sure you want
foundArray = foundItems[0].slice(0);
instead of
foundArray = foundItems.slice(0);
var sync = Meteor.wrapAsync(connection.query);
var rows = sync(selectMailgunChecked);
var emails = rows.map(function(a) {
return a.email
});
var from = 'sample#email.com';
emails.forEach(function(entry) {
mailgunSend( entry, from, contentHTML, contentRAW, subject, tags);
});
Code above (wrapped function of connection.query from node-mysql use in Meteor app) gives me an arror:
Cannot read property 'typeCast' of undefined
It is somehow related to sync(selectMailgunChecked) and external library (from node-mysql) Connection.js:
Connection.prototype.query = function query(sql, values, cb) {
var query = Connection.createQuery(sql, values, cb);
query._connection = this;
if (!(typeof sql == 'object' && 'typeCast' in sql)) {
query.typeCast = this.config.typeCast;
}
if (query.sql) {
query.sql = this.format(query.sql, query.values);
}
this._implyConnect();
return this._protocol._enqueue(query);
};
Every variable in my code is defined and sucessfully passed. What Can be wrong here?
this.config in this line
query.typeCast = this.config.typeCast;
is undefined.
You have to define the context (this) in which the wrap async function connection.query is executed by passing it as the second parameter
var sync = Meteor.wrapAsync(connection.query, connection);
I m creating mobile web application using html5 and javascript.I m having two javascript files. AttributesDatabase.js and AttributeView.js.From AttributeView.js i m calling one function from AttributeDatabase.js in that i m executing one select query.Now the query result should go to AtttributeView.js.But the Websql transaction is asynchronous call that is what it is not returning proper result.Is there any way to handle the websql result.
Please help if any way there?
Edited
AttributeView.js
var AttributeDAOObj = new AttributeDAO();
AttributeDAOObj.GetAttributeList();
alert(AttributeDAOObj.GetAttributeList()); //This alert is coming as undefined.
AttributeDAO.js
this.GetAttributeList = function () {
var baseDAOObj = new BaseDAO();
var query = "SELECT AttributeName FROM LOGS";
// this.Successcalbackfromsrc = this.myInstance.Successcalback;
var parm = { 'query': query, 'Successcalback': this.myInstance.Successcalback };
baseDAOObj.executeSql(parm);
}
//To Create database and execute sql queries.
function BaseDAO() {
this.myInstance = this;
//Creating database
this.GetMobileWebDB = function () {
if (dbName == null) {
var dbName = 'ABC';
}
var objMobileWebDB = window.openDatabase(dbName, "1.0", dbName, 5 * 1024 * 1024);
return objMobileWebDB;
}
//Executing queries and getting result
this.executeSql = function (query) {
var objMobileWebDB = this.myInstance.GetMobileWebDB();
objMobileWebDB.transaction(function (transaction) {
//In this transaction i m returning the result.The result value is coming.
transaction.executeSql(query, [], function (transaction, result) { return result; }, this.Errorclback);
});
}
}
The problem is in you succes call back (like in the comment to your question, stated by DCoder)
function (transaction, result) { return result; }
this is returning where to?
So this is how to do it (or at least one way)
you can do for example:
function (transaction,result){
console.log("yes, I have some result, but this doesn't say anything, empty result gives also a result");
// so check if there is a result:
if (result != null && result.rows != null) {
if (result.rows.length == 0) {
// do something if there is no result
}else{
for ( var i = 0; i < result.rows.length; i++) {
var row = result.rows.item(i);
var id = result.rows.item(i).id; //supposing there is an id in your result
console.log('Yeah! row id = '+id);
}
}
}else{
// do something if there is no result
}
};
note the code above can be compacter, but this is how to understand it better.
another way is to put this function is a seperate piece of code, so you keep the sql statement more compact and readable. Like you call you error callback this can be in your function (with this. in front of it) or a completely seperate function.