0

Please bear with me as I've only just got in to JavaScript and I am completely new at OOP in Javascript, so... can someone please help me solve my problem? (pardon me for the length and annotations in the code)

To start: I have created 2 objects: Product, Basket A table is created on-load via the 'createProductRows()' function passing in an array of Product objects. This prints out the product information and creates a button which adds a product a the basket. And its the button (or maybe something else) that giving me the problem. *I want the button to call the addProduct() function with the index of the product within the productList array which in turn calls 2 functions; addToBasket() and display() in the Basket object.. this should add the created elements to the shopping basket table

I am unsure if I'm passing in the productList array correctly or maybe I should use prototypes for the Basket methods, any help to get this working correctly would greatly appreciated. Thanks

var productList = []; // array where product objects are to be held
var basket;
var obj;

//product constructor
var Product = function(name, description, quantity, price, gender) { 
    obj = this; // a reference to this object //could use
    this.name = name;
    this.description = description;
    this.quantity = quantity;
    this.price = price.toFixed(2);
    this.gender = gender;
};
    //product prototypes
    Product.prototype = {
        toString: function() { return this.name.toLowerCase(); }
    };
    Product.prototype.getPrice = function() {
        return '\u00A3' + this.price;
    };
    Product.prototype.getQuantity = function() {
        return this.quantity;
    };

//instantiate new products 
var shorts = new Product('Shorts', 'Stone Wash Demin Shorts', 20, 25.90, 'F');
var bag = new Product('Bag', 'Leather Shoulder Bag', 4, 50.45, 'F');
var blouse = new Product('Blouse', 'Vintage Blue Silk Polka Dot Blouse', 8, 45.99, 'F');
var boots = new Product('Boots', 'Soft Leather Brown Ankle Boots', 3, 65.35, 'F');
var belts = new Product('Belts', 'Woven Finish Fashion Belt', 15, 21.99, 'F');
var shirt = new Product('Shirt', 'Jacquard Pattern Wrangler Western Shirt', 19, 34.87, 'M');
var shoes = new Product('Shoes', 'Suede Ankle Boots', 6, 55.00, 'M');
var trousers = new Product('Trousers', 'Izod Peach Chinos', 23, 31.75, 'M');
var belt = new Product('Belt', 'Suede Casual Belt', 4, 22.98, 'M');
var hat = new Product('Hat', 'Trilby Style Brown Woven Fix', 2, 67.80, 'M');

//push all product objects to an array
productList.push(shorts, bag, blouse, boots, belts, shirt, shoes, trousers, belt, hat);

// basket constructor
var Basket = function(container, products) { // passes in the product list
    this.container = container; // this tells me where to add the data
    this.products = products; //reference to product values
    this.quantity = []; // stores quantities in bag

    for (var i=0; i < products.length; i++) { //find product

        this.quantity[i] = 0; //amount of each product in basket

        // method to add to basket
        this.addToBasket = function(index) { //reference to the product to add
            this.quantity[index]++;
            this.products[i].quantity--; // minus one from the products list
        };

        // method to remove from basket
        this.removeFromBasket = function(index) {
            if (this.quantity[index] > 0)
                this.quantity[index]--;
                this.products[i].quantity++;
        };

        //displays product
        this.display = function () {
            for (var i=0; i < this.quantity.length; i++) {
                if (this.quantity[i] > 0) {
                    var tbl = this.container
                    var row = tbl.insertRow(tbl.rows.length); // create a row element to append cells to

                    var total_price = this.quantity[i] * this.products[i].price;
                    //cell values
                    var desc = this.products[i].description; //for each value add new cell
                    var qty = this.quantity[i]
                    var price = this.products[i].price;
                    var total = total_price;
                    var remove = createRemoveBtn();

                    var cell = tbl.rows[i].insertCell(-1); // add a new cell, inserted at end of each row
                    //append cells
                    cell.appendChild(desc);
                    cell.appendChild(qty);
                    cell.appendChild(price);
                    cell.appendChild(total);
                    cell.appendChild(remove);
                    tbl.appendChild(row); // finally append the rows to the table

                    function createRemoveBtn() {
                        var btn = document.createElement('input');
                        var buttonName = products[i].name.toUpperCase(); 
                        btn.type = 'button';
                        btn.value = 'Remove';
                        btn.id = buttonName[i]; //append button names from object name
                        btn.onclick = function() {removeProduct(i);}; //test
                    return btn; 
                    };//end function 'createRemoveBtn()'
                };//end if 'quantity'
            };//end for 'basket'
        };//end function 'this.display()'
    };//end for 'products'
};//end Object 'Basket'

//create a new instance of the Basket object
basket = new Basket(document.getElementById('basketTable').getElementsByTagName('tbody')[0], productList); // *** need to create a new container for the basket

