Cordova/Javascript: method as callback function - javascript

Moin,
I feel a little stupid. I try to convert the following function into methods of an object:
function listDir(path){
window.resolveLocalFileSystemURL(path,
function (fileSystem) {
var reader = fileSystem.createReader();
reader.readEntries(
function (entries) {
console.log(entries);
},
function (err) {
console.log(err);
}
);
}, function (err) {
console.log(err);
}
);
}
Source: Cordova list all files from application directory (WWW)
But any way I try it does not work. I don't know how to use the methods as callback function, especially how to set the arguments of the function. With my code I expect the output:
debug: listdir
debug: filesystem
debug: entries
[Array or something]
But I just get:
debug: listdir
debug: filesystem
This ist my Code:
function Filelist() {}
Filelist.prototype = {
methodErr: function (err) {
console.log('debug: error');
console.log(err);
},
methodEntries: function (entries) {
console.log('debug: entries');
console.log(entries);
},
methodFilesystem: function (fileSystem) {
console.log('debug: filesystem');
var reader = fileSystem.createReader();
reader.readEntries(this.methodEntries, this.methodErr);
},
methodListDir: function (path) {
console.log('debug: listdir');
window.resolveLocalFileSystemURL(
path,
this.methodFilesystem,
this.methodErr
);
}
}
fl = new Filelist();
$('.klicken').click(function () {
fl.methodListDir(cordova.file.applicationDirectory);
});
Where is the bug?
Thanks in advance!
Jan

I've got the solution:
I have to bind this in the callback method:
...
reader.readEntries(this.methodEntries.bind(this), this.methodErr.bind(this));
...
window.resolveLocalFileSystemURL(
path,
this.methodFilesystem.bind(this),
this.methodErr.bind(this)
);
...
Now it works :)

Related

callback is not a function - castv2

I'm following this http://siglerdev.us/blog/2021/02/26/google-home-message-broadcast-system-node-js/31 which uses this library castv2-client to send messages to my google home. It works. I get the messages no problem, but the code throws
C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72
callback(null, response.status.volume);
^
TypeError: callback is not a function
at C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72:5 ver.js:72
at fn.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\request-response.js:27:7)
at fn.emit (events.js:203:15)
at Channel.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\controller.js:16:10) s\receiver.js:72:5
at Channel.emit (events.js:198:13) lib\controllers\request-response.js:27:7)
at Client.onmessage (C:\Users\Phil\Documents\google home\node_modules\castv2\lib\channel.js:23:10) ient\lib\controllers\controller.js:16:10)
at Client.emit (events.js:203:15)
at PacketStreamWrapper.onpacket (C:\Users\Phil\Documents\google home\node_module\channel.js:23:10)s\castv2\lib\client.js:81:10)
at PacketStreamWrapper.emit (events.js:198:13) s\castv2\lib\client.js:81:10)
at TLSSocket.<anonymous> (C:\Users\Phil\Documents\google home\node_modules\castv2\lib\packet-stream-wrapper.js:28:16)
What's wrong with the code that is throwing this AND/OR how can I fix it so it's either more graceful in catching error and doesn't throw since the message still delivers to google home or fix it to not throw this at all?
I appreciate any help!
I believe it's here in the castv2-client library that it's referencing, but I haven't been able to make it happy.
ReceiverController.prototype.launch = function(appId, callback) {
this.request({ type: 'LAUNCH', appId: appId }, function(err, response) {
if(err) return callback(err);
if(response.type === 'LAUNCH_ERROR') {
return callback(new Error('Launch failed. Reason: ' + response.reason));
}
callback(null, response.status.applications || []);
});
};
my code
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 });
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED'
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on('status', function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on('error', function (err) {
console.log('Error: %s', err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: 'en-US',
slow: false,
host: 'https://translate.google.com',
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
})
},
broadcast: function(text){
const ips = '192.168.0.15'.split(","); //From config, 192.168.68.105,192.168.68.107,192.168.68.124
for (var s of ips){
App.run(s, text);
}
}
}
App.broadcast("Broadcasted to all of the devices"); //Only works if you did step 4.5
The error you reported:
C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72
callback(null, response.status.volume);
^
TypeError: callback is not a function
at C:\Users\Phil\Documents\google home\node_modules\castv2-client\lib\controllers\receiver.js:72:5
seems to be related to the invocation of the method setVolume in your client:
client.setVolume({ level: 1 });
Please, consider review the source code of receiver.js in the castv2-client library:
ReceiverController.prototype.setVolume = function(options, callback) {
var data = {
type: 'SET_VOLUME',
volume: options // either `{ level: 0.5 }` or `{ muted: true }`
};
this.request(data, function(err, response) {
if(err) return callback(err);
callback(null, response.status.volume);
});
};
The library is claiming because you aren't providing a proper callback when invoking that function.
I have never used the library but probably providing something similar to the following callback could be of help:
client.setVolume({ level: 1 }, function(err, volume) {
if (err) {
// Handle error as appropriate
console.log('Error on setVolume:', err);
} else {
console.log('Volume:', volume)
}
});
Your final code would look like this:
var Client = require('castv2-client').Client;
var DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver;
const googleTTS = require('google-tts-api');
var App = {
playin: false,
DeviceIp: "",
Player: null,
GoogleHome: function (host, url) {
var client = new Client();
client.connect(host, function () {
client.launch(DefaultMediaReceiver, function (err, player) {
client.setVolume({ level: 1 }, function(err, volume) {
if (err) {
// Handle error as appropriate
console.log('Error on setVolume:', err);
} else {
console.log('Volume:', volume)
}
});
var media = {
contentId: url,
contentType: 'audio/mp3',
streamType: 'BUFFERED'
};
App.Player = player;
App.Player.load(media, { autoplay: true }, function (err, status) {
App.Player.on('status', function (status) {
if (status.playerState === "IDLE" && App.playin === false) {
client.close();
}
});
});
});
});
client.on('error', function (err) {
console.log('Error: %s', err.message);
client.close();
});
},
run: function (ip, text) {
App.DeviceIp = ip;
const url = googleTTS.getAudioUrl(text, {
lang: 'en-US',
slow: false,
host: 'https://translate.google.com',
});
App.GoogleHome(App.DeviceIp, url, function (res) {
console.log(res);
})
},
broadcast: function(text){
const ips = '192.168.0.15'.split(","); //From config, 192.168.68.105,192.168.68.107,192.168.68.124
for (var s of ips){
App.run(s, text);
}
}
}
App.broadcast("Broadcasted to all of the devices"); //Only works if you did step 4.5

