I am trying to write server side for my web application using Node.js. The following code is extracted to simulate the situation. Problem is that the application crashes when trying to access this.actions.length in the actionExecuted "method". The property this.actions is undefined there (this == {} within the scope) even it was defined in the "constructor" (the Request function itself). How to make the actions property accessible from other "methods" as well?
var occ = {
exampleAction: function(args, cl, cb)
{
// ...
cb('exampleAction', ['some', 'results']);
},
respond: function()
{
console.log('Successfully handled actions.');
}
};
Request = function(cl, acts)
{
this.client = cl;
this.actions = [];
this.responses = [];
// distribute actions
for (var i in acts)
{
if (acts[i][1].error == undefined)
{
this.actions.push(acts[i]);
occ[acts[i][0]](acts[i][1], this.client, this.actionExecuted);
}
else
// such an action already containing error is already handled,
// so let's pass it directly to the responses
this.responses.push(acts[i]);
}
}
Request.prototype.checkExecutionStatus = function()
{
// if all actions are handled, send data to the client
if (this.actions == [])
occ.respond(client, data, stat, this);
};
Request.prototype.actionExecuted = function(action, results)
{
// remove action from this.actions
for (var i = 0; i < this.actions.length; ++i)
if (this.actions[i][0] == action)
this.actions.splice(i, 1);
// and move it to responses
this.responses.push([action, results]);
this.checkExecutionStatus();
};
occ.Request = Request;
new occ.Request({}, [['exampleAction', []]]);
The problem is the way you are defining your callback. It's called later so it loses context. You have to either create a closure or properly bind the this. To create a closure:
var self = this;
occ[acts[i][0]](acts[i][1], this.client, function() { self.actionExecuted(); });
To bind to this:
occ[acts[i][0]](acts[i][1], this.client, this.actionExecuted.bind(this));
Either one should work.
Related
I'm building a code to parse some JSON details received from the server into a javascript object. The object has many objects inside it.
Then I have another function to create HTML element and apply that object's values (using for - in loop) into HTML tags' "innerHTML".
I have included the code i use below,
// This one is executed on the 'onLoad' event.
function requestDriverListings() {
**//This object stores the received object from server.**
var drivers = {};
// ***This function requests details from the server and the function in the arguments is executed once the details are received.***
sendUserData ({}, "request driver.php", function (request) {
listDrivers(request,drivers); console.log(drivers); displayDrivers(drivers);});
}
This one is the function to create a HTML Element and stores the received data in it and the use JSON.parse() to parse them into a Object.
The driver parameter is the Object passed in the above code.
request parameter has no effect on this problem. (It is the XHR responseText.)
function listDrivers (request,driver) {
var response = document.createElement("html");
response.innerHTML = request;
driver = response.querySelector("#drivers").innerHTML;
var stripComma = driver.lastIndexOf(",");
driver = JSON.parse(driver.substring(0,stripComma) +"}");
}
Here is the displayDrivers function.
drivers Object is passed into driveParsed in the first function.
requestAPage() is a function to request the displaying element from the server. the function in it's arguments is the function to apply the Objects details into the HTML innerHTML.
function displayDrivers (driveParsed) {
var driverElement = document.createElement("div");
driverElement.id = "driverElement";
driverElement.style.display = "none";
document.getElementById("driverContainer").appendChild(driverElement);
requestAPage("Drivers.html", "drivers", "driverElement", function() { selectDrivers();});
var selectDrivers = function () {
for (var x=0; x<=Object.keys(driveParsed).length; x++) {
var driverParsed = driveParsed[x];
setDriversDetails(driveParsed,x);
var element = createAElement( "div", {"margin-top": "10px;"});
element.id = driveParsed.name;
element.className = "container border";
element.innerHTML = driverElement.innerHTML;
document.getElementById("driverContainer").appendChild(element);
}
};
}
================================================================
My problem is this displayDrivers() is not getting the modified drivers Object.
Please help me to solve this problem. Sorry for the long description.
One problem is that inside listDrivers you assign a new value to the driver variable (which is an argument). This means the original variable, drivers, that was passed to the function as second argument, is disconnected from the local function variable driver: they are now two distinct, unrelated objects.
If you want the drivers variable to get a value from calling the function, then let that be the return value of the function, so you would call it like this:
sendUserData ({}, "request driver.php", function (request) {
var drivers = listDrivers(request); // <-----
console.log(drivers);
displayDrivers(drivers);
});
Then the listDrivers function would look like this:
function listDrivers (request) { // <--- only one argument
// declare new variable:
var driver = response.querySelector("#drivers").innerHTML;
// ... rest of your code comes here ...
// ... and finally:
return driver; // <---- return it
}
#trincot beat me to it and his answer is better. I'll leave this up anyway though.
Try doing this in requestDriverListings:
function requestDriverListings() {
var drivers = {};
sendUserData ({}, "request driver.php", function (request) {
var updatedDrivers = listDrivers(request,drivers);
console.log(drivers);
displayDrivers(updatedDrivers);});
}
And this in listDrivers:
function listDrivers (request,driver) {
var response = document.createElement("html");
response.innerHTML = request;
driver = response.querySelector("#drivers").innerHTML;
var stripComma = driver.lastIndexOf(",");
driver = JSON.parse(driver.substring(0,stripComma) +"}");
return driver;
}
I'm attempting to call a service from within another service, then use the returned object to perform some operations. I keep running into a TypeError: getDefinitions is not a function error, however.
Below is my service is called, the service doing the calling, and my relevant controller code:
definitions.service.js:
'use strict';
angular.module('gameApp')
.factory('definitionsService', ['$resource',
function($resource) {
var base = '/api/definitions';
return $resource(base, {}, {
get: {method: 'GET', url: base}
});
}]);
utilities.service.js:
'use strict';
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
return {
description: description,
detail: detail,
severity: severity,
};
function description(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].description;
}
function detail(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].detail;
}
function severity(account) {
var key = angular.isDefined(getDefinitions().ABC[account.code]) ? account.code : '-';
return getDefinitions().IDV[key].severity;
}
var getDefinitions = function() {
definitionsService.get().$promise.then(function(data) {
return data;
});
};
}]);
controller.js:
'use strict';
angular.module('gameApp')
.controller('AccountsController', AccountsController);
AccountsController.$inject = ['$routeParams', 'customersService', 'utilitiesService'];
function AccountsController($routeParams, playersService, utilitiesService) {
var vm = this;
var playerId = $routeParams.playerId;
var getAccounts = function() {
playersService.getAccounts({
playerId: playerId
}).$promise.then(function(accounts) {
for (var i = 0; i < accounts.length; i++) {
if (angular.isDefined(accounts[i].secCode)) {
accounts[i].code = accounts[i].secCode;
accounts[i].severity = utilitiesService.severity(accounts[i]);
accounts[i].detail = utilitiesService.detail(accounts[i]);
accounts[i].description = utilitiesService.description(accounts[i]);
}
}
vm.accounts = accounts;
});
};
var init = function() {
getAccounts();
};
init();
}
Currently your service returns before your variable gets defined. That means the definition is never reached. So it is declared, as the function executes, but is undefined. Just move your variable definition to the top.
This will only prevent the definition error. Another problem is that your getDefinitions function doesn't return anything but you're calling a property on it. One solution I can think of is using a callback, that gets executed when your data is loaded:
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
var data;
reload();
var utils = {
description: description,
detail: detail,
severity: severity,
reload: reload,
loaded: null
};
return utils;
function reload() {
definitionsService.get().$promise.then(function(data) {
data = data;
if (utils.loaded && typeof utils.loaded === "function") {
utils.loaded();
}
});
}
function description(account) {
var key = angular.isDefined(data.ABC[account.code]) ? account.code : '-';
return data.IDV[key].description;
}
}]);
Then in your controller you could use the service like this:
utilitiesService.loaded(function(){
accounts[i].description = utilitiesService.description(accounts[i]);
})
old question but still relevant. To expand on Florian Gl's answer above if you have a service with multiple functions and one or more of those functions requires a "pre-service" function to be called for example to load some resource data in like configuration information move that service call to the top, outside of the nested function (in this case below I am dealing with the promise scenario in JavaScript):
angular.module('gameApp')
.factory('utilitiesService', ['definitionsService', function(definitionsService) {
var myFirstConfigValue = '';
// call any all services here, set the variables first
configurationService.GetConfigValue('FirstConfg')
.then(function (response) {
// set the local scope variable here
myFirstConfigValue = response;
},
function() { });
function myTestFunction() {
// make an ajax call or something
// use the locally set variable here
ajaxService.functionOneTwo(myFirstConfigValue)
.then(response) {
// handle the response
},
function(err) {
// do something with the error
});
}
}]);
Key point to note here is that if you need to load in some data you do that first outside of any other functions inside your service (e.g. you want to load some JSON data).
So, I have an object Async that creates a request for (in this case) a JSON object from github.
There is a method Async.createList that creates a list of all instances of a specific attribute from the github JSON object. It works just fine when Async.createList is called once, but I want to be able to create multiple lists from different target attributes from the same request, and this is where it fails.
Ideally, the lists would be appended to the Async.lists object so that they can be used outside of the Async object. Right now, when I call Async.createList multiple times, only the last call appends to Async.lists.
function Async(address) {
this.req = new XMLHttpRequest();
this.address = address
}
Async.prototype = {
lists : {},
create : function(callback) {
var self = this;
this.req.open('GET', this.address, true);
this.req.onreadystatechange = function(e) {
if (this.readyState == 4) {
if (this.status == 200) {
dump(this.responseText);
if (callback != null) callback()
} else {
dump("COULD NOT LOAD \n");
}
}
}
this.req.send(null);
},
response : function(json) {
return json == true ? JSON.parse(this.req.responseText) : this.req.responseText
},
createList : function(target) {
var self = this
var bits = []
this.req.onload = function(){
var list = self.response(true)
for(obj in list){
bits.push(self.response(true)[obj][target])
}
self.lists[target] = bits
}
},
}
I am creating the object and calling the methods like this:
var github = new Async("https://api.github.com/users/john/repos")
github.create();
github.createList("name");
github.createList("id");
And then trying:
github.lists
You are re-assigning the function for this.req.onload every time you call github.createList.
i understand you want to do things after the request is loaded using req.onload, but you are assigning a new function everytime, so the last assigned function will be called.
You need to remove the onload function within Async.createList
Call github.createList("name"); only after the request is loaded as follows
var github = new Async("https://api.github.com/users/john/repos")
github.create();
github.req.onload = function () {
github.createList("name");
github.createList("id");
}
The answer is painfully simple. All I needed to do was get rid of the onload function within Async.createList and simple call Async.createList in the callback of Async.create
createList : function(target) {
var self = this
var bits = []
var list = self.response(true)
for(obj in list){
bits.push(self.response(true)[obj][target])
}
self.lists[target] = bits
},
Using this to instantiate:
var github = new Async("https://api.github.com/users/john/repos")
github.create(callback);
function callback(){
github.createList("name");
github.createList("id");
}
is there a way to define global namespace, so that i can call function from this namespace from all my page?
e.g
// in one file i define below code
DefineNameSpace("my.namespace.api", {
addObject: function(obj) {
// store obj into indexDB
},
readAllObject: function() {
// return array of object from indexdb
}
})
// so that in another javascript file i can do
my.namespace.api.addObject({name: "foo", desc: "bar"});
is there a way to implement "DefineNameSpace" method?
Thanks
one way to do it, which is very simple, is this:
my = {
namespace: {
api : {}
}
}
my.namespace.api.addObject = function (obj) { }
you're actually creating objects but in this way it will function as a namespace just as well :)
hm it's not the method you're implementing. But building a namespace with a method would require the function to be called before the script files are loaded where the namespace is used like that, otherwise those lines of code are called before the DefineNamespace method is called and you will run into parts of namespaces that are undefined at that point. With above solution that won't be the case, although it is not dynamic unfortunately.
building a namespace dynamically can be done in the following way:
// the root of the namespace would still be handy to have declared here
var my = {};
function defineNamespace(namespaceStr) {
var namespaceSegments = namespaceStr.split(".");
var namespaceSoFar = null;
// iterate through namespace parts
for (var i = 0; i < namespaceSegments.length; i++) {
var segment = namespaceSegments[i];
if (i == 0) {
// if namespace starts with my, use that
if (segment == "my") {
// set pointer to my
namespaceSoFar = my;
}
else {
// create new root namespace (not tested this, but think this should work)
var otherNamespace = eval(segment);
if (typeof otherNamespace == "undefined") {
eval(segment + " = {};");
}
// set pointer to created root namespace
namespaceSoFar = eval(segment);
}
}
else {
// further build the namespace
if (typeof namespaceSoFar[segment] == "undefined") {
namespaceSoFar[segment] = {};
}
// update the pointer (my -> my.namespace) for use in the next iteration
namespaceSoFar = namespaceSoFar[segment];
}
}
}
I am obviously missing some concept/understanding and most definitely javascript OO basics!
I am loving using RequireJS, and my web app now looks more like a structured app now rather than a whole heap of crazy code.
I am just struggling to understand how/if the following is possible.
I have a module which acts as a base dataservice module called dataservice_base as follows:
define(['dataservices/dataservice'], function (dataservice) {
// Private: Route URL
this.route = '/api/route-not-set/';
var setRoute = function (setRoute) {
this.route = setRoute;
return;
}
// Private: Return route with/without id
var routeUrl = function (route, id) {
console.log('** Setting route to: ' + route);
return route + (id || "")
}
// Private: Returns all entities for given route
getAllEntities = function (callbacks) {
return dataservice.ajaxRequest('get', routeUrl())
.done(callbacks.success)
.fail(callbacks.error)
};
getEntitiesById = function (id, callbacks) {
return dataservice.ajaxRequest('get', routeUrl(this.route, id))
.done(callbacks.success)
.fail(callbacks.error)
};
putEntity = function (id, data, callbacks) {
return dataservice.ajaxRequest('put', routeUrl(this.route, id), data)
.done(callbacks.success)
.fail(callbacks.error)
};
postEntity = function (data, callbacks) {
return dataservice.ajaxRequest('post', routeUrl(this.route), data)
.done(callbacks.success)
.fail(callbacks.error)
};
deleteEntity = function (id, data, callbacks) {
return dataservice.ajaxRequest('delete', routeUrl(this.route, id), data)
.done(callbacks.success)
.fail(callbacks.error)
};
// Public: Return public interface
return {
setRoute: setRoute,
getAllEntities: getAllEntities,
getEntitiesById: getEntitiesById,
putEntity: putEntity,
postEntity: postEntity,
deleteEntity: deleteEntity
};
});
As you can see, I am referencing dataservices/dataservice, which is actually the core AJAX call mechanism (not shown, but really just basic jQuery ajax call in a wrapper).
What I am trying to do is allow this base dataservice module to be "instanced" as follows (within another module - snippet code only):
define(['dataservices/dataservice_base', 'dataservices/dataservice_base', 'dataservices/dataservice_base'], function (dataservice_profile, dataservice_qualifications, dataservice_subjects) {
// Set the service route(s)
dataservice_profile.setRoute('/api/profile/');
dataservice_qualifications.setRoute('/api/qualification/');
dataservice_subjects.setRoute('/api/subject/');
As you can see, I am trying to include the same dataservice_base(defined above) 3 times, but in the function references, I am trying to refer to each instance by named vars i.e:
dataservice_profile, dataservice_qualifications, dataservice_subjects
.. and of course I am trying be able to set a unique setRoute value for each of those instances to use further on in the module.. whilst leveraging the common calls (get,puts,posts etc).
Obviously I am missing a few things here.. but any help to point me back on the road would be very gratefully received!!
Kind Regards,
David.
I think you need to include your dependency only once and use the new keyword. Possibly you will need to refactor so that the common functions are in a depending module:
define(['dataservices/dataservice'], function (dataservice) {
var dataservice_profile = new dataservice();
var dataservice_qualifications = new dataservice();
var dataservice_subjects = new dataservice();
// Set the service route(s)
dataservice_profile.setRoute('/api/profile/');
dataservice_qualifications.setRoute('/api/qualification/');
dataservice_subjects.setRoute('/api/subject/');
// define needs to return something
return {
profile: dataservice_profile,
qualifications: dataservice_qualifications,
subjects: dataservice_subjects
};
});
Yes, brain-freeze or whatever.. problems of working alone sometimes!
So, as #asgoth mentioned, quite rightly had to clear my mind and think things through a bit!
I ended up with a re-factored dataservice_base module as follows:
define(['dataservices/dataservice'], function (dataservice) {
// Set any class/static vars
// Set the instance function
function dataservice_base(setRoute) {
var self = this;
self.route = setRoute;
console.log('setting route: ' + self.route);
function routeUrl(route, id) {
console.log('** Setting route to: ' + route);
return route + (id || "")
}
self.getAllEntities = function (callbacks) {
return dataservice.ajaxRequest('get', routeUrl())
.done(callbacks.success)
.fail(callbacks.error)
}
self.getEntitiesById = function (id, callbacks) {
return dataservice.ajaxRequest('get', routeUrl(self.route, id))
.done(callbacks.success)
.fail(callbacks.error)
}
self.putEntity = function (id, data, callbacks) {
return dataservice.ajaxRequest('put', routeUrl(self.route, id), data)
.done(callbacks.success)
.fail(callbacks.error)
}
self.postEntity = function (data, callbacks) {
return dataservice.ajaxRequest('post', routeUrl(self.route), data)
.done(callbacks.success)
.fail(callbacks.error)
}
self.deleteEntity = function (id, data, callbacks) {
return dataservice.ajaxRequest('delete', routeUrl(self.route, id), data)
.done(callbacks.success)
.fail(callbacks.error)
}
} // eof instance
return dataservice_base;
}
and of course again as #asgoth mentioned, I only need to of course include one reference to the dataservice_base module, and instance it for my needs as follows:
define(['dataservices/dataservice_base','viewmodels/viewmodel_profile', 'viewmodels/viewmodel_qualifications', 'viewmodels/viewmodel_subjects', 'app/common'], function (dataservice_base, viewmodel_profile, viewmodel_qualifications, viewmodel_subjects, common) {
var dataservice_profile = new dataservice_base('/api/profile/');
var dataservice_qualifications = new dataservice_base('/api/qualification/');
var dataservice_subjects = new dataservice_base('/api/subject/');
// do whatever now with those instance objects...
}
SO.. now all working!
I guess the only other thing I need to do is looking up about cleaning up process to ensure these objects are released.. however there will only ever be a few.. but still..
thanks again #asgoth
Just return a function instead of a object like this
return function(){
return {
// your public interface goes here
};
}
Now you can create new instances of your plugin with new componentName().