ko.computed on an ko.observableArray - javascript

I'm trying to use a computed to calculate the total of some product.
function productViewModel(){
self = this;
function productModel(data)
{
var self=this;
self.id=ko.observable(data.id);
self.codigo=ko.observable(data.codigo);
self.recurso=ko.observable(data.recurso);
self.unidad=ko.observable(data.unidad);
self.precio_unitario=ko.observable(0);
self.cantidad=ko.observable(0);
self.total=ko.computed(function()
{
return self.precio_unitario()*self.cantidad();
},productModel);
}
self.products = ko.observableArray([]);
self.addProduct = function(product)
{
self.products.push(new productModel(product));
};
self.removeProduct = function()
{
self.products.remove(this);
};
}
orden = new productViewModel()
ko.applyBindings(orden);
But when precio_unitario and cantidad are changed. total doesn't update.

function productModel(data)
{
var self=this;
...
self.total=ko.computed(function()
{
return self.precio_unitario()*self.cantidad();
},this);
}
You should be binding the ko.computed to this not to the function. You want it to be bound to the object thats created, not to the constructor, which won't have those properties on it. Since you're using self, this will actually be taken care of by default, and if you like you can omit the second argument entirely.
Within the constructor function, this or self will refer to the object that is created when you use the new operator. So all the properties will be created on that object.

self = this; should be var self = this;; otherwise you're overwriting the global self. Also take out ,productModel on the computed; it's not necessary.
Important parts:
function productViewModel() {
var self = this;
function productModel(data) {
var self = this;
...
self.total = ko.computed(function() {
return self.precio_unitario()*self.cantidad();
});
}
...
}
Also it's important make sure you're always using the correct format for writing to observables. It should be self.catidad(newValue); and not self.catidad = newValue;

Related

private object not setting data

Hi I'm trying to implement a LinkedList in Javascript. When i assign a value to my node it doesn't seem to store it when I use my getter. For example:
var Node =function() {
var _data;
var _next ={};
var that = this;
that.getData = function() {
return _data;
};
that.setData = function(data) {
that._data = data;
};
that.getNext = function() {
return _next;
};
that.setNext = function(next) {
that._next = next;
};
return that;
};
Will not work with:
var nodeObj = new Node();
nodeObj.setData("hello");
console.log(nodeObj.getData());
_data is not the same as that._data, you must do this:
that.getData = function() {
return that._data;
};
OR you could do this instead:
that.setData = function(data) {
_data = data;
};
the benefit of the second approach being that you're simulating a private variable (because you cannot do nodeObj._data in the second case but you can in the first)
also var that = this; is unnecessary, you can simply do this._data in this case.
For your case here, you can assume that if you're calling a function like yourObject.someFunction(), then within someFunction the value of this equals yourObject. (And this isn't always true in javascript but since you're starting off you should think about it this way for now. If you pass a function to another function as a variable and then call it then this wouldn't be the case).

How come this and _this reference different instances of an object?

I have a class that can be simplified like this:
Captcha = function(el) {
this.el = $(el);
_this = this;
captchas.push(this);
};
Captcha.prototype.render = function(grecaptcha){
console.log(this.el.dom[0]);
console.log(_this.el.dom[0])
};
The class is intantiated twice with two different DOM-elements passed in as el.
Render is run when a global callbackfunction is run.
captchas = [];
//We need this for captchas.
window.CaptchaCallback = function(){
app.captchas.forEach(function(capt){
capt.grecaptcha = grecaptcha;
capt.render();
});
};
For some reason, this.el.dom[0] references the two different elements, but _this.el.dom[0] always references the last instance of the class, why?
You left off var when you initialized _this:
var Captcha = function(el) {
this.el = $(el);
var _this = this; // don't forget var!
captchas.push(this);
};
Your code was therefore creating a global variable, not a local one.
Of course, it's local to that constructor function, so it won't be visible outside anyway. You could make _this a property of the constructed object:
this._this = this;
but that doesn't make a lot of sense, since you'd need this to find _this anyway.
Because you didn't declare _this with the var keyword, a global variable is implicitly declared. Your constructor code then equivalent to:
var _this;
Captcha = function(el) {
this.el = $(el);
_this = this;
captchas.push(this);
};
Because it's global, _this always holds the value of the last instance created.

JavaScript Call or Apply using Knockout ViewModelBase

I have this base view model:
var baseViewModel = function () {
var self = this;
// <!----- AJAX SAVING ------!> \\
self.saving = ko.observable();
// <!----- SEARCHING ------!> \\
self.fields = ko.observableArray();
self.selectedField = ko.observable();
self.searchTerm = ko.observable().extend({ throttle: 150 });
}
And I inherit it using this:
var viewModel = function () {
baseViewModel.call(this);
var self = this;
//stufff
}
viewModel.prototype = new baseViewModel();
And it works perfectly. Quite pleased with it.
Now, I want to setup the self.fields property with some initial data, that I want to send through the line baseViewModel.call(this) and I'm not sure whether to do this:
var viewModel = function () {
baseViewModel.call(this, new userModel()); // just a function object
var self = this;
}
OR:
var viewModel = function () {
baseViewModel.apply(this, new userModel()); // just a function object
var self = this;
}
So that the baseViewModel will do this:
var baseViewModel = function (data) {
var self = this;
// <!----- AJAX SAVING ------!> \\
self.saving = ko.observable();
// <!----- SEARCHING ------!> \\
self.fields = ko.observableArray().getKeys(data); // viewModel parameter passed here
self.selectedField = ko.observable();
self.searchTerm = ko.observable().extend({ throttle: 150 });
}
I have read this Difference between call and apply still not sure where to go and I have read the official documentation.
EDIT
I have just tried call because as I understand it the only difference is either putting in a bunch or args (with call) or putting in an array of args (with apply)
Its worked with call so far, just wondering if there are going to be any caveats with choosing this method?
Unless there are any caveats, the only difference is whether the args come as and array or separate objects
Call Link
Apply Link
with call you do baseViewModel.call(this [, arg1, arg2, .... argn])
with apply you do baseViewModel.apply(this [, arg_array[] ])

