Knockout js gathering data from other view models - javascript

I have an app that uses several different view models (this is the first time I have built an app with knockout js).
Basically what I'm doing is a wizard and each page is a knockout view model, at the end I'd like to take all the json from all the view models and submit it with a final button.
What would be the best way of doing this?

window.firstViewModel = new function()
{
var self = this;
self.firstProperty = ko.observable();
//
}
window.secondViewModel = new function()
{
var self = this;
self.secondProperty = ko.observable();
//
}
var submit = function()
{
var firstProperty = firstViewModel.firstProperty(); // access to firstViewModel
var secondProperty = secondViewModel.secondProperty(); // access to secondViewModel
//...
}

I would recommend going towards a Single Page Application.
Essentially, you would have a single view model with sections for each step in the wizard, and on submit, you have all the data you need.
The visibility/aesthetics can be controlled via css and intelligent binding.
RP Niemeyer has a good demo, and also talks about it in this answer.

Related

dynamic observable classes for knockout

I am working on a reporting related project, where I need to build lot of reports rendered using KO. All data pulled using AJAX and the model is updated. Currently I am writing tons of js functions to map the models. Something like:
function modelx(child) {
var self = this;
self.Name = ko.observable(child.Name);
self.Relation = ko.observable(child.Relation);
// hundred other properties
};
function modely(child) {
var self = this;
self.Age = ko.observable(child.Age);
self.Relation = ko.observable(child.Relation);
// hundred other properties
};
and after AJAX call, I am filling the observable arrays
for (var i = 0; i < jsn.length; i++)
{
VM.modelxlist().push(new modelx(jsn[i]));
}
for (var i = 0; i < jsn1.length; i++)
{
VM.modelylist().push(new modely(jsn1[i]));
}
Is there any way to avoid the definition of modelx, modely,... such that the model is automatically built without loosing the benefits of this approach while using in HTML? Of course there could be a corner case where I may not get a specific property from server, which I should check on the server side.
Also, at times I may need to add additional computed observables (just to be more flexible)
Why don't you use knockout mapping plugin:
http://knockoutjs.com/documentation/plugins-mapping.html
You would then have something like:
var modelxInstance= ko.mapping.fromJS(child);
There are a few mapping plugins for knockout, the one i like the most is actually this one:
https://github.com/LucasLorentz/knockout.mapper
And the reason is that it is more configurable and it is faster.
I think this is what you want..
With ko.mapping.fromJS method u can automatically observe all the properties from your object..
Take some time to read about that..

Can not update observable of one viewModel from another

I want to update one observable of one viewModel from another. The value gets changed but it does not reflect on UI (in HTML)
I am not sure what's wrong with the code..
Here is the code :
http://jsfiddle.net/rahulrulez/RSL4u/2/
<p data-bind="text: name"></p>
does not gets updated.
You are binding to two independent view-model instances
ko.applyBindings(new viewModel1(), document.getElementById("firstViewModel"));
ko.applyBindings(new viewModel2(), document.getElementById("secondViewModel"));
So there is no connection between your viewModel1 and viewModel2 and when you write in your viewModel1:
var vm2Object = new viewModel2();
then you are creating a completly new viewModel2 instance which has nothing to do with the one used in the applyBindings.
To fix this you need to make a connection between your view models, somehow like this (there are multiple other ways to do like using a container view model, nest your view models to each other, etc.):
var viewModel1 = function(vm2){
var self = this;
var vm2Object = vm2; //use vm from parameter
//...
}
And when calling the applyBindings:
var vm2 = new viewModel2();
ko.applyBindings(new viewModel1(vm2), document.getElementById("firstViewModel"));
ko.applyBindings(vm2, document.getElementById("secondViewModel"));
Demo JSFiddle.
You have two different instances of the VM2 ViewModel. Instead do
http://jsfiddle.net/RSL4u/4/
I have made VM2 a sub model of VM1
this.vm2Object = new viewModel2();
I tried all your approaches, for some reason they were not really working with the solution I have.
After a lot of googling and digging deep in KnockoutJS documentations and extensions, I found Knockout Postbox which lets us sync or communicate between multiple irrelevant View Models.
https://github.com/rniemeyer/knockout-postbox
Here is snippet of my demo example...
var viewModel1 = function(){
var _self = this;
_self.name = ko.observable("Rahul").syncWith("syncedName");
}
var viewModel2 = function(){
var _self = this;
_self.firstName = ko.observable("Rahul").syncWith("syncedName");
}
ko.applyBindings(new viewModel1(), document.getElementById("div1"));
ko.applyBindings(new viewModel1(), document.getElementById("div2"));
Both the observable are in sync now.. I found this much better than nesting objects.. At least it satisfied the need of my application..
Thank you so much for help anyway..
My Demo : JSFiddle : http://jsfiddle.net/rahulrulez/2kuSh/1/
I hope it helped..
Thank you so much,
Rahul Patil.

