Ashkan Hovold
Ashkan Hovold

Reputation: 908

use ko.computed with if statement to set object observable property

Just got started with knockout and javascript. I'm stuck at setting the object monthData's mPrice value.

function monthData(mYear, mMonth, mSS, mMs, mLimit, mPerItem, mStartingPrice) {
    var self = this;
    self.mYear= mYear;
    self.mMonth = mMonth;
    self.mSs = mSS;
    self.mMs = mMs;
    self.mTotal = mSS + mMs;
    self.mLimit = mLimit;
    self.mPerItem = mPerItem;
    self.mStartingPrice = mStartingPrice;
};

This is were it stops working. if I set self.mPrice = 0 i get all the months printed out atleast.

 self.mPrice = ko.computed(function() {
    var limit = self.mLimit;
    var perItem = self.mPerItem;
    var startingPrice = self.mStartingPrice;
    var total = self.mTotal;
    if (total <= limit) {
        return startingPrice;
    } else {
    var aboveLimit = total - limit;
    var extra = aboveLimit * perItem;
    return startingPrice + extra;
    }
});

}

function statViewModel() {
this.tak = ko.observable(100);
this.styckpris = ko.observable(10);
this.grundpris = ko.observable(500);
var self = this;

// testing with some months
self.allMonths = ko.observableArray([
    new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris),
    new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris),
    new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris),
    new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris),
    new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris)       
]);    
}

-Edit: Just after I posted I found a missing paranteses. Now I get it to print out but it says:

function d(){if(0<arguments.length){if(!d.equalityComparer||!d.equalityComparer(c,arguments[0]))d.H(),c=arguments[0],d.G();return this}b.r.Va(d);return c}NaN

Html:

<p>Tak: <input data-bind="value: tak" /></p>
<p>Styckpris: <input data-bind="value: styckpris" /></p>
<p>Grundpris: <input data-bind="value: grundpris" /></p>

<table>
<thead>
    <tr>
    <th>År</th><th>Månad</th><th>SS</th><th>MS</th><th>Total</th><th>Pris</th>
    </tr>
</thead>
<tbody data-bind="foreach: allMonths">
    <tr>
        <td><span data-bind="text: mYear"></span></td>
        <td><span data-bind="text: mMonth"></span></td>
        <td><span data-bind="text: mSs"></span></td>
        <td><span data-bind="text: mMs"></span></td>
        <td><span data-bind="text: mTotal"></span></td>
        <td><span data-bind="text: mPrice"></span></td>        
    </tr>
</tbody>
</table>

Upvotes: 1

Views: 3223

Answers (3)

Ashkan Hovold
Ashkan Hovold

Reputation: 908

Edit- this is now working as I wanted

function monthData(mYear, mMonth, mSS, mMs, parent) {
var self = this;
self.mYear = mYear;
self.mMonth = mMonth;
self.mSs = mSS;
self.mMs = mMs;
self.mTotal = mSS + mMs;

self.mPrice = ko.computed(function () {
    var limit = parent.tak();
    var perItem = parent.styckpris();
    var startingPrice = parent.grundpris();
    var total = self.mTotal;
    if (total <= limit) {
        return startingPrice;
    } else {
        var aboveLimit = total - limit;
        var extra = aboveLimit * perItem;
        var sum = parseInt(startingPrice) + parseInt(extra);
        return sum;
    }
});
}

function statViewModel() {
var self = this;

self.tak = ko.observable(100);
self.styckpris = ko.observable(10);
self.grundpris = ko.observable(500);

// testing with some months
self.allMonths = ko.observableArray([
new monthData(2013, 1, 412, 142, self),
new monthData(2013, 2, 112, 642, self),              
new monthData(2013, 2, 100, 742, self),
new monthData(2013, 3, 6513, 69, self),
new monthData(2013, 4, 34, 211, self),
new monthData(2013, 5, 123, 435, self),
new monthData(2013, 6, 412, 142, self),
new monthData(2013, 7, 412, 142, self)

]);
}

// Activates knockout.js
ko.applyBindings(new statViewModel());

Upvotes: 2

Sethi
Sethi

Reputation: 1398

Please ignore my other answer. I was getting confused between the code in your original question and the answer you posted. And so my explanation was unclear. So here I am going to answer your question, and not using anything from your answer.

The error you were receiving