Error in my nodejs code

I am writing this code as a project for a customer
and when i go to a show route i got this 500 internal server error
http.get('/files/:id', function(req, res) {
var vid;
var pap;
Videos.find({}, function(err, videos) {
if (err) {
console.log(err);
} else {
vid = videos;
}
});
Papers.find({}, function(err, file) {
if (err) {
console.log(err);
} else {
pap = file;
}
});
Material.findById(req.params.id, function(err, found) {
if (err) {
console.log(err);
} else {
res.render('files', {
file: pap,
video: vid,
current: found
});
}
});
});
this is my show route code.
Note : if i reload the page the error is gone and the page open.
The reason is you need to wait for all the database queries to finish before rendering. In your code, it is possible for the page to render before the other two queries have completed and returned their data. The good news is that Mongoose supports Promises for asynchronous functions.
http.get('/files/:id', function(req, res) {
Promise.all([
Videos.find({}).exec(),
Papers.find({}).exec(),
Material.findById(req.params.id).exec()
]).then( ([video, paper, material]) => {
res.render('files', {
file: paper,
video: video,
current: material
});
}).catch( error => console.log(error) );
});
The functions you're using with Mongoose are asynchronous in nature; the variables vid and pap are not initialized when you run res.render. When you attempt to use those variables in your frontend (template like Jade, Handlebars EJS, I don't know what you're using), they are undefined, and subsequently cause the 500 error. You'll need to run the functions such that the results of all Mongoose queries are available to res.render when it runs; either using an async NodeJS library, or calling each function within one another and then calling res.render at the end.
Solution 1: Using async Node module
var async = require('async');
async.parallel([
// Each function in this array will execute in parallel
// The callback function is executed once all functions in the array complete
function (cb) {
Videos.find({}, function(err, videos) {
if (err) {
return cb(err);
} else {
return cb(null, videos);
}
});
},
function (cb) {
Papers.find({}, function(err, papers) {
if (err) {
return cb(err);
} else {
return cb(null, papers);
}
});
},
function (cb) {
Material.findById(req.params.id, function(err, found) {
if (err) {
return cb(err);
} else {
return cb(null, found);
}
});
}
], function (err, results) {
if (err) {
// If any function returns an error
// (first argument), it will be here
console.log(err);
}
else {
// Even though the functions complete asynchronously,
// the order in which they are declared in the array
// will correspond to the position in the array
// if it returns anything as a second argument.
var videos = results[0];
var files = results[1];
var found = results[2];
res.render('files', {
file: files,
video: videos,
current: found
});
}
});
Solution 2: Nested Callbacks
Videos.find({}, function(err, videos) {
var vid = videos;
if (err) {
console.log(err);
} else {
Papers.find({}, function(err, file) {
var pap = file;
if (err) {
console.log(err);
} else {
Material.findById(req.params.id, function(err, found) {
if (err) {
console.log(err);
} else {
res.render('files', {
file: pap,
video: vid,
current: found
});
}
});
}
});
}
});

How to force a function to execute first in Jasmine test?

