I have a list of reocrds fetching from the server, and converted to knockout observableArray by ko.mapping.fromJS.
self.items = ko.observableArray([]);
$.getJSON('/api/getdata', function(data) {
var mappedTasks = $.map(dataFromServer, function(cls) {
return ko.mapping.fromJS(cls);
});
self.items(mappedTasks);
}
In certain conditions I want some properties to be set as null and bind to the UI. Later upon users action I want to revert the old existing data instead of null. Is there way to attach a property oldValue to observable, so that I can preserve the old value and set it back when necessary. Can knockout extend be used for holding the data? Any help on this greatly appreciated,
George
When you're mapping the data from the server to an object with knockout observables, you can also define additional observables on the same object.
self.items = ko.observableArray([]);
$.getJSON('/api/getdata', function(data) {
var mappedTasks = $.map(dataFromServer, function(cls) {
var item = ko.mapping.fromJS(cls);
item.AdditionalValue1 = ko.observable(0);
item.AdditionalValue2 = ko.observable('test');
return item;
});
self.items(mappedTasks);
}
If you would like to change the way the object that you're binding to is structured, it may be easier to build mapping from the server side data to your client side view model.
Related
I have a model defined as:
var TestModel = function () {
var self = this;
self.Item = ko.mapping.fromJS(new Item());
self.ItemList = ko.observableArray();
};
var Model = new TestModel();
ko.applyBindings(Model);
After an AJAX call I fill the array with:
ko.mapping.fromJS(data, {}, Model.ItemList);
The array is displayed in the screen and when the user selects an item, a modal dialog is opened to edit that item. The same modal dialog can be opened to add a new item. That modal has a data-bind="with: Item", so I can do something like this:
function add() {
ko.mapping.fromJS(new Item(), {}, Model.Commission); //Empty object
$('#dialog').dialog('open');
};
function saveAdd() {
//Clone the properties...
Model.ItemList.push(ko.mapping.fromJS(ko.mapping.toJS(Model.Item)));
}
function edit(i) {
//Clone properties!
ko.mapping.fromJS(ko.mapping.fromJS(ko.mapping.toJS(Model.ItemList()[i])), {}, Model.Item);
$('#dialog').dialog('open');
}
function saveEdit(i) {
//Clone properties!!!!
Model.ItemList()[i] = ko.mapping.fromJS(ko.mapping.toJS(Model.Item));
}
I want to avoid cloning the properties every time I need to assign the values of one observable to another one. I understand that if I manage to tell Knockout that the Model.Item is some Model.ItemList()[i], the objects references should make sure the changes in the UI reflect directly to my observableArray item, but I cannot just assing it as Model.Item = Model.ItemList()[i]; because that breaks the binding between the original Model.Item object and the view [*1].
Is there a way to assign an observable to another observable that's binded to the view, and maintain the references?
Failing to do that, is the a better way to do this? Basicaly a page where you store the list of items in an observableArray and then add/edit in another piece of HTML.
Edit: Explanation for [*1]
I cannot find the stackoverflow question where this was explained, but it was something like this:
When we do ko.applyBindings(Model);, the observable objects inside Model get binded to the view. At this point the object referenced by Model.Item is binded, so if I do Model.Item = Model.ItemList()[i]; the new object being referenced by Model.Item is not binded to anything. The original object (in memory) is still the one binded to the view, just that there are no references to it now.
If you make Model.Item an observable, with the mapped object inside of it, then you can set the observable's contents to the new item. The reference to the observable made by knockout remains intact because you're just updating the contents and not the property itself.
var TestModel = function () {
var self = this;
self.Item = ko.observable(ko.mapping.fromJS(new Item()));
self.ItemList = ko.observableArray();
};
...
Model.Item(Model.ItemList()[i]);
is it possible to fire a function when observable array is modified? my goal here is to notify me if my observable array is modified to do some logics on my current application
this is my view model it has a observable array inside it
WT.BM.BarsViewModel = function () {
var self = this;
self.BarsDataHolder = ko.observableArray([]);
};
i just want to fire a function to notify me if self.BarsDataHolder has been modified
any ideas?
You can subscribe to any ko.observable or ko.observable array.
WT.BM.BarsViewModel.BarsDataHolder.subscribe(function(newArray) {
console.dir(newArray);
});
you can find this in the knockout documentation on the observables page
I've tried to describe my problem in the below illustration.
When the page loads, a javascript object is parsed and becomes my backbone model called obj model. This obj model is passed along to many different independent and modular submodules that make use of the data in different ways.
Everything works great except for when I'm dealing with collections. To give the user the ability to manage "Photos" and "Comments" I need to create a separate collection/model data structure for them.
How should I sync back the changes to my "obj model"?
class Obj extends Backbone.DeepModel
class Comment extends Backbone.DeepModel
class CommentCollection extends Backbone.Collection
model: Comment
class Photo extends Backbone.DeepModel
class PhotoCollection extends Backbone.Collection
model: Photo
You're obj model should be externalized.
App.module("Entities",function(Entities,App,Backbone,Marionette,$,_){
"use strict";
//these are locally scoped, so they aren't accessable by the your app
var Obj = Backbone.Model.extend({
urlRoot:'api/obj',
});
var Data = {};
var API = {
//wrap request in deferred
getObjById : function(id, reset){
var deferred = $.Deferred();
//returned cached data if we don't request refreshed data
reset = reset || false;
if (Data[id] && !reset) {
return Data;
}
this._getObj(id, function(loadedObj) {
//cache object
Data[loadedObj.id] = loadedObj
deferred.resolve(loadedObj);
});
return deferred.promise();
},
_getObj: function(id, callback) {
var obj = new Obj({id:id});
obj.fetch({success:callback});
},
};
//Interface for mucking with Obj model
App.reqres.setHandler("obj:getObj", function(id) {
return API.getObjById(id);
});
});
You can get an obj like so. If reset isn't passed in, it will be the cached version (all sub modules can refer to same obj just by passing in ID or w/e your criteria is for loading):
$.when(App.request('obj:getObj', 123)).done(function(loadedObj) {
//show view, or do whatever
);
OR to get fresh data:
$.when(App.request('obj:getObj', 123, true)).done(function(loadedObj) {
//show view, or do whatever
);
These are just examples of how to GET data. You could extend the API and expose new handlers for UPDATING data. You could either have each sub module's controller listen to the obj (by all listening to the same, cached obj model), or just request the most up to date obj model each time you need it. This would be a round trip to the server if your cached data reflects the most recent changes. But then you have to worry about keeping things in sync.
Hope this helps
I have a problem with creating Ember objects from a JSON ajax data source. If I create the object the manual way, it works perfectly, and the view gets updated. If the data itself comes from a JSON ajax data call, however, it does not work. If I inspect the resulting objects, the Ember model objects does not get the correct getter and setter properties. Does anyone know why this happens?
App.AlbumView = Ember.View.extend({
templateName:'album',
albums:[],
getAll:function() {
var self = this;
//This works!
self.albums.push(App.Album.create({title: 'test', artist: 'test'}));
$.post('/Rest/list/album',null,function(data) {
$.each(data, function (index, item) {
//This does not work?!?
self.albums.push(App.Album.create(item));
});
}, 'json');
}
});
You should always use embers get('variableName') and set('variableName', newValue) methods when accessing instance variables of a view. Strange things tend to happen if you don't.
i have this result from the restful service i have:
NOTE: Response is in JSON format it's the plugin from chrome which displays it like that.
if you look at image two [the one above this] the models attributes is Items then each item are under Items. What should i do to access item?
my problem is i can't access or retrieve data of each item from this result. i must not change anything from the server side though. I'm using backbone with this code.
window.Item = Backbone.Model.extend();
window.ItemCollection = Backbone.Collection.extend({
model: Item,
url: 'http://localhost/InterprisePOS/Product/loaditembycategory/Event Materials'
});
window.ItemListView = Backbone.View.extend({
tagName : 'ul',
initialize: function(){
this.model.bind("reset",this.render,this);
},
render: function(eventName){
_.each(this.model.models.Items, function(item){
$(this.el).append(new ItemListItemView({model:item}).render.el);
},this);
return this;
}
});
window.ItemListItemView = Backbone.View.extend({
template : _.template($("#item-list").html()),
render: function(eventName){
$(this.el).html(this.template(this.model.toJSON()));
return this;
}
});
var AppRouter = Backbone.Router.extend({
routes:{
"":"list"
},
list:function(){
this.itemList = new ItemCollection();
this.itemListView = new ItemListView({model:this.itemList});
this.itemList.fetch();
$("#itemContainer").html(this.itemListView.render().el);
}
});
var app = new AppRouter();
Backbone.history.start();
UPDATE
I was able to correct the my problem with nested json objects. Now the Models attrib or my Collection is populated with individual items. But still the problems is it doesn't work and doesn't display my views.
This is the code i added:
parse: function(response) {
return response.Items;
}
UPDATE
I Finally answered my question! horray! somehow i forgot to put "()" on render in my ItemListview. and also $("#ItemContainer") doesn't seem to work so i made it to $('#ItemContainer) now i'm displaying the Details from my model.
I'm fairly certain that Backbone by default uses JSON for all requests and has no idea what to do with XML, you'll probably need to override the sync method for the collection in order to us use XML.
The following should be helpful in addressing your issue: http://newcome.wordpress.com/2011/02/20/consuming-xml-web-services-in-backbone-js-using-jath/
They use a 3rd party library xml parser in the sync parse operation which converts the model to JSON which Backbone can use natively.
Make sure the response is returned as JSON. Backbone works on JSON data by default.