dk123
dk123

Reputation: 19680

KnockoutJS if statement validity

I have the following javascript:

function RandomViewModel() {
    var self = this;
    self.RnadomSquaresK = ko.observableArray([
        randomSquare(),
        randomSquare(),
    ]);
}

var randomSquare = function() { return ko.observable({
    innate: ko.observableArray([
        { star: "test1", Class: "starList", Style: {} },
        { star: "test2", Class: "starList", Style: {display:'inline'} },
        { star: "test3", Class: "starList", Style: {} },
        { star: "test4", Class: "starList", Style: {} }
    ])
})};

ko.applyBindings(new RandomViewModel());

It's basically creating an observableArray of two observableArrays each with 4 elements.

I have the following html:

<script id="starTemplate" type="text/html">
    <!-- ko if: ($index >= 0) -->
    <div data-bind="text: $index, attr: { class: Class }, style: Style"></div>
    <!-- /ko -->
</script>

<div class="starList" data-bind="template: { name: 'starTemplate', foreach: RnadomSquaresK()[0]().innate }"></div>

I'm expecting divs to be created with each of their indexes printed according to the observableArray they're bound to. (in this case: 4 divs, since all indexes should be equal or larger than 0)

What I actually get: blank.

Here's the JSFiddle link: http://jsfiddle.net/qrwBE/

If I change the if statement to ex: if: $index != 0, I'd print all 4 elements bound to the array, but I don't quite understand why in this case the first element (index 0) is being printed alongside the other 3 elements.

Am I using the if statement incorrectly?

Explanations on what's happening, and any other comments regarding javascript, etc. are much welcome.

Upvotes: 0

Views: 343

Answers (2)

Matthew Cox
Matthew Cox

Reputation: 13672

So there are a number of items to examine here:

Why isn't your foreach loop displaying divs with index values in them?

The answer is because you $index() is an observable, which are functions. So you must use it like I demonstrated above. The reason why if: $index != 0 worked is because in javascript, things that evaluate to 0 are false any non-zero evaluates to true (just like in c/c++ if you are familiar with those languages.

So this means when you wrote if: $index != 0 you where really saying, is the function $index not null or undefined? which was true so it would continue to output divs from your template.

Refactor Recommendation:

A foreach binding will automatically take care of not rendering the div tags in the event that your observableArray is empty. This means you can just remove the if check altogether ... which also would have fixed your issue.

<script id="starTemplate"a type="text/html">
    <div data-bind="text: $index, attr: { class: Class }, style: Style"></div>
</script>

Model changes:

Let's examine this function:

var randomSquare = function() { return ko.observable({
    innate: ko.observableArray([
        { star: "test1", Class: "starList", Style: {} },
        { star: "test2", Class: "starList", Style: {display:'inline'} },
        { star: "test3", Class: "starList", Style: {} },
        { star: "test4", Class: "starList", Style: {} }
    ])
})};

Here you are returning an observable that contains a property called innate which is an observable array. Why not just remove this being contained in a observable because it only makes the syntax for accessing the array more funky RnadomSquaresK()[0]().innate

Instead of that, how about the following:

var randomSquare = function() { return {
       innate: ko.observableArray([
        { star: "test1", Class: "starList", Style: {} },
        { star: "test2", Class: "starList", Style: {display:'inline'} },
        { star: "test3", Class: "starList", Style: {} },
        { star: "test4", Class: "starList", Style: {} }
    ]});
})};

which would simply accessing it to the following: RnadomSquaresK()[0].innate

When to make something Observable

Just remember that you only ever need to make something an observable ... if you intend for the UI or a subscriber function in js to be alerted when a change in the observable's state occurs. Otherwise, just make it a copied value (plain old js variable).

The dangers of mixing function declarations with function expressions (Hoisting concerns): This is purely a js note.

This is a function declaration function RandomViewModel() { }

This is a function expression var randomSquare = function() { };

These are slightly different in how they are handled by the interpretters. The first one (declaration) will be hoisted to the top of it's parent scope. Therefore in subsequent code calling RandomViewModel() will work flawlessly because it is defined before everything else thanks to the interpreter.

However, the second one (expression) will only have the variable name hoisted to the top, but it's assignment will stay where it is, which means your code would be equivalent to this:

function RandomViewModel() { }
var randomSquare;

randomSquare = function() { };

This will work fine in your case. However, as your models become more complex and more tightly coupled with one another, you may end up with issues like this scenario:

function RandomViewModel() { 
    var x = randomSquare(); //<-- would throw an undefined exception here
    alert(x);
}
var model = new RandomViewModel();

var randomSquare = function() { return 5; };

This would happen because the interpretter hoisted your var randomSquare to the top of the scope but it isn't assigned a function until after it's attemped use in RandomViewModel

Safe bet, do it a consistent, don't mix and match these to appraoches to writing functions.

Upvotes: 3

WiredPrairie
WiredPrairie

Reputation: 59763

When doing javascript expressions in a binding, you need to get the value of the property:

http://jsfiddle.net/wiredprairie/EWrsF/

if: $index() > 0

In use:

<script id="starTemplate" type="text/html">
    <!-- ko if: $index() >  0 -->
    <div data-bind="text: star, attr: { class: Class }, style: Style"></div>
    <!-- /ko -->
</script>

I admittedly don't understand the rest of your code (as the variable names and pattern you're using is challenging to understand).

Upvotes: 1

Related Questions