I have a route in Node that gets an auth key. I want to use this auth key in all my jasmine tests as a parameter in the URL request. I want SetUp function to run, set a global var, then allow me to use this variable in all the rest of the test cases.
SetUp Function
var global_key = request({
uri : 'http://localhost:3000/grabToken',
method : 'GET'
},
function (err, body, res) {
if (err) { console.log(err);}
else {
return body['auth_key'];
}
});
Test Suite
function testCases() {
describe(TEST_SUITE, function() {
describe("GET /retrieveSecret/VALID_UUID", function() {
it('Requests the secret - Successful response', function(done) {
// ...
}
}
}
}
You could use asynchronous version of beforeAll function:
describe(TEST_SUITE, function() {
let key;
beforeAll(function (done) {
const params = { uri: 'http://localhost:3000/grabToken', method: 'GET' };
request(params, function (err, body, res) {
if (err) {
console.log(err);
done.fail();
} else {
key = body['auth_key'];
done();
}
});
})
describe("GET /retrieveSecret/VALID_UUID", function() {
it('Requests the secret - Successful response', function(done) {
// `key` is available here
}
});
})

Make multiple callbacks from node js asynchronous function

How can I return a object of data returned by asynchronous function called multiple times from within a asynchronous function.
I'm trying to implement like this :
var figlet = require('figlet');
function art(dataToArt, callback)
{
var arry[];
figlet(dataToArt, function(err, data) {
if (err) {
console.log('Something went wrong...');
console.dir(err);
return callback('');
}
arry[0] = data;
callback(arry);
});
figlet(dataToArt, function(err, data) {
if (err) {
console.log('Something went wrong...');
console.dir(err);
return callback('');
}
arry[1] = data;
callback(arry);
});
}
art('Hello World', function (data){
console.log(data);
});
How can I do it correctly, I searched and searched but couldn't find a solution.
Ps. I'm using Figlet.js
I don't know if you're ok using an external module, but you can use tiptoe.
Install it using npm install tiptoe like any regular module and it basically goes like this:
var tiptoe = require('tiptoe')
function someAsyncFunction(obj, callback) {
// something something
callback(null, processedData);
}
tiptoe(
function() {
var self = this;
var arr = ['there', 'are', 'some', 'items', 'here'];
arr.forEach(function(item) {
someAsyncFunction(item, self.parallel());
});
},
function() {
var data = Array.prototype.slice.call(arguments);
doSomethingWithData(data, this);
},
function(err) {
if (err) throw (err);
console.log('all done.');
}
);
the someAsyncFunction() is the async function you want to call does something and calls the callback parameter as a function with the parameters error and data. The data parameter will get passed as an array item to the following function on the tiptoe flow.
Did it Myself :) Thanks to mostafa-samir's post
var figlet = require('figlet');
function WaterfallOver(list, iterator, callback) {
var nextItemIndex = 1;
function report() {
nextItemIndex++;
if(nextItemIndex === list.length)
callback();
else
iterator([list[0],list[nextItemIndex]], report);
}
iterator([list[0],list[1]], report);
}
var FinalResult = [];
WaterfallOver(["hello","Standard","Ghost"], function(path, report) {
figlet.text(path[0], { font: path[1] }, function(err, data) {
if (err) {
FinalResult.push("Font name error try help");
report();
return;
}
data = '<pre>.\n' + data + '</pre>';
FinalResult.push(data);
report();
});
}, function() {
console.log(FinalResult[0]);
console.log(FinalResult[1]);
});

Delete Folder From File system in phonegap for IOS [duplicate]

I'm trying to delete a directory and it's contents with PhoneGap on Android using:
deleteDirectory = function deleteDirectory(uri) {
uri = uri.substring(0, uri.lastIndexOf('/'));
return $.Deferred(function (def) {
fileSystem.root.getDirectory(uri, {
create: false
}, function (directory) {
directory.removeRecursively();
def.resolve();
}, function (error) {
resolveError("Error deleting directory: ", error, def);
});
}).promise();
}
with the following error: File No Modification Allowed Error
I've confirmed this permission is set:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Where else should I be looking?
I have done it with this approach:
function ClearDirectory() {
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, fail);
function fail(evt) {
alert("FILE SYSTEM FAILURE" + evt.target.error.code);
}
function onFileSystemSuccess(fileSystem) {
fileSystem.root.getDirectory(
"yours/dir/ect/ory",
{create : true, exclusive : false},
function(entry) {
entry.removeRecursively(function() {
console.log("Remove Recursively Succeeded");
}, fail);
}, fail);
}
}
From this answer:
I'd suggest using resolveLocalFileSystemURL if you want to access
locations under cordova.file.* (eg cordova.file.dataDirectory), which
is most of the time (if not always), and use requestFileSystem if you
need to have access to the root of the filesystem.
This also saves some lines of code and is more readable:
deleteFolder(fileName: string) {
const uri = `${cordova.file.dataDirectory}${fileName}`;
window.resolveLocalFileSystemURL(uri, (dirEntry: DirectoryEntry) => {
dirEntry.removeRecursively(
() => console.log('successfully deleted the folder and its content'),
e => console.error('there was an error deleting the directory', e.toString())
)
});
}
And here an awaitable version:
deleteFolder(fileName: string): Promise<void> {
const promise = new Promise<void>((resolve, reject) => {
const uri = `${cordova.file.dataDirectory}${fileName}`;
window.resolveLocalFileSystemURL(uri, (dirEntry: DirectoryEntry) => {
dirEntry.removeRecursively(() => resolve(), e => reject(e));
}, e => reject(e));
});
return promise;
}

Categories