I am defining a backbone.collection from my database. I would like to execute a function which has a callback in the success callback. How could I do it?
function firstFunction(callback){
callback()
}
myCollection = new Backbone.Collection
myCollection.url = /collection
myCollection.fetch({
success: firstFunction(callback)
})
Thank you so much!
If you want to execute some callback function on success you can just subscribe on 'sync' event in your View (if you call fetch there):
var view = new Backbone.View.extend({
initialize: function() {
this.listenTo(this.collection, 'sync', this.yourFirstCallback);
},
yourFirstCallback: function() {
//code of yourFirstCalback here
this.yourSecondCallback();
},
yourSecondCallback: function() {
//code of yourSecondCallback
}
});
But actually this is not very clear what do you exactly want.
If you want to use 'success' property, you can do it by two ways.
1. You can specify callback function directly for 'success':
youCollection.fetch({
success: yourFirstCallback
});
function youFirstCallback(collection, response, options) {
//do your staff here
yourSecondCallback();
}
function yourSecondCallback() {
//do your staff here
}
2. You can execute your callback function in 'success' callback:
youCollection.fetch({
success: function(collection, response, options) {
//what you want to do first is here
yourSecondCallback();
}
});
function youCallback() {
//do your staff here
}
Related
Which is the right way to declare a service ?
.service('myService', function(myFactory) {
myFactory.LoadData("...",function(newData){
this.data= newData;
});
}
or
.service('myService', function() {
var data= {};
myFactory.LoadData("...",function(newData){
data= newData;
return data ;
});
}
I don't want to make http calls everytime I use it but only once, and then access the data whenever I want.
I've tried the first example but i'm getting 'undefined' value when I try to use it in my controller.
EDIT:
ok here is my code:
the service :
.service('ClassService', function(ClassFactory) {
ClassFactory.query(function(rawClasses){
this.classes= [];
rawClasses.forEach(function(classe) {
var classeRow={};
classeRow.grade=classe.grade;
classe.branch.forEach(function(branch) {
classeRow._id=branch._id;
classeRow.branch= branch.name;
classeRow.fees= branch.fees;
branch.sub_class.forEach(function(subClass) {
classeRow.label=subClass.label;
classeRow.name= _getGradeName_(classeRow.grade)+(classeRow.branch || '')+ (subClass.label || '');
console.info('ClasseService hihihi!');
this.classes.push(_byValue_(classeRow));
console.log( this.classes);
}, this);
}, this);
}, this);
});
})
and the controller:
.controller('StudentController', function(StudentFactory, ClassService, $scope, $stateParams) {
//...
$scope.classes= ClassService.classes;
//but here console.log($scope.classes) gives 'undefined'
//...
});
The query in your service is running asynchronously so you need to use a promise to wait for the value to become available.
If you make ClassFactory.query() return a promise instead of using a callback it will all become much simpler. Use thePromise.then() in ClassService to handle the completion, but that also returns a promise which you should expose to the controller. Then in the controller use another .then() to know when the data is available.
So assuming you've updated ClassFactory.query() to return a promise:
.service('ClassService', function(ClassFactory) {
var promise =ClassFactory.query();
this.dataLoaded = promise.then(... the code to convert rawClasses to this.classes goes here...);
})
and:
.controller('StudentController', function(StudentFactory, ClassService, $scope, $stateParams) {
//...
ClassService.dataLoaded.then(function () {
$scope.classes= ClassService.classes;
});
});
I'm having a hard time initializing a simple view with a collection that I'm pulling from an API. Everything I try returns an empty array when I try to return the collection from within the view.
app.models.Employee = Backbone.Model.extend({
});
app.collections.Employees = Backbone.Collection.extend({
Model: app.models.Employee,
url: "http://api.sample.com",
initialize: function(){
this.fetch();
},
parse: function(response){
console.log(response)
},
});
app.views.Container = Backbone.View.extend({
el: $('.container'),
initialize: function(){
this.collection = new app.collections.Employees();
console.log(this.collection)
var that = this;
this.collection.fetch({
success: function(){
that.render();
}
});
},
render: function(){
console.log(this.collection) //returns no models
}
});
You have to use parse correctly:
parse: function(response) {
console.log(response);
return response;
} //`,` - remove it at last item in object
There are 3 problems with your code
You are using parse incorrectly. Per the specifications
The function is passed the raw response object, and should return the
array of model attributes to be added to the collection
so yes, as #Sergey suggested
parse: function(response) {
console.log(response);
return response;
}
or better, just remove your parse function, don't override it.
You are calling your fetch function twice.
First time at this line
this.collection = new app.collections.Employees();
which eventually calls this function
initialize: function(){
this.fetch();
},
Second time is inside your initialize function in the View.
3) You break render
Again, same thing as your parse. You are supposed to
renders the view template from model data, and updates this.el with
the new HTML
Something like this
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
I have a single factory defined with ngResource:
App.factory('Account', function($resource) {
return $resource('url', {}, {
query: { method: 'GET' }
});
});
I am making multiple calls to the query method defined on this factory. The calls can happen asynchronously, but I need to wait for both calls to complete before continuing:
App.controller('AccountsCtrl', function ($scope, Account) {
$scope.loadAccounts = function () {
var billingAccounts = Account.query({ type: 'billing' });
var shippingAccounts = Account.query({ type: 'shipping' });
// wait for both calls to complete before returning
};
});
Is there a way to do this with AngularJS factories defined with ngResource, similar to jQuery's $.when().then() functionality? I would prefer not to add jQuery to my current project.
You'll want to use promises and $q.all().
Basically, you can use it to wrap all of your $resource or $http calls because they return promises.
function doQuery(type) {
var d = $q.defer();
var result = Account.query({ type: type }, function() {
d.resolve(result);
});
return d.promise;
}
$q.all([
doQuery('billing'),
doQuery('shipping')
]).then(function(data) {
var billingAccounts = data[0];
var shippingAccounts = data[1];
//TODO: something...
});
I think a better solution is:
$q.all([
Account.query({ type: 'billing' }).$promise,
Account.query({ type: 'shipping' }).$promise
]).then(function(data) {
var billingAccounts = data[0];
var shippingAccounts = data[1];
//TODO: something...
});
The solution from Ben Lesh is the best but it's not complete. If you need to handle error conditions--and, yes, you do--then you must use the catch method on the promise API like this:
$q.all([
doQuery('billing'),
doQuery('shipping')
]).then(function(data) {
var billingAccounts = data[0];
var shippingAccounts = data[1];
//TODO: something...
}).catch(function(data) {
//TODO: handle the error conditions...
}).finally(function () {
//TODO: do final clean up work, etc...
});
If you don't define catch and all of your promises fail, then the then method won't ever execute and thus will probably leave your interface in a bad state.
I have an application using backbone but whenever I call the fetch() method for the collection it returns undefined:
// App
(function () {
window.app = {};
app.collections = {};
app.models = {};
app.views = {};
$(function () {
app.collections.complaintTypes = new app.collections.ComplaintTypesCollection();
app.views.complaintTypesView = new app.views.ComplaintTypesView({ collection: app.collections.complaintTypes });
});
})();
// Collections
(function (collections, model) {
collections.ComplaintTypesCollection = Backbone.Collection.extend({
initialize: function () {
this.fetch();
},
model: model,
url: '/api/ComplaintTypes'
});
})(app.collections, app.models.ComplaintType);
// Models
(function (models) {
models.ComplaintType = Backbone.Model.extend({
idAttribute: 'ComplaintTypeId'
});
})(app.models);
// Views
(function (views) {
views.ComplaintTypesView = Backbone.View.extend({
initialize: function () {
this.collection.on('reset', this.render, this);
},
render: function () {
console.log(this.collection);
}
});
})(app.views);
But this doesn't return anything? If I use fiddler and go to my URL: /api/ComplaintTypes I do retrieve data back so I'm not sure what Im doing wrong here?
Deleting "model" line in the collection file worked for me.
Fetch is async. If you need to get the results, you need to pass a "success" handler, i.e.:
function myHandler(models) {
}
...
this.fetch({success: myHandler});
I think the problem is that you create the view while the collection has not been fetched yet (because of JS's asynchronous behavior). Try this way:
$(function () {
app.collections.complaintTypes = new app.collections.ComplaintTypesCollection();
app.collections.complaintTypes.fetch({success:function(){
app.views.complaintTypesView = new app.views.ComplaintTypesView({ collection: app.collections.complaintTypes });
}});
});
And remove the initialize function from your collection.
I have Backbone view. It load content (component on backbone with AJAX):
render: function () {
var self = this;
$.get('/Blog', request, function (data) {
self.$el.html(data);
model.trigger('blogContainerLoaded');
});
}
Component code:
window.App = {
init: function (options) {
var BlogModel = Backbone.Model.extend({
});
var model = new BlogModel();
var BlogController = Backbone.Router.extend({
routes: {
"": "posts",
"posts": "posts",
"posts/:postId": "post",
"posts/:postId/": "post",
},
posts: function (anchor) {
window.App.views.posts.render(anchor);
},
post: function (postId, commentId) {
window.App.views.post.render(postId, commentId);
},
});
var controller = new BlogController();
if (notLoaded) {
var views = {
posts: new PostListView({ model: model, controller: controller }),
post: new PostView({ model: model, controller: controller }),
};
this.views = views;
Backbone.history.start();
}
}
};
var PostListView = Backbone.View.extend({
el: $(".posts"),
events: {
"click .js-edit-message": "editMessage",
"click .js-open-post": "navigateToPost",
"click .js-move-post": "move"
},
editMessage: function () {
debugger;
},
navigateToPost: function () {
debugger;
},
move: function () {
debugger;
},
All html code loads with ajax.
It works, but events (clicks and other) not firing!
When I load Blog component without AJAX - events working. Please Help!
Most likely your view is instantiated before your ajax call is complete. Because ajax is asynchronous, while it does load (eventually) your code to initialize the view runs first. Thus, when your view is looking to attach the events to your DOM, the necessary elements are not present.
This would also explain why when you eliminate the ajax portion of it, the code works (it's now synchronous and the DOM is present before you initialize your view code.)
Try putting your initialization code inside the ajax callback like this: (if possible)
render: function () {
var self = this;
$.get('/Blog', request, function (data) {
self.$el.html(data);
self.blogContainer = new BlogContainerView(); // Or however you do it
});
}
Another possibility is to keep your trigger.('blogContentLoaded') then attach the events at that time. Backbone.View's delegateEvents() method might be useful in your case.
Or something of that nature. Now you're guaranteed that the DOM is available for your View object to attach events to.