Javascript OOP private functions - javascript

I would like to create a constructor which can be instantiated with a json file which then is used by some private functions which in the end pass their results to a public function of the prototype. Is this the right approach?
Here more specific code:
//constructor
function queryArray(json){
this.json = json;
//init qry template with default values
function qryInit() {
var qryTemplate = {
//some stuff
}
return qryTemplate;
}
//generate array of request templates
function qryTempArray(json){
var template = qryInit();
var qryTempArray1 = [];
for(var i = 0; i < json.length; i++){
qryTempArray1.push({
'SearchIndex': json[i].SearchIndex,
'Title': json[i].Title,
'Keywords': json[i].Keywords,
'MinimumPrice': json[i].MinimumPrice,
'MaximumPrice': json[i].MaximumPrice,
'ResponseGroup': template.ResponseGroup,
'sort': template.sort
});
}
return qryTempArray1;
}
}
//function for finally building all the queries
queryArray.prototype.qryBuilder = function(){
var qryTempArray1 = [];
qryTempArray1 = qryTempArray(this.json);
//other stuff
}
If I call the qryBuilder function on an Object, I get an error
in the function qryTempArray at the json.length in the for loop (undefined).
Why that?

As the code is written above, I'm surprised you even get to the loop. It would seem you'd get undefined when you called qryBuilder();
I would expect something along the lines of the following to work.
//constructor
function queryArray(json) {
var self = this;
self.json = json;
//init qry template with default values
self.qryInit = function() {
var qryTemplate = {
//some stuff
}
return qryTemplate;
}
//generate array of request templates
self.qryTempArray = function(json) {
var template = self.qryInit();
var qryTempArray1 = [];
for (var i = 0; i < json.length; i++) {
qryTempArray1.push({
'SearchIndex': json[i].SearchIndex,
'Title': json[i].Title,
'Keywords': json[i].Keywords,
'MinimumPrice': json[i].MinimumPrice,
'MaximumPrice': json[i].MaximumPrice,
'ResponseGroup': template.ResponseGroup,
'sort': template.sort
});
}
return qryTempArray1;
}
return self;
}
queryArray.prototype.qryBuilder = function() {
var qryTempArray1 = [];
qryTempArray1 = this.qryTempArray(this.json);
return qryTempArray1;
}
var q = new queryArray([{
'SearchIndex': 0,
'Title': 'foo',
'Keywords': 'testing',
'MinimumPrice': 20,
'MaximumPrice': 40
}]);
console.log(q);
console.log(q.qryBuilder());

Related

Adding html item to infoWindow - ArcGIS Javascript API 3.18

