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[] ])
Related
I'm using the ko.subscribable() functionality, to pass an object from knockout in mypage.js used in mypage.aspx (which inherits from site.master) to knockout in sitemaster.js (site.master.aspx) which is loaded and used in every page in my solution.
The problem is.. when following the recommended structure of using this functionality (see this JSFiddle) it states that i have to have one "MasterViewModel" which creates both of the viewmodels, which i find problematic in the scenario where you have two seperate javascript files, either though both are loaded in the destinationpage (mypage.aspx).
This is the necessary code i cannot figure out how to handle :
var masterVM = (function(){
this.viewModel1 = new viewModel1(),
this.viewModel2 = new viewModel2();
})();
ko.applyBindings(masterVM)
Here is my code :
mypage.js
var shouter = new ko.subscribable();
var PhotoModel = function() {
var self = this;
self.photos = ko.observableArray();
self.selectedPhoto = ko.observable();
self.setSelectedPhoto = function(selPhoto) {
self.selectedPhoto.subscribe(function (selPhoto) {
shouter.notifySubscribers(selPhoto, "photoToShow");
});
}
}
var photosModel = new PhotoModel();
ko.applyBindings(photosModel, document.getElementById("latest-photos"));
sitemaster.js
var PhotoViewModel = function(photosModel) {
var self = this;
self.viewPhoto = ko.observable();
shouter.subscribe(function(selPhoto) {
self.viewPhoto(selPhoto);
}, self, "photoToShow");
};
var photoViewModel = new PhotoViewModel();
ko.applyBindings(photoViewModel, document.getElementById("photo-viewer"));
I have a nested viewmodel setup that need to be expressed as functions due to having to have instances (unless I am missing something). Everything works in the UI, I can nest viewmodels that have nested viewmodels and so on.
However in its current form I get an error
JavaScript runtime error: Pass a function that returns the value of the ko.computed
when trying to call ko.toJSON(x);.
This worked when I had the vms defined without the need for instances however my nesting did not hence the change.
Here is an example of how it is currently.
var CityViewModel = function() {
var self = this;
self.Name = ko.observable("");
self.ATMs = ko.observableArray();
self.AddATM = function () {
self.ATMs.push(new ATMViewModel);
}
self.GetJson = function() {
alert(ko.toJSON(self)); //Area of interest
}
}
var ATMViewModel = function() {
var self = this;
self.PostCode = ko.observable("");
self.Features = ko.observableArray();
self.AddFeature = function () {
self.Features.push(new FeaturesViewModel());
}
}
var FeaturesViewModel = function () {
var self = this;
self.Name = ko.observable("");
self.Reference = ko.observable("");
}
ko.applyBindings(CityViewModel);
Took me a while to spot it as well.. a missing new keyword
ko.applyBindings(new CityViewModel());
// ====
As a Fiddle: http://jsfiddle.net/Quango/zf0dLLyr/
I'd recommend Ryan's suggestion for debugging:
<pre data-bind="text: ko.toJSON($data, null, 2)"></pre>
It was the fact that returned nothing gave me the hint. See
http://www.knockmeout.net/2013/06/knockout-debugging-strategies-plugin.html
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)
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;
I have a class like structure in javascript, Im attempting to invoke a sibling function using a passed in function name.
This is difficulty to explain so let me show you an example of what im trying to accomplish..
function windowFactory(){
this.init = function(functionName,args[]){
SetTimeout(functionName(args),2000)
}
this.func1 = function(var1){
alert(var1);
}
this.func2 = function(var1, var2){
alert(var1+var2);
}
}
var win1 = new windowFactory();
win1.init("func1","hello");
var win2 = new windowFactory();
win2.init("func2","world","!");
Please note that this is only a demo function, syntax errors / typos included.
Now i had this working using a dreaded Eval when it was outside the class...
eval(funcName+"('"+darray[1]+"','"+darray[2]+"')");
It just required it being outside the Class and passed in dummy values for parameters
Something like this should do the trick:
var windowFactory = function() {
var self = this;
this.init = function(functionName){
var args = Array.prototype.slice.call(arguments, 1);
setTimeout(function() {
self[functionName].apply(self, args);
}, 2000);
};
this.func1 = function(var1){
alert(var1);
};
this.func2 = function(var1, var2){
alert(var1+var2);
};
};
var win1 = new windowFactory();
win1.init("func1","hello");
var win2 = new windowFactory();
win2.init("func2","world","!");
Note the custom self reference var self = this;. This is used because when the timed out function is called, the this object will be window (at least in a web browser).
Another clarification: To address a specific object property in JavaScript you can do in the following ways:
object.property; // Or
object['property']; // When you have a string literal, like in your example