knockout.js: No concat on observableArray - javascript

Only starting with knockout.js, but already running into some trouble when trying to make a computed method based on 2 different observableArrays
Using the documentation on knockout.js' website, I've created the following viewmodel:
var Cart = function() {
var self = this;
self.Products = ko.observableArray([]);
self.Products2 = ko.observableArray([]);
self.Messages = ko.observableArray([]);
self.TotalAmount = ko.computed(function() {
var result = 0;
ko.utils.arrayForEach(
this.Products().concat(this.Products2()),
function(item) {
result+=item.AmountIncludingVAT();
}
);
return result;
});
};
Doing this throws an error of "Uncaught TypeError: Object #<error> has no method 'concat'.
I know there is this function called arrayPushAll, but it's a destructive function which would alter the original observableArray. (I don't think this is something I want).
Is there any clean way to achieve what I'm trying to do? Or do I have to make 2 different calls to arrayForEach, one for each array?

Change:
this.Products().concat(this.Products2()),
to:
self.Products().concat(self.Products2()),
Inside your TotalAmount ko.computed function.
this in the context of your computed refers to the global object rather than the view model. So you need to use the self variable that is assigned the correct this value earlier.
Working Example - http://jsfiddle.net/55kZp/

concat didn't works for me .. I did push
self.Products.push.apply(
self.Products, self.Products2()
);

Related

Merging history state object with constructor using knockout merge