Knockout multiple viewmodels with same name variables conflict?

I bound multiple ko viewmodels to different panels in the same page, but when the viewmodels have properties with the same name they seem to lose their binding to their own viewModel like:
var Panel1ViewModel = function Panel1ViewModel() {
var self = this;
self.isVisible = ko.observable(false);
self.change1 = function() {
self.isVisible(!self.isVisible());
};
};
ko.applyBindings(Panel1ViewModel(), document.getElementById('panel1'));
var Panel2ViewModel = function Panel1ViewModel() {
var self = this;
self.isVisible = ko.observable(false);
self.change2 = function() {
self.isVisible(!self.isVisible());
};
};
ko.applyBindings(Panel2ViewModel(), document.getElementById('panel2'));
To make it more clear I recreated the problem in jsfiddle.
I know I can nest ViewModels with with but the page is big and some content is loaded dynamically so I want to separate it.
Can someone explain me why this is happening and wat a possible solution is?
You're not initiating your view models correctly. Try it like this:
var Panel1ViewModel = function Panel1ViewModel() {
var self = this;
self.isVisible = ko.observable(false);
self.change1 = function() {
self.isVisible(!self.isVisible());
};
};
ko.applyBindings(new Panel1ViewModel(), document.getElementById('panel1'));
var Panel2ViewModel = function Panel1ViewModel() {
var self = this;
self.isVisible = ko.observable(false);
self.change2 = function() {
self.isVisible(!self.isVisible());
};
};
ko.applyBindings(new Panel2ViewModel(), document.getElementById('panel2'));
http://jsfiddle.net/XWD96/3/
The difference is that the new operator will create a new object (this inside your view model). So by not having the new, this will point to the window in both view models, therefor causing conflicts.
You can read more about Constructor Functions (new) here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Using_a_constructor_function)

Difference between knockout View Models declared as object literals vs functions

In knockout js I see View Models declared as either:
var viewModel = {
firstname: ko.observable("Bob")
};
ko.applyBindings(viewModel );
or:
var viewModel = function() {
this.firstname= ko.observable("Bob");
};
ko.applyBindings(new viewModel ());
What's the difference between the two, if any?
I did find this discussion on the knockoutjs google group but it didn't really give me a satisfactory answer.
I can see a reason if I wanted to initialise the model with some data, for example:
var viewModel = function(person) {
this.firstname= ko.observable(person.firstname);
};
var person = ... ;
ko.applyBindings(new viewModel(person));
But if I'm not doing that does it matter which style I choose?
There are a couple of advantages to using a function to define your view model.
The main advantage is that you have immediate access to a value of this that equals the instance being created. This means that you can do:
var ViewModel = function(first, last) {
this.first = ko.observable(first);
this.last = ko.observable(last);
this.full = ko.computed(function() {
return this.first() + " " + this.last();
}, this);
};
So, your computed observable can be bound to the appropriate value of this, even if called from a different scope.
With an object literal, you would have to do:
var viewModel = {
first: ko.observable("Bob"),
last: ko.observable("Smith"),
};
viewModel.full = ko.computed(function() {
return this.first() + " " + this.last();
}, viewModel);
In that case, you could use viewModel directly in the computed observable, but it does get evaluated immediate (by default) so you could not define it within the object literal, as viewModel is not defined until after the object literal closed. Many people don't like that the creation of your view model is not encapsulated into one call.
Another pattern that you can use to ensure that this is always appropriate is to set a variable in the function equal to the appropriate value of this and use it instead. This would be like:
var ViewModel = function() {
var self = this;
this.items = ko.observableArray();
this.removeItem = function(item) {
self.items.remove(item);
}
};
Now, if you are in the scope of an individual item and call $root.removeItem, the value of this will actually be the data being bound at that level (which would be the item). By using self in this case, you can ensure that it is being removed from the overall view model.
Another option is using bind, which is supported by modern browsers and added by KO, if it is not supported. In that case, it would look like:
var ViewModel = function() {
this.items = ko.observableArray();
this.removeItem = function(item) {
this.items.remove(item);
}.bind(this);
};
There is much more that could be said on this topic and many patterns that you could explore (like module pattern and revealing module pattern), but basically using a function gives you more flexibility and control over how the object gets created and the ability to reference variables that are private to the instance.
I use a different method, though similar:
var viewModel = (function () {
var obj = {};
obj.myVariable = ko.observable();
obj.myComputed = ko.computed(function () { return "hello" + obj.myVariable() });
ko.applyBindings(obj);
return obj;
})();
Couple of reasons:
Not using this, which can confusion when used within ko.computeds etc
My viewModel is a singleton, I don't need to create multiple instances (i.e. new viewModel())

Categories

Resources