This question regarding a web app built using the ArcGIS Javascript API 3.18. I am trying to add an dojo select to the title of an infoWindow. The select box is intended to be populated with the list of identified results. I'm trying two different ways:
1) Adding the combobox declaratively using html:
var template = new esri.InfoTemplate(layerName + "<br/><select id="id_select" data-dojo-type="dijit/form/Select"</select>,"<br/> FID : ${FID}");
The combobox is there, but I don't know how to access the combobox to add the options dynamically (via addOptions). I would normally do dijit.byId("id_select"), but considering it doesn't exist until it's created...I'm not sure how to go about this way.
2) Programmatically
With the code below, the title displays information regarding the dijit/form/select widget (It displays: [object HTML TableElement]), but not the widget itself. Wondering if this can be rectified using dijitStartup(), but I can't haven't figured out how to use it (currently trying something along the lines of myTemplate.startupDijits(mySelectBox)--not with these variable names). I tried using domConstruct like this example
var identifyTask, identifyParams, idPoint;
var identifyResults;
require([
"esri/dijit/Popup",
"esri/tasks/IdentifyTask",
"esri/tasks/IdentifyParameters",
"dijit/form/Select",
"dojo/dom-construct",
"dojo/promise/all",
"dojo/domReady!"
], function (
Popup, IdentifyTask, IdentifyParameters, Select, domConstruct, All
) {
var identifySelect;
//dojo.connect(window.myMap, "onLoad", mapReady);
mapReady(window.myMap);
function mapReady(map) {
dojo.connect(window.myMap, "onClick", runIdentifies);
}
function runIdentifies(evt) {
identifyResults = [];
idPoint = evt.mapPoint;
var layers = dojo.map(window.myMap.layerIds, function (layerId) {
return window.myMap.getLayer(layerId);
});
layers = dojo.filter(layers, function (layer) {
if (layer.visibleLayers[0] !== -1) {
return layer.getImageUrl && layer.visible
}
}); //Only dynamic layers have the getImageUrl function. Filter so you only query visible dynamic layers
var tasks = dojo.map(layers, function (layer) {
return new IdentifyTask(layer.url);
}); //map each visible dynamic layer to a new identify task, using the layer url
var defTasks = dojo.map(tasks, function (task) {
return new dojo.Deferred();
}); //map each identify task to a new dojo.Deferred
var params = createIdentifyParams(layers, evt);
var promises = [];
for (i = 0; i < tasks.length; i++) {
promises.push(tasks[i].execute(params[i])); //Execute each task
}
var allPromises = new All(promises);
allPromises.then(function (r) { showIdentifyResults(r, tasks); });
}
function showIdentifyResults(r, tasks) {
var results = [];
var taskUrls = [];
var resultNames = [];
r = dojo.filter(r, function (result) {
return r[0];
});
for (i = 0; i < r.length; i++) {
results = results.concat(r[i]);
for (j = 0; j < r[i].length; j++) {
taskUrls = taskUrls.concat(tasks[i].url);
}
}
results = dojo.map(results, function (result, index) {
var feature = result.feature;
var layerName = result.layerName;
var serviceUrl = taskUrls[index];
resultNames.push({
value: result.layerName,
label: result.layerName
});
feature.attributes.layerName = result.layerName;
var identifiedList = getIdentifiedList(resultNames);
console.log(identifiedList);
var template = new esri.InfoTemplate();
template.setTitle(identifiedList);
feature.setInfoTemplate(template);
var resultGeometry = feature.geometry;
var resultType = resultGeometry.type;
return feature;
});
if (results.length === 0) {
window.myMap.infoWindow.clearFeatures();
} else {
window.myMap.infoWindow.setFeatures(results);
}
window.myMap.infoWindow.show(idPoint);
identifySelect.on('change', function(evt) {
var identIndex = identifySelect.get("value");
console.log(identIndex);
window.myMap.infoWindow.select(identIndex);
});
return results;
}
function getIdentifiedList(options) {
identifySelect = new Select({
name: "identifySelect",
id: "id_select",
options: options
}, domConstruct.create("select"));
return identifySelect.domNode;
}
function createIdentifyParams(layers, evt) {
var identifyParamsList = [];
identifyParamsList.length = 0;
dojo.forEach(layers, function (layer) {
var idParams = new esri.tasks.IdentifyParameters();
idParams.width = window.myMap.width;
idParams.height = window.myMap.height;
idParams.geometry = evt.mapPoint;
idParams.mapExtent = window.myMap.extent;
idParams.layerOption = esri.tasks.IdentifyParameters.LAYER_OPTION_VISIBLE;
var visLayers = layer.visibleLayers;
if (visLayers !== -1) {
var subLayers = [];
for (var i = 0; i < layer.layerInfos.length; i++) {
if (layer.layerInfos[i].subLayerIds == null)
subLayers.push(layer.layerInfos[i].id);
}
idParams.layerIds = subLayers;
} else {
idParams.layerIds = [];
}
idParams.tolerance = 5;
idParams.returnGeometry = true;
identifyParamsList.push(idParams);
});
return identifyParamsList;
}
});
Hi this is kinda old but i'll give it a shot. I hope this answers your question.
So if the problem is accessing the infoWindow what you need to do is set up a listener for when it is created.
on(map.infoWindow, "show", function () {
// do something
})
I have a fiddle that shows how to access infoWindow upon creation:
https://jsfiddle.net/kreza/jpLj5y4h/

