I have an json object which I am responding from servlet to knockout js. I want to initialize this data in my view model for that I am writing this code.
success: function (data)
{
var jsondata = data['jsonObj'];
self.PopulateStates = ko.computed(function(){
ko.utils.arrayForEach(jsondata, function(item){
self.States.push(new State(item));
});
});
},
error: function (exception)
{
alert( "fail" );
}
});
My json object as string looks like this
{data:[{"id":"5345345","name":"dsfsdf","ssc":"","bic":"dgffdgfdg"},{"id":"123456","name":"SBI","ssc":"654321","bic":"vxvxc"}]}
js fiddle link is demo
What is my mistake ? Or do I need to do it by mapping plugin of knockout js?
I use this knockout extension, declared before use.
ko.observableArray.fn.map = function (data, Constructor) {
var mappedData = ko.utils.forEach(data, function () {
return new Constructor(data);
});
this(mappedData);
return this;
}
Then in my $.ajax request I do this:
success: function (data)
{
var jsondata = data['jsonObj'];
self.PopulateStates = ko.observableArray().map(data, State);
});
You had the results in a computed observable which isn't what you need.
Another thing I have noticed is that your jsondata is set using the data that gets returned from the GET. You are asking that data for the field jsonObj however, looking at your JSON it seems you don't have this field. I think I am correct in saying you have data as the field with the list of items being returned.
If in your view model you have already declared self.PopulateStates which, I'm guessing you have. You can do this:
var State = function (data) {
var self = this;
self.property = ko.observable().set(data, "property");
}
var viewModel = function () {
var self = this;
self.PopulateStates = ko.observable();
function getStates() {
var request = $.ajax();
request.done(function (data, msg) {
if (data) self.PopulateStates.map(data, State);
});
}
}
If you notice in the State model I have self.property using a custom observable function to set it. All this does is if there is data to set the property to, set it. Otherwise give it a default value. I also have a third parameter that I use when I want it to construct an object for me using the data. This is when I have say, a contact, with a modifiedBy property and this modifiedBy is a user object (or just a complex object)
EDIT
The main thing, which isn't an error, but isn't necessary is the jQuery inclusion. Knockout is built to work independant of jQuery so where you do $(document).ready(function () {}) to make sure this loads on DOM ready isn't needed. This means you don't have to include jQuery if the page doesn't need it.
Here is the update fiddle, this will now work!
Related
I am trying to pass data from directive to controller via service, my service looks like this:
angular
.module('App')
.factory('WizardDataService', WizardDataService);
WizardDataService.$inject = [];
function WizardDataService() {
var wizardFormData = {};
var setWizardData = function (newFormData) {
console.log("wizardFormData: " + JSON.stringify(wizardFormData));
wizardFormData = newFormData;
};
var getWizardData = function () {
return wizardFormData;
};
var resetWizardData = function () {
//To be called when the data stored needs to be discarded
wizardFormData = {};
};
return {
setWizardData: setWizardData,
getWizardData: getWizardData,
resetWizardData: resetWizardData
};
}
But when I try to get data from controller it is not resolved (I think it waits digest loop to finish), So I have to use $timeout function in my controller to wait until it is finished, like this:
$timeout(function(){
//any code in here will automatically have an apply run afterwards
vm.getStoredData = WizardDataService.getWizardData();
$scope.$watchCollection(function () {
console.log("getStoredData callback: " + JSON.stringify(vm.getStoredData));
return vm.getStoredData;
}, function () {
});
}, 300);
Despite of the fact that it works, what I am interested in is, if there is a better way to do this, also if this is bug free and the main question, why we use 300 delay and not 100 (for example) for $timeout and if it always will work (maybe for someone it took more time than 300 to get data from the service).
You can return a promise from your service get method. Then in your controller, you can provide a success method to assign the results. Your service would look like this:
function getWizardData() {
var deferred = $q.defer();
$http.get("/myserver/getWizardData")
.then(function (results) {
deferred.resolve(results.data);
}),
function () {
deferred.reject();
}
return deferred.promise;
}
And in your ng-controller you call your service:
wizardService.getWizardData()
.then(function (results) {
$scope.myData = results;
},
function () { });
No timeouts necessary. If your server is RESTFULL, then use $resource and bind directly.
Use angular.copy to replace the data without changing the object reference.
function WizardDataService() {
var wizardFormData = {};
var setWizardData = function (newFormData) {
console.log("wizardFormData: " + JSON.stringify(wizardFormData));
angular.copy(newFormData, wizardFormData);
};
From the Docs:
angular.copy
Creates a deep copy of source, which should be an object or an array.
If a destination is provided, all of its elements (for arrays) or properties (for objects) are deleted and then all elements/properties from the source are copied to it.
Usage
angular.copy(source, [destination]);
-- AngularJS angular.copy API Reference
This way the object reference remains the same and any clients that have that reference will get updated. There is no need to fetch a new object reference on every update.
I am trying to figure out how to use Pager.js in conjunction with Knockout.js to lazy-load a page and bind its contents. I am trying to translate the demo example, but I am not familiar with require.js and am just getting lost.
I have spent several hours trying to reimplement the system using jQuery's getJSON instead of require and define, but the bindings are failing silently. I am having two issues:
The view model is a JSON array, so I don't know what the array is called
The code is not actually doing a getJSON request (nothing in the logs). And is failing silently.
Here is the code:
<div data-bind="page: {id: 'history', title: 'History', withOnShow: $root.getHistory }">
var ViewModel = function (data) {
var self = this;
ko.mapping.fromJS(data, {}, self);
self.getHistory = function () {
return function (f) {
$.getJSON("#{HistoryR}", function (data) {
viewModel.history = ko.mapping.fromJS(data, {});
f(viewModel.history);
});
}
}
};
$.getJSON("#{HomeR}", function (data) {
viewModel = new ViewModel(data);
pager.extendWithPage(viewModel);
ko.applyBindings(viewModel);
pager.start();
});
I refactored the code some, to fit in with huocp's answer:
self.getExamHistory = function (f) {
$.getJSON("#{ExamHistoryR}", function (data) {
self.history = ko.mapping.fromJSON(data, {});
f(self.history);
});
}
and the getJSON call is getting triggered (and I see the response in my console), but my viewModel.history is still empty.
You did a wrong wrap of withOnShow callback function.
Remove the wrap, you should be fine :-)
self.getHistory = function (f) {
$.getJSON("#{HistoryR}", function (data) {
self.history = ko.mapping.fromJS(data); // can u try self instead of viewModel
f(self.history);
});
};
The reason the Pager.js demo page with extra wrap, is that it use withOnShow: requireVM('invention'), not withOnShow: requireVM. It uses the return value of requireVM function, not the function itself.
I have a resource that returns an array from a query, like so:
.factory('Books', function($resource){
var Books = $resource('/authors/:authorId/books');
return Books;
})
Is it possible to add prototype methods to the array returned from this query? (Note, not to array.prototype).
For example, I'd like to add methods such as hasBookWithTitle(title) to the collection.
The suggestion from ricick is a good one, but if you want to actually have a method on the array that returns, you will have a harder time doing that. Basically what you need to do is create a bit of a wrapper around $resource and its instances. The problem you run into is this line of code from angular-resource.js:
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
This is where the return value from $resource is set up. What happens is "value" is populated and returned while the ajax request is being executed. When the ajax request is completed, the value is returned into "value" above, but by reference (using the angular.copy() method). Each element of the array (for a method like query()) will be an instance of the resource you are operating on.
So a way you could extend this functionality would be something like this (non-tested code, so will probably not work without some adjustments):
var myModule = angular.module('myModule', ['ngResource']);
myModule.factory('Book', function($resource) {
var service = $resource('/authors/:authorId/books'),
origQuery = service.prototype.$query;
service.prototype.$query = function (a1, a2, a3) {
var returnData = origQuery.call(this, a1, a2, a3);
returnData.myCustomMethod = function () {
// Create your custom method here...
return returnData;
}
}
return service;
});
Again, you will have to mess with it a bit, but that's the basic idea.
This is probably a good case for creating a custom service extending resource, and adding utility methods to it, rather than adding methods to the returned values from the default resource service.
var myModule = angular.module('myModule', []);
myModule.factory('Book', function() {
var service = $resource('/authors/:authorId/books');
service.hasBookWithTitle = function(books, title){
//blah blah return true false etc.
}
return service;
});
then
books = Book.list(function(){
//check in the on complete method
var hasBook = Book.hasBookWithTitle(books, 'someTitle');
})
Looking at the code in angular-resource.js (at least for the 1.0.x series) it doesn't appear that you can add in a callback for any sort of default behavior (and this seems like the correct design to me).
If you're just using the value in a single controller, you can pass in a callback whenever you invoke query on the resource:
var books = Book.query(function(data) {
data.hasBookWithTitle = function (title) { ... };
]);
Alternatively, you can create a service which decorates the Books resource, forwards all of the calls to get/query/save/etc., and decorates the array with your method. Example plunk here: http://plnkr.co/edit/NJkPcsuraxesyhxlJ8lg
app.factory("Books",
function ($resource) {
var self = this;
var resource = $resource("sample.json");
return {
get: function(id) { return resource.get(id); },
// implement whatever else you need, save, delete etc.
query: function() {
return resource.query(
function(data) { // success callback
data.hasBookWithTitle = function(title) {
for (var i = 0; i < data.length; i++) {
if (title === data[i].title) {
return true;
}
}
return false;
};
},
function(data, response) { /* optional error callback */}
);
}
};
}
);
Thirdly, and I think this is better but it depends on your requirements, you can just take the functional approach and put the hasBookWithTitle function on your controller, or if the logic needs to be shared, in a utilities service.
I am having trouble getting data binding to work with Knockout when using revealing module pattern.
my javascript is like this
var HMS = HMS || {};
$(function () {
HMS.PatientModel = function () {
this.Patient_Name = ko.observable();
this.Patient_Address = ko.observable();
};
HMS.PatientViewModel = function () {
var patient = ko.observable(),
loadPatient = function () {
patient = new HMS.PatientModel();
patient.Patient_Name("Premkumar");
};
return {
patient: patient,
loadPatient: loadPatient
};
} ();
HMS.PatientViewModel.loadPatient();
ko.applyBindings(HMS.PatientViewModel);
});
I am unable to get the data binding to work with patient name properly. The HTML div tag has data-bind="text:patient.Patient_Name".
Please refer to the code in jsFiddle http://jsfiddle.net/stprem/pp9ym/1/. I would appreciate if you could tell me what I am doing wrong in data binding.
In your loadPatient function you are replacing the patient variable with a new object, but your module already returned a reference to the original observable. So, updating it in this way will not update what the object returned.
Here is an option: http://jsfiddle.net/rniemeyer/pp9ym/6/
Basically, you keep patient as an observable and then update it in your loadPatient function. In your view, using the with binding can help you protect against your object being null, in case you want to load it after you call ko.applyBindings.
I want to create a custom javascript object which contains data returned from a jQuery AJAX request, but I don't know which is the right way to do it. I was thinking maybe one way could be to include the AJAX request inside the constructor function so the object is created like this:
// Constructor function
function CustomObject(dataUrl) {
var that = this;
$.ajax(dataUrl, {
success: function (json) {
that.data = $.parseJSON(json);
}
});
}
// Creating new custom object
var myObject = new CustomObject('http://.....');
Another way may be to use a function which does the AJAX and then returns the new object based on the data from the AJAX response.
function customObject(dataUrl) {
// Constructor function
function CustomObject(data) {
this.data = data;
}
$.ajax(dataUrl, {
success: function (json) {
var data = $.parseJSON(json);
return new CustomObject(data);
}
});
}
// Creating new custom object
var myObject = customObject('http://.....')
I would like to know what is the best practice when doing something like this, as well as advantages/disadvatages of different methods. Maybe you can point me to some article or example on something similiar to what I am trying to do.
Thanks in advance.
I think this would be a better approach, it makes your CustomObject only knowledgeable about the data it contains. Here you delegate the work of creating objects to a factory, and pass in a callback to get a reference to the created object, since ajax is asynchronous. If you don't mind making it synchronous, then the createCustomObject function can just return the instance, and the callback can be removed.
function CustomObject(data) {
this.data = data;
}
var factory = (function(){
function create(dataUrl, objectClass, callback){
$.ajax({
url: dataUrl,
success: function (data) {
callback(new objectClass(data));
}
});
}
return{
createCustomObject: function(dataUrl, callback){
create(dataUrl, CustomObject, callback);
}
};
})();
// Creating new custom object
var myObject = null;
factory.createCustomObject('http://..', function(customObject){
myObject = customObject;
});
I'd argue that the second method is better because then you only create a new CustomObject once the script is actually fully prepared to do so (i.e. it has the data it needs from the AJAX request).