0

I have an observable array of items in which each item has a collection of images (example below):

{"itemImages":[{"id":4,"itemId":9,"itemImageUrl":"$_57.JPG","createdOn":"2015-02-25T21:41:38.193","modifiedOn":"2015-02-25T21:41:38.193"}],"id":9,"itemName":"Item 1","itemDescription":"Item 1","itemPrice":5.00,"itemQuantity":1,"itemInStock":1,"createdOn":"2015-02-25T21:41:38.193","modifiedOn":"2015-02-25T21:41:38.193"}

OR the below JSON when no image available:

{"itemImages":[],"id":9,"itemName":"Item 1","itemDescription":"Item 1","itemPrice":5.00,"itemQuantity":1,"itemInStock":1,"createdOn":"2015-02-25T21:41:38.193","modifiedOn":"2015-02-25T21:41:38.193"}

The issue I am having is that the Image collection is not always populated and in these instances, I am getting the error:

Uncaught TypeError: Cannot read property 'itemImages' of undefined

I get that the error is saying the collection is undefined and have been trying to handle this by setting the image to a 'no-image' image in these cases. Even with this check, the error is still being thrown which implies I am writing the computed observable logic incorrectly.

@section scripts {
    <script>
        $(function () {
            var ViewModel = function () {

                var self = this;

                self.items = ko.observableArray(@Html.Raw(Model.ItemsJSON));
            };


        var vm = new ViewModel();

        // Iterates over each item.
        _.each(vm.items(), function (item) {

            var self = this;

            item.imageUrl = ko.computed(function () {
                if (item.itemImages[0].length == 0)
                { var fileName = 'no-image-jpg' }
                else { fileName = item.itemImage[0].itemImageUrl }

                var url = '@Model.ImageUrlPrefix' + fileName

                return url;
            });
        //End Foreach loop.
        });

        ko.applyBindings(vm);

And then for the Html, I am iterating over the item observable array on the viewmodel.

<div id="list-container" class="row">
    <div data-bind="template: { name: 'list-template', foreach: items }"></div>
</div>

<script type="text/html" id="list-template">
    <div>
        <div>
            <div>
            <img data-bind="attr: { src: imageUrl }"/>
            </div>
      ...

Would appreciate if anyone could point out why my computed observable is not catching undefined collections and setting the fileName to 'no-image.jpg' for them.

ricky89
  • 1,326
  • 6
  • 24
  • 37
  • The problem is not directly apparent from what you've posted, you'd get better help with a short-as-possible repro. – Jeroen Mar 01 '15 at 21:17
  • 2
    As to the question, you assume that the collection is undefined, but the error says "property 'itemImages' ***of*** undefined". So it is probably `this.item` that's the culprit. Consider using [the `var self = this;` idiom](http://stackoverflow.com/q/962033/419956) (or see [a Knockout-specific suggestion in their docs](http://knockoutjs.com/documentation/computedObservables.html#managing-this)). – Jeroen Mar 01 '15 at 21:20

1 Answers1

0

As @Jeroen pointed out, you are on the wrong track. It is not the itemImages property which is undefined. It fails to find a property in an undefined object!

Which means that you think you're accessing your view model, but you're actually referencing undefined.

You are probably accessing this incorrectly. You assume that it's the ViewModel, but it's something else. Please refer to this section of the docs for details on how to fix it.

In short: you need to create a variable that keeps the reference to this accross functions and computeds. For example:

function AppViewModel() {
    var self = this;

    self.firstName = ko.observable('Bob');
    self.lastName = ko.observable('Smith');
    self.fullName = ko.computed(function() {
        return self.firstName() + " " + self.lastName();
    });
}
Guilherme Rodrigues
  • 2,818
  • 1
  • 17
  • 22
  • I have been playing around with this for a bit. If there is an image in the itemImages collection on the item, then no error is thrown and the image displays as wanted. It is just when the collection is empty. I will and amend the example to better give the context of what I have set up. – ricky89 Mar 03 '15 at 22:53