A Javascript function which creates an object which calls the function itself

I am trying to make an angular service that returns a new object.
That's fine and good and works. new MakeRoll() creates an instance. But self.add, near the end also calls new MakeRoll() and that doesn't create an instance when I call add like I think it should.
I'm probably doing this all wrong but I haven't been able to figure it out.
var services = angular.module('services', []);
services.factory('Roll', [function() {
var MakeRoll = function () {
var self = {};
self.rolls = [];
self.add = function(number, sizeOfDice, add) {
var newRoll = {};
newRoll.number = number || 1;
newRoll.sizeOfDice = sizeOfDice || 6;
newRoll.add = add || 0;
newRoll.rollDice = function() {
var result = 0;
var results=[];
for (var i = 0; i < newRoll.number; i++) {
var roll = Math.floor(Math.random() * newRoll.sizeOfDice) + 1;
result += roll;
results.push(roll);
}
newRoll.results = results;
newRoll.result = result;
newRoll.Roll = new MakeRoll();
};
self.rolls.push(newRoll);
return self;
};
self.remove = function(index) {
self.rolls.splice(index, 1);
};
self.get = function(index) {
return self.rolls[index];
};
return self;
};
return new MakeRoll();
}
]);
angular service is designed to be singleton to accomplish some business logic, so don't mix up plain model with angular service. if you want to have more objects, just create a constructor and link it in service to be operated on.
function MakeRoll() {
...
}
angular.module('service', []).factory('Roll', function () {
var rolls = [];
return {
add: add,
remove: remove,
get: get
}
function add() {
// var o = new MakrRoll();
// rolls.push(o);
}
function remove(o) {
// remove o from rolls
}
function get(o) {
// get o from rolls
}
});

How to iterate anonymous function inside each function in Knockout viewmodel

I am building a Knockout viewmodel. The model has some fields like dateFrom, DateTo, Status and so forth. In addition, there is a list of invoices.
The invoices have some pricing information, which is a price object. My main object also have a price object, which should iterate all the invoice objects and find the total price.
My problem is the following:
The code runs smooth, until I add the following in my view:
<label data-bind="text:totalPrice().price().priceExVat"></label>
Here I get an:
TypeError: $(...).price is not a function
Which refers to my:
exVat += $(ele).price().priceExVat;
I don't understand it, because in my each function, I should have the element. The element have a price() function, so why would it not work? Is it some scope issue?
My viewmodel:
function invoice(invoiceDate, customerName, pdfLink, status) {
var self = this;
self.pdfLink = pdfLink;
self.print = ko.observable(0);
self.customerName = customerName;
self.status = status;
self.pdfPagesCount = function () {
return 1;
};
self.invoiceDate = invoiceDate;
self.price = function () {
return new price(1.8, 2.1);
};
}
function price(exVat, total) {
var self = this;
self.currency = '€';
self.total = total;
self.priceExVat = exVat;
self.vatPercentage = 0.25;
self.vatAmount = self.exVat - self.total;
self.priceExVatText = function() {
return self.priceExVat + ' ' + self.currency;
};
}
var EconomicsViewModel = function (formSelector, data) {
var self = this;
self.dateFrom = data.dateFrom;
self.dateTo = data.dateTo;
self.invoices = ko.observableArray([
new invoice('05-05-2014', 'LetterAmazer IvS', "http://www.google.com","not printed"),
new invoice('05-05-2014', 'LetterAmazer IvS', "http://www.google.com", "not printed")
]);
self.totalPrice = function () {
var exVat = 0.0;
$(self.invoices).each(function (index, ele) {
console.log(ele);
exVat += $(ele).price().priceExVat;
});
return price(exVat, 0);
};
};
From what I read, totalPrice is actually a price object, you don't need to put a .price():
<label data-bind="text:totalPrice().priceExVat"></label>
EDIT:
Sorry, there were also problems on your javascript:
self.totalPrice = function () {
var exVat = 0.0;
$(self.invoices()).each(function (index, ele) { //<-- add () to self.invoices to get the array
console.log(ele);
exVat += ele.price().priceExVat; //<-- remove useless jQuery
});
return new price(exVat, 0); //<-- add 'new'
};
Check this fiddle
EDIT2:
To answer robert.westerlund's comment, you could remove $().each and replace with ko.utils.arrayForEach or even simpler use a for loop:
var arr = self.invoices();
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
exVat += arr[i].price().priceExVat;
}
Updated fiddle