//button functions
function addProduct(item) { //add to basket function
    basket.addToBasket(item);
    basket.display();
    alert(productList[item].name + ' added to basket');
}
function removeProduct(item) { //remove item from basket
    basket.removeFromBasket(item);
    basket.display();
    alert(productList[item].name + ' removed to basket');
}

//displays product table which is called on the body onload event
function createProductRows(products) {   // passing in productList[]

    var tbl = document.getElementById('productTable').getElementsByTagName('tbody')[0]; // reference to the table to add rows to in the table body
    for (var i=0; i < products.length; i++) { // index the productsList (iterate through 0-9)

        var myProduct = products[i]; // keep a reference to each individual product - shorts, bag, blouse, etc...
        var myRow = tbl.insertRow(tbl.rows.length); // create a row element to append cells to
        var myProperties = ['name', 'description', 'quantity', 'price', 'gender']; //store the property names of the products, references to the object data

        for (var j=0; j < myProperties.length; j++) // for each property in myProperties [0-4]
        {   
            var myCell = myRow.insertCell(j); //create table cell element
            var data = myProduct[myProperties[j]]; // store property values of products
            var node = document.createTextNode(data); //add the data to a text node 
            myCell.appendChild(node); // append text node to table cell
            myRow.appendChild(myCell); // add to end of the row
        }

        var newCell = tbl.rows[i].insertCell(-1); // create a new cell, inserted at end of each row
        newCell.appendChild(createAddBtn()); // add buttons to cells
        tbl.appendChild(myRow); // finally append the rows to the table

        function createAddBtn() {
            var btn = document.createElement('input'); 
            var buttonName = products[i].name.toLowerCase(); // to be added to the button's id value
            btn.type = 'button';
            btn.value = 'Add';
            btn.id = buttonName; //append button names from object name
            btn.onclick = function() {addProduct(i);};
            return btn;
        };
    };
};
user3414871
  • 99
  • 1
  • 1
  • 8
  • 2
    You haven't actually explained what the actual problem is. What is wrong with the button? Also, if you could try to cut down the code to a minimal example (see https://stackoverflow.com/help/mcve for help in creating a good question) – Steve Mitcham Jan 21 '15 at 03:25
  • Not what you're asking about, but note that you don't need those `shorts`, `bag`, etc. variables for your products, since the only place you use them again is to add to the `productList` array: you can just add new products directly into the array like so - `productList.push(new Product('Shorts', 'Stone Wash Demin Shorts', 20, 25.90, 'F'));` – nnnnnn Jan 21 '15 at 03:28
  • Where are you appending dynamically created input type button? – Rakesh_Kumar Jan 21 '15 at 03:34
  • @Steve Mitcham I'm sorry, very new at this.. I want the button to add a product to the basket by calling the addProduct which in turn calls 2 functions within the Basket constructor function... but this just does not work – user3414871 Jan 21 '15 at 03:40
  • @Rakesh_Kumar this Add button is being created with the createProductRows() function and the remove button is being created within the Basket constructor – user3414871 Jan 21 '15 at 03:43
  • Just need to know where i'm going wrong.. – user3414871 Jan 21 '15 at 04:20

1 Answers1

0

[update]

In your case the problem may be in the way you create your closure:

btn.onclick = function() {addProduct(i);};

You could try to log and check out the console to see what the value of i is:

btn.onclick = function() {console.log('and i is:',i);addProduct(i);};

When creating closures in a loop the value of i is not what you think it is, you can probably solve it by using IIFE:

btn.onclick = (function(index) {
  return function(){
    console.log('and index is:',index);
    addProduct(index);
  };
}(i));

Problem with setting event handlers is you get the wrong invoking object.

Here is a copy from the following answer about the invoking object:

The this variable

In all the example code you'll see this referring to the current instance.

The this variable actually refers to the invoking object, it refers to the object that came before the function.

To clarify see the following code:

theInvokingObject.thefunction();

The instances where this would refer to the wrong object are usually when attaching event listeners, callbacks or timeouts and intervals. In the next 2 lines of code we pass the function, we don't invoke it. Passing the function is: someObject.aFunction and invoking it is: someObject.aFunction(). The this value does not refer to the object the function was declared on but on the object that invokes it.

setTimeout(someObject.aFuncton,100);//this in aFunction is window
somebutton.onclick = someObject.aFunction;//this in aFunction is somebutton

To make this in the above cases refer to someObject you can pass a closure instead of the function directly:

setTimeout(function(){someObject.aFuncton();},100);
somebutton.onclick = function(){someObject.aFunction();};
Community
  • 1
  • 1
HMR
  • 37,593
  • 24
  • 91
  • 160
  • okay I can see the index now.. but can you tell me where i'm going wrong in regards to setting the row data with the data specified in the basket constructor. It doesn't seem to read the product data from it. – user3414871 Jan 21 '15 at 05:01
  • the display() function within the basket constructor should create the row to be inserted into the basket – user3414871 Jan 21 '15 at 05:04
  • how do I access the product values in the Basket constructor? – user3414871 Jan 21 '15 at 05:24