encapsulation issue with making an array of objects - javascript

Would anyone mind explaining to me why...
$(document).ready(function() {
var scu = ['0291285', '0409338', '0521704', '0521990', '0523652', '0523657', '0523660', '0523704'];
var inData = $('#output');
var testdiv = $('#testdiv');
function Item(scu, description, price, extended, type) {
this.scu = scu;
this.description = description;
this.price = price;
this.extended = extended;
this.type = type;
//this.saved = function() {};
}
var rows = [];
function get() {
inData.html('');
$.each(scu, function(index, val) {
$.post('chBuild.php', {scu:val}, function(output) {
$.each(output, function(i, obj) {
var i = 0;
rows[i] = new Item(obj.scu, obj.description, obj.price, obj.extended, obj.type);
console.log(rows[i].price)
//this logs every object but...
i =+ 1;
});
}, 'json');
});
console.log(rows[0].price);
//this says rows[0] is undefined?
}
inData.click(get);
});
I am trying to find the best way to create and store multiple objects.

$.post('chBuild.php', {scu:val}, function(output) {
$.each(output, function(i, obj) {
var i = 0;
rows[i] = new Item(obj.scu, obj.description, obj.price, obj.extended, obj.type);
console.log(rows[i].price)
i =+ 1;
});
}, 'json');
Here the call to $.post is asynchronous, is going to be filled just when the ajax call returns. Maybe you should make it synchronous
$.ajax({'url': 'chBuild.php', 'async': false, ...);

It's because $.post is asynchronous. The each only start the HTTP request, but it returns immediatly, so when the second console.log runs the item had not been created yet.

Related

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

Why can't I access JS object properties?

I have the following function:
function getAggregateData(){
var sums = new Object();
$.getJSON("example.json.php", function(data) {
//for each month
c = 0;
$.each(data, function(key, val, index) {
//for each store
$.each(val, function(key2, val2, index2) {
if(c == 0){
sums[key2] = val2;
}
else{
sums[key2] += val2;
}
});
c++
});
})
return sums;
}
Which I then call as such:
var totals = getAggregateData();
But when I console log I am totally stumped:
console.log(totals)
reveals an object like this:
store1 500
store2 900
store3 750
and so on and so forth...
but when I do console.log(totals['store1') I get undefinded.
I have also tried console.log(totals.store1)
and console.log(totals[0].store1)
I am having some type of issue of scope, or I am not creating the object I think I am.
It looks like the function would be returning an empty object since it's not waiting for the AJAX call to finish.
If you tried doing console.log(totals.store1) on the last line inside your $.getJSON callback you'll probably get a result.
You'll need to put any code that requires data from "example.json.php" inside a callback that only gets run after the AJAX call has returned.
E.g.
function getAggregateData(){
var sums = new Object();
$.getJSON("example.json.php", function(data) {
//for each month
c = 0;
$.each(data, function(key, val, index) {
//for each store
$.each(val, function(key2, val2, index2) {
if(c == 0){
sums[key2] = val2;
}
else{
sums[key2] += val2;
}
});
c++
});
processAggregateData(sums);
})
}
function processAggregateData(totals) {
console.log(totals.store1);
}
getAggregateData();
given:
{
"1": {
"store1": 2450,
"store2": 1060,
"store3": 310
},
"2": {
"store1": 2460,
"store2": 1760,
"store3": 810
}
};
This should work if you intend to add the results for each store.
/**
* This functions need to be called when we have the data
*/
function processSums(obj){
console.log(obj);
}
function getAggregateData(){
var sums = {};
$.getJSON("example.json.php", function(data) {
$.each(data, function() {
$.each(this, function(key, val, index){
sums[key] = sums[key] || 0;
sums[key] += val;
});
});
// 4910
processSums(sums);
});
return sums;
}
getAggregateData();

Computed observable calculated from async data

In my view model, I have an observable array that needs to be populated from a $.getJSON call. I would like to have a computed observable to represent the total of a "price" column contained in the JSON returned.
I've managed to populate the observable array...
(function($){
function Coupon(expiration, value) {
var self = this;
self.expiration = expiration;
self.value = value;
}
$(function() {
$.when($.getJSON(coupons_url, null)).done(function(couponsJson) {
ko.applyBindings({
coupons: ko.utils.arrayMap(couponsJson[0].objects,
function(coupon) {
return new Coupon(coupon.expiration, coupon.value);
})
savingsAvailable: ko.computed(function() {
var total = 0;
for (var i = 0; i < this.coupons().length; i++) {
total += parseFloat(this.coupons()[i].value / 100);
}
return total.toFixed(2);
})
});
});
});
})(jQuery);
...but I'm not sure how to then access the value of coupons when I try to populate the computed observable. this.coupons() errors out: "this.coupons() is not a function". What do I need to do to accomplish this, and/or what am I doing wrong?
ko.computed() takes a second parameter that defines the value of "this" when evaluating the computed observable. So what ever object hold "coupons" you would want to pass that in as the second parameter.
Or you could try something like the following create a view model instead of defining the object on the fly and passing it as a parameter to applyBindings.
var Coupon = function(expiration, value) {
var self = this;
self.expiration = expiration;
self.value = value;
}
var viewModel = function(couponsJson){
var self = this;
self.coupons = ko.utils.arrayMap(couponsJson[0].objects, function(coupon) {
return new Coupon(coupon.expiration, coupon.value);
})
self.savingsAvailable = ko.computed(function() {
var total = 0;
for (var i = 0; i < self.coupons().length; i++) {
total += parseFloat(self.coupons()[i].value / 100);
}
return total.toFixed(2);
})
}
(function($){
$(function() {
$.when($.getJSON(coupons_url, null)).done(function(couponsJson) {
var vm = new viewModel(couponsJson)
ko.applyBindings(viewModel);
});
});
})(jQuery);

Return a value with jQuery each() function

i'm new to javascript, and I would like to retrieve values from JSON and push it into an array so that I can parse again this array in another function, But I don't know how to return the array after pushing element inside it.
In the following script I can't display values in items
function gC(b,c,p) {
$.getJSON('getmonths', 'b='+b+'&c='+c+'&p='+p, processJSON);
}
function processJSON(data) {
var retval = [];
$.each(data, function(key, val) {
retval.push(val);
//alert(retval.pop());
});
return retval;
}
$(document).ready(function(){
var b = $("#b").val();
var c = $("#c").val();
var p = $("#p").val();
var items = [];
items = gC(b,c,p);
var i = 0;
$('td').each(function(index) {
$(this).attr('bgcolor', items[i]);
i++;
}
How could I access the array ?
thank !
You don't return from an AJAX call, you have it call a callback function when it's done.
function gC(b,c,p) {
var retval = [];
$.getJSON('getmonths', 'b='+b+'&c='+c+'&p='+p, processData);
}
function processData(data){
var retval = [];
$.each(data, function(key, val) {
retval.push(val);
//alert(retval.pop());
});
alert(retval);
}
processData would be called when the AJAX call is done. This can't return a value to another function, so all your logic has to be inside this callback function.
UPDATE: You can also pass in a callback function to gC to be called when it's done.
function gC(b,c,p,f) {
var retval = [];
$.getJSON('getmonths', 'b='+b+'&c='+c+'&p='+p, function(d){
if(typeof f == 'function'){
f(d);
}
});
}
Then you call gC like so:
gC(b,c,p,function(data){
var retval = [];
$.each(data, function(key, val) {
retval.push(val);
//alert(retval.pop());
});
alert(retval);
});
UPDATE2: I saw the code you added to the question. This needs to be done in the callback.
gC(b,c,p,function(data){
var items = [];
$.each(data, function(key, val) {
items.push(val);
});
$('td').each(function(index){ // You don't need a separate i variable
// you can just use the index from the loop
$(this).attr('bgcolor', items[index]);
}
})
Just have the code inside the callback:
function processJSON(data) {
var retval = [];
$.each(data, function(key, val) {
retval.push(val);
});
$('td').each(function(index) {
if (index < retval.length)
$(this).attr('bgcolor', retval[index]);
});
}

Javascript - Array of 'classes'

I'm trying to create an array of 'classes' like so:
function Main(initURL){
var item_array = [];
this.initURL = initURL;
function construct() {
$.ajax({
url: initURL,
dataType: 'json',
success: function(data){
for(var i=0;i<data.length;i++){
var item = new Item(data[i]);
item_array.push(item);
}
init();
}
});
}
function init() {
setInterval(update, 1000);
}
function update() {
for(var item in item_array){
console.log(item.name);
}
}
construct();
}
function Item(data) {
var dirty = false;
function init(data) {
this.id = data.pk;
this.name = data.fields.name;
}
init(data);
}
When attempting to print out the item name, I'm getting "undefined". Is there more to it than this? Data.field.name is definitely set.
A for..in loop loops through keys, not values. Change it to:
for(var i = 0; i < item_array.length; i++) {
console.log(item_array[i].name);
}
Don't use for... in to iterate over an array.
for(var i=0; i < item_array.length; i++){
console.log(item_array[i].name);
}
https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in#Description
The problem is that you're calling your "init()" function in "Item()" without any context object. Thus, this isn't the object you want it to be. Try this change:
function Item(data) {
var item = this;
function init(data) {
item.id = data.pk;
item.name = data.fields.name;
}
init(data);
}
Now, I'm not sure why you'd want to write the function that way in the first place; that little "init()" function doesn't really do anything useful. It could just be:
function Item(data) {
this.id = data.pk;
this.name = data.fields.name;
}
and that would work too.

Categories

Resources