Node Module Inheritance

I wrote the below listed module for an ExpressJS application. I now need to create a similar module with about 3 changed methods, and a few different instance variables. My plan is to create a superclass that has all the common (call it Common.js) and then require it for the two or more subclasses.
I generalized pointer to a tutorial might help me, but here are my specific questions:
the requires will be common, I suppose I put them in Common.js,
right?
I assume I should promote as many instance variables (the subclasses) into Common as possible?
The following could be a template fro the subclasses, with the Object.create coming at the top of the file
SubClass snippet:
var Common = require("./Common");
SubClass.prototype = Object.create(Common.prototype);
SubClass.prototype.subMethod = function() {....}
and also I assume that any submethod can refer to variables in the superclass, as well as new variables in the subclass, with as this.variableName,
BTW, how would I create new subClass instance variables?
Here is my original Code:
var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var tools = require("../tools/tools");
var Job = require("./falconJob");
var Batch = function (ticket) {
this.counts = [];
this.maxes = [];
this.errors = [];
this.done = [];
this.jobs = 0;
this.started = Date.now();
this.ended = Date.now();
this.jobBatch = {};
this.ticket = ticket;
this.batchRoot = null;
}
Batch.prototype.setup = function (frameList, req, next) {
this.group(frameList);
this.makeRoot(req, next);
}
Batch.prototype.group = function (list) {
_.forEach(list, function (obj) {
if (this.jobBatch[obj.type] == undefined) {
this.jobBatch[obj.type] = [];
}
this.jobBatch[obj.type].push(obj);
}, this);
};
Batch.prototype.makeRoot = function (req, next) {
var config = global.app.settings.config;
this.batchRoot = path.join(config.JobsPath, this.ticket);
var self = this;
fs.mkdir(this.batchRoot, function (err) {
if (err) return next(err);
var mapInfoFile = path.join(self.batchRoot, "MapInfo.json");
var mapInfo = {
Date: (new Date()).toISOString(),
Version: global.manifestVID,
Zoom: req.body.Zoom,
CenterLat: req.body.CenterLat,
CenterLon: req.body.CenterLon
};
fs.writeFile(mapInfoFile, tools.pretty(mapInfo), function (err) {
if (err) return next(err);
return next(null);
});
});
};
Batch.prototype.spawn = function () {
_.forEach(this.jobBatch, function (files, key) {
var job = new Job(key, files, this.batchRoot, this.ticket, this);
this.begin(job);
job.exec();
}, this);
};
Batch.prototype.count = function () {
var sum = 0;
for (var key in this.counts) {
sum += this.counts[key];
}
return sum;
}
Batch.prototype.total = function () {
var sum = 0;
for (var key in this.maxes) {
sum += this.maxes[key];
};
return sum;
}
Batch.prototype.fails = function () {
var sum = 0;
for (var key in this.errors) {
sum += (this.errors[key]) ? 1: 0;
};
return sum;
}
Batch.prototype.finished = function () {
var keylist = Object.keys(this.done);
if (keylist.length == 0) return false;
for (var key in this.done) {
if (this.done[key] == false) return false;
};
if (this.jobs != 0) return false;
return true;
}
Batch.prototype.rate = function () {
var speed = (this.count() * 1000) / (this.ended - this.started); // tiles / second
return speed;
}
Batch.prototype.begin = function (job) {
var type = job.type;
this.jobs++;
this.counts[type] = 0;
this.maxes[type] = 0;
this.errors[type] = false;
this.done[type] = false;
}
Batch.prototype.end = function (job) {
type = job.type;
this.jobs--;
this.errors[type] = job.errors;
this.done[type] = true;
}
Batch.prototype.update = function (status) {
type = status.layer;
this.ended = Date.now();
this.counts[type] = status.tilesCount;
this.maxes[type] = status.tilesMax;
this.done[type] = status.done;
}
module.exports = Batch;
I am surprised, no one answered. Well I have a solution, and a few tips. First, read the Mozilla developer page about an introduction to javascript inheritance: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript
Here is how I structured my "sub-module" which I can just require in and it will pull in the super-module, and then subclass it.
var _ = require('lodash'); // require any additional modules that your sub module needs
var BatchRoot = require('./Batch'); // require the super-module with the superclass
var Job = require("./falconJob"); // another module that I need
var Batch = function (ticket) {
BatchRoot.call(this, ticket); // The superclass constructor takes "ticket" as a param
// define new subclass instance variables here, e.g. this.foobar = 33;
}
Batch.prototype = new BatchRoot(); // This does the subclassing
Batch.prototype.constructor = BatchRoot; // MDN says to do this to correct the constructor pointer because it points to Batch
// this is a new subclass function, notice that I use Job which is only defined here
Batch.prototype.spawn = function () {
_.forEach(this.jobBatch, function (files, key) {
var job = new Job(key, files, this.batchRoot, this.ticket, this);
this.begin(job);
job.exec();
}, this);
};
module.exports = Batch;