Deleting a table row in object oriented programming

I started learning OOP in JavaScript at school and I have to do my homework in OOP. It is a shopping list, one fills in a product and price and that is put into an array, the array its contents are put in a table in HTML. The list also has got delete buttons for every product, I have thought about using this in JavaScript, as I am working in OOP, but I am sceptic because of the scope.
The idea is that one clicks "verwijder" on the webpage (EDIT: in the corresponding row) and the table row is gone. I have thought about using this but also about dynamically updating the table after removing the selected row from the array.
Who can advise me about what to do and how to do that correctly?
I have uploaded my JavaScript here: http://jsfiddle.net/hNAZ9/
If you want to study oop in javascript (and you are allowed to use frameworks), my suggestion is to learn a framework like http://knockoutjs.com/ . The separation between view/html interaction code and js object modeling code will make you life much easier. Here is an example reimplementation of your fiddle, using knockout: http://jsfiddle.net/kaGzz/3/
I think it's a lot clearer what the objects are doing:
var Item = function(product, price, parentArray){
var self = this;
self.product = ko.observable(product);
self.price = ko.observable(price);
self.deleteItem = function(){
parentArray.remove(self);
}
}
var ShoppingList = function(){
var self = this;
self.currentProduct = ko.observable();
self.currentPrice = ko.observable();
self.items = ko.observableArray();
self.addItem = function(){
self.items.push(new Item(self.currentProduct(), self.currentPrice(), self.items));
}
}
var shoppingList = new ShoppingList();

Knockout observable/viewmodel that can be accessed by other viewmodels?

I am building an SPA and everything is going well. It has multiple Viewmodels which are built dynamically and there can be multiple of the same kind, i.e you can open two calculators each having its own model which is bound to a specific div on the page.
Recently I realized that several of the viewmodels were requesting the same data from a web service and on a constant loop every 30 secs - 1 minute. So the same service call was being made multiple times every 30 seconds yet returning the same information.
So what I am trying to figure out is how I can create a "global" observableArray which multiple viewModels can be notified of a change and update rather than doing it themselves, this also helps to make sure the data on the page is consistent.
I was hoping I could do something like:
var GlobalData = (function() {
var commonData = ko.observableArray();
setInterval(function() {...go get data...commonData(data);}, 30000);
return {CommonData:commonData}
})();
ko.applyBindings(GlobalData, $('#RandomLonelyDiv')[0]);
Then later
function Calculator(element){
function init() { ko.applyBindings(calculator, $(element)[0]); }
var calculator = {
CommonData = GlobalData.CommonData
}
return calculator;
}
If it helps the only reason why I dont have a MainViewModel which contains all my other viewmodels is because i frankly dont know how to set that up for my environment.
I have a AppViewModel which contains a ko.observableArray called Windows, which is contains objects which define the options/information to build certain window types.
<!-- ko template:{name:'WindowTemplate', foreach:SelectedTab().Windows} --><!-- /ko -->
and then I have a custom Window binding that creates a modified kendoWindow, which creates a new viewmodel of a specific type such as Calculator, and like I said you could have multiple calculators at one time. But when I started this I wasnt really sure how to put that viewmodel into my AppViewModel. Perhaps its just another array?
It sounds like what you really need is a "Pub/Sub" model. That would allow you to publish and subscribe to messages that are ignorant of their generation or destination. Check out https://github.com/postaljs/postal.js/wiki.
I believe this may be what you are looking for: http://jsfiddle.net/xSKyR/474/
You can subscribe to another viewmodel's observable like so..
var ViewModel1 = function () {
var self = this;
self.something1 = ko.observable("1");
self.clickMe = function (data, event) {
self.something1("2");
};
};
var ViewModel2 = function () {
var self = this;
self.something2 = ko.observable();
vm1.something1.subscribe(function (newValue) {
self.something2(newValue);
});
};
var vm1 = new ViewModel1();
var vm2 = new ViewModel2();
ko.applyBindings(vm1, document.getElementById("vm1"));
ko.applyBindings(vm2, document.getElementById("vm2"));