i'm having issues with merging my history state object with a constructor that i have, that gets saved in the same history state for later use.
Plugin example page: https://rawgit.com/grofit/knockout.merge/master/example.html
Using the example that is shown in knockout merge plugin page that uses a constructor like my own i've built my code but unfortunately since i'm relatively new to knockout i ran into issues.
This is the a piece of code shown inside knockout merge's example
function Person()
{
this.Firstname = ko.observable();
this.Surname = ko.observable();
}
function ViewModel()
{
this.SimpleExampleModel = new Person();
this.MergeSimpleExample = function() {
var personJson = { Firstname: "James", Surname: "Bond" };
ko.merge.fromJS(this.SimpleExampleModel, personJson);
};
};
ko.applyBindings(new ViewModel());
Now my code:
(The object that is pushed to the history is the constuctor's observables as an object)
Constructor:
var searchTerm = function () {
this.MinPrice = ko.observable();
};
lbx.vm = {
term: new searchTerm(),
injectHistory: function () {
// ko.merge.fromJS(this.term, history.state); Doesn't work
// var json = ko.toJSON(history.state) - Doesn't work
//var json = JSON.Parse(history.state) - Doesn't work
//var json = { MinPrice: 222 }; Works
var json = { "MinPrice": 222 }; // Works
ko.merge.fromJS(this.term, json);
console.log("injected");
}
};
As you can see according to my testing, whenever i try to turn my JS object into json it doesn't work, but it does if i build the json manually.
Fiddle with my problem: https://jsfiddle.net/Badzpeed/05zdLgxh/1/
As you will see in the fiddle when i popstate, nothing happens, the observable value is always the same and it doesn't throw any error.
Any help would be appreciated !
Thank you in Advance
Finally fixed my issue, turns out that i forgot to add the constructor to the merge and i also had a double call to the function that was passing the object to my history state, making two of them.
Fixed the issue by changing those two things.
Thank you for your time !

How can an object literal's property be assigned to the return value of one of its methods?

So I am making a game to practice using the mvc paradigm. I create an object literal for my model and want the functions it needs to generate values for its properties to be in the object itself. I have tried this with no success. I have tried using "this" when calling the function and not using it. Either way, I get a function not defined error from chrome. What can I do to fix this? Here's the relevant code:
var model = {
genPlayers: function() {
return tempPlayerArray;
},
playerArray: genPlayers()
}
You can't do that. 1. there is no function with the name genPlayers() only model.genPlayers() and 2. object properties can't be accessed during object initialization.
What you could do is:
var model = {
genPlayers: function() {
return tempPlayerArray;
},
playerArray: null
};
model.playerArray = model.genPlayers();
Or if model.genPlayers always only returns the tempPlayerArray you could do this:
var model = {
genPlayers: function() {
return tempPlayerArray;
},
playerArray: tempPlayerArray
};

KnockoutJS Observable

I am working with Knockout for the first time and I am struggling with an observable. I have declared it in one view model and I need to gain access to the value in another. Any tips on how to go about that?
What you have to do, it's to inherit the parentViewModel
function parentViewModel() {
// Just a best practice potato here :P
var self = this;
// Initialize the childViewModel change it's 'this'
childViewModel.call(self);
self.observable = ko.observable();
}
function childViewModel() {
var self = this;
console.log(self); // It will output the parentViewModel scope
}
First declare your 1st model in a shared scope, then the 2nd. If you do something like myfirstmodel.myobservable() within your second model, you should see it and interact with it.
var myModel = whatever();
var mySndModel = whateverElse();
ko.applyBindings(myModel, document.getElementById('whatever'))
ko.applyBindings(mySndModel, document.getElementById('whateverElse'))
whateverElse is the constructor of your second model, in which you can call myModel.myObservable()

knockout.js accessing container model property in a contained viewModel

I have nested view models like below. I am trying to access value in container view model from the contained view model (child). I got undefined error when the modelA.prop1 trying to get mainVM.prop1 value. Thanks for your help.
function mainVM() {
var self = this;
//chain associated view models
self.modelA = new modelA();
self.modelB = new modelB();
self.prop1 = ko.observable("some value from mainVM.prop1");
}
function modelA(){
var self = this;
self.prop1 = ko.observable(mainVM.prop1); //I'd like to get value in containing view model above
}
function modelB(){....}
$(function () {
var viewModel = new mainVM();
ko.applyBindings(viewModel);
});
If you want to make sub-ViewModels dependent/aware of their parent you'll have to pass it to them. E.g.:
function mainVM() {
var self = this;
//chain associated view models
self.modelA = new modelA(self);
self.modelB = new modelB(self);
self.prop1 = ko.observable("some value from mainVM.prop1");
}
function modelA(parent){
var self = this;
self.prop1 = ko.observable(parent.prop1); //I'd like to get value in containing view model above
}
function modelB(parent){....}
$(function () {
var viewModel = new mainVM();
ko.applyBindings(viewModel);
});
Think carefully though if this dependency is something you want in your design.
An alternative (though arguably worse from a design standpoint) solution would be to give them access through the scope, e.g.:
$(function () {
function mainVM() {
var self = this;
//chain associated view models
self.modelA = new modelA();
self.modelB = new modelB();
self.prop1 = ko.observable("some value from mainVM.prop1");
}
function modelA(){
var self = this;
self.prop1 = ko.observable(viewModel.prop1); //I'd like to get value in containing view model above
}
function modelB(){....}
var viewModel = new mainVM();
ko.applyBindings(viewModel);
});
Some additional thoughts to #Jeroen answer
Having dependencies to parent from children is not only bad design it can create hard to find memory leaks
If you use the parent from a computed in the child KO will hook up a dependency, if you remove the child it's computed will still fire when the parent change state.
My general way of solving dependencies between models is to use a EventAggregator pattern, I have made one for this library
https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy
Its a signalR library, if you do not need singalR you can extract the event aggregation part
Demo
http://jsfiddle.net/jh8JV/
ViewModel = function() {
this.events = ko.observableArray();
this.subModel = new SubViewModel();
signalR.eventAggregator.subscribe(Event, this.onEvent, this);
};
ViewModel.prototype = {
onEvent: function(e) {
this.events.push(e);
}
};
I think you've got an "XY problem" here: you want to accomplish task X (which you haven't named here) and you think that implementation Y (in this case, a child VM having a dependency on its parent) is the way to do it, even though Y might not be the best (or even a good) way to do it.
What's the actual problem you're trying to solve? If you need to access the parent property from within a child binding, Knockout's binding context ($root, $parent, $parents[], etc.) will let you do it, e.g.
<div data-bind="with:modelA">
<p>prop2 is <span data-bind="text:prop2"></span>
and prop1 from the main model is
<span data-bind="text:$root.prop1"></span>
</p>
</div>
In this case you could use $parent in place of $root since there's only one level of nesting.

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