How can I perform a callback from object constructor? - javascript

I have an object that looks like this:
var users = function(url){
this.users = []
console.time("api");
d3.json(url, function(data){
console.timeEnd("api");
this.users = data.data
})
}
it's instatiated like this:
var liveUsers = new users(apiPoint)
d3.json is an asynchonomous api-call. I wish to perform a callback once it's complete, preferably a chaned one, how can I do this?

All you need is to implement a callback system. Here's one simple way of doing it:
var users = function(url){
var self = this;
this.users = []
console.time("api");
d3.json(url, function(data){
console.timeEnd("api");
self.users = data.data
self.cb && self.cb();
})
this.complete = function (fn) {
this.cb = fn;
};
}
var liveUsers = new users(apiEndpoint).complete(function (){
console.log(this.users);
});
Still seems a bit overly-complicated to me, why do you need it to chain? why does users need to be a constructor? why does users even exist instead of simply using d3.json, which already has all of the functionality you are looking for in your question right out of the box?
Usually the point of abstracting a request behind a function is to avoid needing to specify the api endpoint so that if you need to change the endpoint you can do so all in one place. In this case you have to specify it with each request, making the name of the function kinda... pointless since it can be used to request from any endpoint.

If you want to chain, just return this!
var users = function(url){
this.users = []
console.time("api");
d3.json(url, function(data){
console.timeEnd("api");
this.users = data.data
})
return this;
};
users.prototype.somethingElse = function(){
console.log(this.users);
return this;
};
var liveUsers = new users(apiPoint).somethingElse();
The use of return this keeps the chain going, and you can add additional functions to the class by adding prototype methods. The this is retained by using that prototype capability, however if you wanted to use another function that isn't associated with the class and still use that same this, then you'd need to get a little trickier:
var users = function(url){
this.users = []
console.time("api");
d3.json(url, function(data){
console.timeEnd("api");
this.users = data.data
})
return this;
},
somethingElse = function(){
console.log(this.users);
return this;
};
var liveUsers = new users(apiPoint);
// bunch of other codey stuffs
somethingElse.call(liveUsers);
By using .call() applying liveUsers as the first argument, it overrides whatever this the somethingElse function originally had, and gives it the context that you want (making this === liveUsers).
Hope this helps!

Related

Use $timeout to wait service data resolved

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.

Difference between array of objects in Angularjs

I'm using Angular. I'm trying to compare two arrays of objects
I was able to get it working doing it like this:
var compareUsers = function () {
//Comparing assigned groups with all to return available users
var assignedUsersIds = {};
var usersIds = {};
availableUsers = []; //declared higher up, populated with user objects on load
//assignedUsers, declaired higher up, populated by function calling compareUsers
assignedUsers.forEach(function (el, i) {
assignedUsersIds[el.id] = assignedUsers[i];
});
allUsers.forEach(function (el, i) {
usersIds[el.id] = allUsers[i];
});
for (var i in usersIds) {
if (!assignedUsersIds.hasOwnProperty(i)) {
availableUsers.push(usersIds[i]);
}
};
console.log(availableUsers);
return availableUsers;
}
I found a better way to do it so I refactored it to this, using lodash:
var compareUsers = function () {
availableUsers = _.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
});
console.info(availableUsers);
return availableUsers;
}
However, I'm not getting the correct results and not sure what I messed up. The new methods returns availableUsers which are in the assignedUsers list for some groups. The first time it runs, it seems to work but if I keep changing what group i'm looking at the results are all off and don't add up.
I found this method here.
In the first example you are using Array.push, which is always adding up (obviously), no matter how often you call compareUsers.
In the second example you are overwriting availableUsers each time you are calling compareUsers with the result of _.filter
I'd suggest that instead of doing:
availableUsers = _.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
});
you do:
availableUsers = availableUsers.concat(_.filter(allUsers, function(user){
return !_.findWhere(assignedUsers, user);
}));
This should work. It will concat the availableUsers array with the result of _.filter.

Add methods to a collection returned from an angular resource query

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.

JavaScript OOP Models Formula

Working on creating a dirt simply MVC framework for one of my own projects. Rather than using one that is public, I decided to create one since my needs are very unusual.
I've got my structure down for the Controllers and Views, however, I'm having some issues creating my model structure.
This is what I have for my model structure:
model.models = function(args){
init: function(){
this.on_init();
},
on_init: args.on_init || noop,
data: args.data || {},
};
So then, I would call this as a basic formula for all of the models I want to create. For example, I want to create employees, notifications and some other models using this as a basic blueprint, then make some basic adjustments.
I call:
model.employees = new model.models({
on_init: function(){
//something specific
},
data: {
//defaults
}
});
And we're all good up to this point, but here is where I'm having troubles. Now, when I want to create my end result, the model, I cannot create a new object from an object.. it must be a function.
The only thing I can think of is creating a return function for the second method, but that renders some issues in itself. I have done some research looking at other MVC code, but I was unable to wrap my head around it.
Any help would be very much appreciated!
is this what you want ?
model.models = function(args){
var noop = function(){};
var o = {};
var init = args.on_init || noop;
var data = args.data || {};
init();
//handle other initialization
//o.a = xx;
//o.b = xx;
//o.c = data.xxx;
//....
return o;
}
then you can use the new, and it can't appear syntax error
Did a lot of fiddling, came up with this:
var blueprint = function(args){
return {
data: args.data,
on_init: args.on_init,
create: function(args){
this.on_init();
return {
data: this.data,
whatever: function(){
console.log(args);
}
};
}
};
};
var notifs = new blueprint({
on_init: function(){
console.log('init');
},
data: {
test: 'test'
}
});
var res = notifs.create('test');
console.log(blueprint);
console.log(notifs);
console.log(res);
It comes out with a main function that works, the notifs function is customizable for each individual object type, then calling the create method will create the end method.
Boom!

Creating custom JavaScript object from data returned by jQuery AJAX request

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).

Categories

Resources