0

I am pretty new at using KnockoutJS as a framework, and I (obviously) encountered a question I cannot (or more likely, am not skilled enough to) find.

I have this product group and each product group contains array of products. Now, I managed to display a list of product groups and a list of all products without much problem. I want to increase product quantity, when user left-clicks a div containing product and decrease product quantity when user right-clicks a div containing products.

I have set up event handlers correctly as they get fired and handled without any problems and quantity is changed, but I cannot seem to get it to reflect on the page.

Markup:

<div class="viewProductGroup" data-bind="with: productGroupData">
    <h2 data-bind="text: Title"></h2>
    <div class="stickies" data-bind="foreach: Products">
        <div data-bind="click: $root.addProduct, 
                        event: { contextmenu: $root.removeProduct }">
            <span data-bind="text: Title"></span><br />(<span data-bind="text: Quantity"></span>)</div>
    </div>
</div>

Excerpt from JS:

function StickyExViewModel() {
    var self = this;

    self.productGroupData = ko.observable();

    // Behaviors
    self.addProduct = function (item) {
        item.Quantity++;
    }
    self.removeProduct = function (item) {
        item.Quantity--;
    }
}

ko.applyBindings(new StickyExViewModel());

Now, I am sure I don't understand something or have missed some wiring. Can you please help?

Edit

Here is JSON obtained from WCF service:

{"d":
    {"__type":"ProductGroup:#Stickyex.Services",
     "Created":"\/Date(1373407200000+0200)\/",
     "ID":1,
     "Products":[
        {"__type":"Product:#Stickyex.Services","Code":"0","ID":0,"Quantity":0,"Title":"0"},
        {"__type":"Product:#Stickyex.Services","Code":"1","ID":1,"Quantity":1,"Title":"1"},
        {"__type":"Product:#Stickyex.Services","Code":"2","ID":2,"Quantity":2,"Title":"2"}],
     "Title":"ProductGroup 1"}
}

Code to obtain JSON data (inside StickyExViewModel):

self.goToProductGroup = function(productGroup) {
    $.get(serviceUrl, { ixProductGroup: productGroup.ID }, function (data) {
        self.productGroupData(data.d);
    });
}
4
  • how do you get your data for product groups?? maybe you are accessing them in a wrong way Commented Jul 10, 2013 at 10:45
  • then do you use what to create your array?? ko.mapping.fromJSON()?? Commented Jul 10, 2013 at 11:00
  • I get JSON data from WCF service with $.get jQuery function. Commented Jul 10, 2013 at 11:01
  • can you write the code you use to convert your JSON into array? Commented Jul 10, 2013 at 11:02

4 Answers 4

1

I guess that item.Quantity is observable so you cannot use increment and decrement to it. Each observable is a function so you have to use () to get or set value:

// Behaviors
self.addProduct = function (item) {
    item.Quantity(item.Quantity() + 1);
}
self.removeProduct = function (item) {
    item.Quantity(item.Quantity() - 1);
}
Sign up to request clarification or add additional context in comments.

Comments

1

The Quantity property needs to be an obsevable. So in StickyExViewModel you will able to do this :

function StickyExViewModel() {
    var self = this;

    self.productGroupData = ko.observable();

    // Behaviors
    self.addProduct = function (item) {
        item.Quantity( item.Quantity()++);
    }
    self.removeProduct = function (item) {
        item.Quantity( item.Quantity()--);
    }
}

By converting the Quantity into an observable, your Gui will be refreshed a soon as you change the property.

3 Comments

How do I convert an item in an array of an array to observable though?
In the Product contructor write : self.Quantity = ko.observable(0); Instead of self.Quantity = 0;
Aha. So basically, I shouldn't bind directly to JSON object, but instead map it to JS objects with similar properties?
1

i think you need to make little modification on the way you convert your productGroupData into observable object.

First: productGroupData should be observableArray not observable because it's actually an array.
self.productGroupData = ko.observableArray([]);

Second to convert your JSON string into that observableArray you can take advantage of KnockoutJS mapping plugin. This 'll make your coding a lot much better. So you can use it to convert your JSON into an observableArray

ko.mapping.fromJS(yourJS_Object, {}, self.productGroupData);//in case you have JS object

OR

ko.mapping.fromJSSON(yourJSON_String, {}, self.productGroupData);//in case you have JSON string.

Notice now all your properties or objects of your productGroupData are observables so to access them you need to use ()

2 Comments

productGroupData is not an array. It is an item containing an array.
In original post, you can see, I assign to self.productGroupData value of data.d, not data. Property "d" is just a thing that WCF services attach to JSON response they generate. So, basically, root item of self.productGroupData is an item where "__type":"ProductGroup:#Stickyex.Services" is defined.
0

Thanks to all of you who tried to and succeeded in helping me, I came to a final solution. In order to update Quantity, I should not bind to JSON object directly, but instead create classes in JS that represent a model of a JSON object.

function Product(item) {
    this.ID = item.ID;
    this.Title = item.Title;
    this.Quantity = ko.observable(item.Quantity);
}

function ProductGroup(data) {
    var self = this;
    this.ID = data.ID;
    this.Title = data.Title;
    this.Products = [];
    for (var i = 0; i < data.Products.length; i++) {
        self.Products.push(new Product(data.Products[i]));
    }
}

And the part where I obtain JSON was changed to:

self.goToProductGroup = function(productGroup) {
    $.get(serviceUrl, { ixProductGroup: productGroup.ID }, function (data) {
        self.productGroupData($.map(data, function (pg) {
            return new ProductGroup(pg);
        }));
    });
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.