How can I iterate over this Javascript object?

I have the object:
var IOBreadcrumb = function () {
this.breadcrumbs = [];
this.add = function(title, url) {
var crumb = {
title: title,
url:url
};
this.breadcrumbs.push(crumb);
};
};
Lets say I add 3 items to breadcrumbs:
IOBreadcrumb.add('a',a);
IOBreadcrumb.add('b',b);
IOBreadcrumb.add('c',c);
How can I iterate over this and print out the title, and the url?
You could add an each method:
var IOBreadcrumb = function IOBreadcrumb() {
this.breadcrumbs = [];
this.add = function(title, url) {
var crumb = {
title: title,
url:url
};
this.breadcrumbs.push(crumb);
};
this.each = function(callback) {
for (var i = 0; i < this.breadcrumbs.length; i++) {
callback(this.breadcrumbs[i]);
}
}
};
And use it like this:
var ioBc = new IOBreadcrumb();
ioBc.add('a',a);
ioBc.add('b',b);
ioBc.add('c',c);
ioBc.each(function(item) {
console.log(item.title + ' ' + item.url);
});
add a method
print = function(){
for (var i = 0; i < this.breadcrumbs.length; i++) {
var current = this.breadcrumbs[i];
console.log(current.title, current.url);
}
}
to your IOBreadcrumb object, and then just invoke it
IOBreadcrumb.print();
Jason's answer is almost there. The only improvement is that there is no need to implement your own each function if you're using jquery.
var ioBc = new IOBreadcrumb();
ioBc.add('a',a);
ioBc.add('b',b);
ioBc.add('c',c);
$.each(ioBc.breadcrumbs, function(item) {
console.log(item.title + ' ' + item.url);
});
At the very least you should use $.each from your each function if you don't want callers to access breadcrumbs directly
...
this.each = function(callback) {
$.each(this.breadcrumbs, callback);
}
...

Categories

Resources