Confused about complex class structure and Knockout

Been getting into Knockout and and slowly getting used to it. Trying to use it in a new project, but am having a hard time getting things lined up to work. While I understand and can do simple examples (simple form with text boxes bound to ko.observables, or a table or list bound to a ko.observableArray), I can't get the syntax right for a combination, especially if I want to convert the data to JSON format in order to transmit it, via a webservice, to be saved into a database.
Basically it's a data entry form, with some text entry boxes, then a list of items (think company information + a list of it's employees).
I have a sample Fiddle here: http://jsfiddle.net/rhzu6/
In the saveData function, I just don't know what to do to get the data packaged. Doing ko.toJS(self) just shows "Object".
I tried defining the data as objects, but quickly got lost:
function Company(CompanyName, ZipCode) {
var self = this;
self.ZipCode = ko.observable(ZipCode);
self.CompanyName = ko.observable(CompanyName );
self.Employees = ko.observableArray();
}
function Employee(FirstName, LastNameB) {
var self = this;
self.FirstName = ko.observable(FirstName);
self.LastName = ko.observable(LastName);
}
Then the ViewModel looked like:
function viewModel() {
var self = this;
self.Company = ko.observable(); // company?
self.Employees = ko.observableArray(); // ?
}
But ran into the same issue. And also had binding problems - data-bind:"value: CompanyName" threw an exception saying it didn't know what CompanyName was...
Color me stumped. I'm sure it's something easy that I'm just missing.
Any and all help would be appreciated!
Thanks
You are looking for ko.toJSON which will first call ko.toJS on your ViewModel and afterwards JSON.stringify.
ko.toJS will convert your knockout model to a simple JavaScript object, hence replacing all observables etc. with their respective values.
I updated your Fiddle to demonstrate.
For more info, take a look at this post from Ryan Niemeyers blog.
An alternative is to make use of ko.utils.postJson:
ko.utils.postJson(location.href, {model: ko.toJS(viewModel) });
Notice the ko.toJS again.
It looks to me as if you (semantically) want to submit a form. Therefore, I think that you should use the submit binding. The biggest benefit is that you listen to the submit event, which allows submit by other means, such as Ctrl+Enter or any other keyboard combination you want.
Here is an example on how that submitEvent handler could look like. Note that it uses ko.mapper, which is a great way to create a viewModel from any JS/JSON-object you want. Typically, you would have
[backend model] -> serialization -> [JS/JSON-ojbect] -> ko.mapper.fromJSON(obj) -> knockout wired viewModel.
viewModel.submitEvent = function () {
if (viewModel.isValid()) { //if you are using knockout validate
$.ajax(
{
url: '/MyBackend/Add',
contentType: 'application/json',
type: 'POST',
data: ko.mapping.toJSON(viewModel.entityToValidateOnBackend),
success: function (result) {
ko.mapping.fromJSON(result, viewModel);
}
}
);
}
};
Good luck!

Categories