0
votes

I have a shoppingcart page which I am building with KnockoutJS 3.4.2. I test this in the latest Chrome. The products in the cart are being loaded by Ajax.

The shoppingcart is then filled in an HTML table.

For some reason when I bind 'productTitle' in the TD it works but when I try to bind it in a span it won't. I also tried to use templating: <span><!--ko text: productTitle--><!--/ko--></span> but this also won't work.

Does it has something to do with the fact that when the page is loaded the observableArray could still be empty? I found that when I put 'productTitle' in the TD text bind the text works.

Also for some reason the observable count won't bind to an input on the data-bind value. Maybe the way my viewModel is constructed is wrong?

Here is my code:

HTML

<tbody data-bind="foreach: productsInCart">
                <tr data-bind="attr: {productOptionId: productOptionId}">
                    <td></td>
                    <td class="cart-title" data-bind="text: productTitle"></td>
                    <td data-bind="text: price"></td>
                    <td>
                        @*<span><!--ko text: productTitle--><!--/ko--></span>*@
                            <div click-action="" class="qtyminus"></div>
                            <input type='text' name="quantity" data-bind="value: count" class="qty"/>
                            <div click-action="" class="qtyplus"></div>                        
                    </td>
                    <td class="cart-total" data-bind="text: total"></td>@*
                    <td><a href="@Url.Action("RemoveProductOption", "ShoppingCart", new {id = product.ProductOptionId})" class="cart-remove"></a></td>*@
                </tr>
            </tbody>

JS

$(function () {  
    var productInCart = function(data) {
        var self = this;

        self.productOptionId = ko.observable(data.ProductOptionId);
        self.price = ko.observable(data.Price);
        self.count = ko.observable(data.Count);
        self.productBrandName = ko.observable(data.ProductBrandName);
        self.productOptionName = ko.observable(data.ProductOptionName);
        self.productName = ko.observable(data.ProductName);
        self.productFriendlyUrl = ko.observable(data.ProductFriendlyUrl);

        self.productTitle = ko.computed(function () {
            return self.productName() + " " + self.productOptionName();
        });

        self.total = ko.computed(function () {
            return self.count() * self.price();
        }, self);

        return self;
    }

    function viewModel() {
        var self = this;

        self.productsInCart = ko.observableArray([]);

        $.ajax({
            type: "GET",
            url: window.cartUrl,
            dataType: "json",
            contentType: 'application/json; charset=utf-8',
            traditional: true, //// traditional option to true
            success: function (result) {
                console.log(result);
                ko.utils.arrayForEach(result, function (data) {
                    self.productsInCart.push(new productInCart(data));
                });
                console.log(self.productsInCart());
            }
        });
    }

    var vm = new viewModel();
    ko.applyBindings(vm);
});

The object being loaded by AJAX:

[
{
ShoppingSessionId: 2061,
ProductOptionId: 3,
Price: 26.95,
Count: 1,
ProductBrandName: "G-Star",
ProductOptionName: "Maat 36/36",
ProductName: "G-Star Loose, 340166",
ProductFriendlyUrl: "g-star-3301-loose-dus",
TaxId: 1,
TaxRate: 21,
FirstProductImageId: 7,
Id: 48
}
]

One of the not able to bind errors:

Uncaught ReferenceError: Unable to process binding "value: function (){return count }"
Message: count is not defined
    at value (eval at parseBindingsString (knockout-3.4.2.js:280), <anonymous>:3:58)
    at m (knockout-3.4.2.js:470)
    at Function.Uc (knockout-3.4.2.js:204)
    at Function.Vc (knockout-3.4.2.js:201)
    at Function.U (knockout-3.4.2.js:200)
    at Object.a.m.a.B (knockout-3.4.2.js:193)
    at init (knockout-3.4.2.js:472)
    at knockout-3.4.2.js:309
    at Object.w (knockout-3.4.2.js:149)
    at knockout-3.4.2.js:308
1

1 Answers

1
votes

NOTE: To this arriving here, the issue was caused by a third-party library (StackTable) which was augmenting the KO bindings.

It looks like your code is razor? I notice the <span></span> is commented out. Is this the problematic span you mention?

If so, I would recommend placing the binding on the <span> element itself. I am not certain the text binding supports virtual tag use.

Replace:

<span><!--ko text: productTitle--><!--/ko--></span>

With:

<span data-bind="text: productTitle"></span>

Also, as a means of "best practice" I wouldn't push the elements individual into the array. Each push causes knockout to re-evaulate dependencies, and will/might cause a redraw (i.e. not so good for performance). Instead:

  1. Build a local plain old JavaScript array.
  2. Push the items into it.
  3. Set the value of your observable array to be that of the local array you've just built.

I would also wrap the foreach in a logical check to determine that the array is not null/undefined or empty. To prevent any potential bindings errors.