function d(){if(0<arguments.length){if(!d.equalityComparer||!d.equalityComparer(c,arguments[0]))d.H(),c=arguments[0],d.G();return this}b.r.Va(d);return c}NaN

Can be simplified to

<ko.observable>NaN

And can be read as: "An observable is not a number"

This is an indication that you are trying to perform math operations to an observable.

In your if statement, you have: if(total <= limit). You cannot compare if a number (total) is less than or equal to (<=) an observable (limit). You must get the value of limit by calling it like a function.

Your computed should now look like this:

self.mPrice = ko.computed(function() {
    var limit = self.mLimit();
    var perItem = self.mPerItem();
    var startingPrice = self.mStartingPrice();
    var total = self.mTotal;
    if (total <= limit) {
        return startingPrice;
    } else {
        var aboveLimit = total - limit;
        var extra = aboveLimit * perItem;
        return startingPrice + extra;
    }
});

Only the first 3 lines need changing - minor changes at that. Nothing else.

Upvotes: 2

Sethi
Sethi

Reputation: 1398

Using an observableArray only 'asks' knockout to 'observe' the array functionality - i.e. pushing, removing, moving items, etc...

To 'ask' knockout to observe objects within that array, you actually want it to observe an objects particular properties.

So, your monthData object should declare observables instead of regular/primitive properties:

function monthData(mYear, mMonth, mSS, mMs, mLimit, mPerItem, mStartingPrice) {

    var self = this;
    self.mYear = mYear;
    self.mMonth = mMonth;
    self.mSs = mSS;
    self.mMs = mMs;
    self.mTotal = ko.observable( mSS + mMs );
    self.mLimit = ko.observable( mLimit );
    self.mPerItem = ko.observable( mPerItem );
    self.mStartingPrice = ko.observable( mStartingPrice );

    ...

}

Note, I have only chosen the last 4 to be observables because those are the properties you used in your computed. Now that they are observable the computed will know to reevaluate when any of them change.

Also, if you expect self.mTotal to auto-update (i.e. if you expect self.mSs or self.mSm to change), that will need to be a computed not an observable.

EDIT:

Don't forget to append any references to these variables with (). Your computed should now look like:

self.mPrice = ko.computed(function() {
    var limit = self.mLimit();
    var perItem = self.mPerItem();
    var startingPrice = self.mStartingPrice();
    var total = self.mTotal();
    if (total <= limit) {
        return startingPrice;
    } else {
        var aboveLimit = total - limit;
        var extra = aboveLimit * perItem;
        return startingPrice + extra;
    }
});

EDIT 2:

Apologies for not reading the question properly.

I didn't realise you were passing in tak, styckpris and grundpris - my brain may have switched off when it saw a different language :p

THE PROBLEM

So what you have done when passing in these to your monthData constructor is dereference them. You have passed the value behind the observable rather than passing the observable. By passing the value, your monthData constructor, and thus your mPrice computed, will have no way of seeing changes to the observable.

THE SOLUTION

Pass the observables into your constructor without dereferencing them.

function monthData(mYear, mMonth, mSS, mMs, mLimit, mPerItem, mStartingPrice) {

    var self = this;
    self.mYear= mYear;
    self.mMonth = mMonth;
    self.mSs = mSS;
    self.mMs = mMs;
    self.mTotal = mSS + mMs;

    /* Remember that these are observable! */
    self.mLimit = mLimit;
    self.mPerItem = mPerItem;
    self.mStartingPrice = mStartingPrice;

    self.mPrice = ko.computed(function() {
        /* So, remember to dereference them! */
        var limit = self.mLimit();
        var perItem = self.mPerItem();
        var startingPrice = self.mStartingPrice();
        var total = self.mTotal;
        if (total <= limit) {
            return startingPrice;
        } else {
            var aboveLimit = total - limit;
            var extra = aboveLimit * perItem;
            return startingPrice + extra;
        }
    });

}

function statViewModel() {

    this.tak = ko.observable(100);
    this.styckpris = ko.observable(10);
    this.grundpris = ko.observable(500);
    var self = this;

    // testing with some months
    self.allMonths = ko.observableArray([
        new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris),
        new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris),
        new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris),
        new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris),
        new monthData(2013, 1, 412, 142, this.tak, this.styckpris, this.grundpris)       
    ]);    

}

Upvotes: 3

Related Questions