-
Notifications
You must be signed in to change notification settings - Fork 624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tutorial: Encapsulating Meteor within AngularJS Services #17
Comments
Have you looked at the new $methods service? Can you also write a simple code of how would you want the service usage to look like? |
Unfortunately, due to an unrelated full-time day job, I just have not had the time to try angular-meteor in my own app yet. However, when I do, I think i will be following patterns I have seen other folk discuss for hooking up with other backend services, like this: http://www.webdeveasy.com/angularjs-data-model/ and Sorry - that's all I have right now.... |
I've looked at the links you sent. So for example a user can create a 'Parties' service. It fine. but I think that with angular-meteor there is less of a need for those then the other services. If you look for example at the PartyDetailsCtrl in the tutorial the only thing you need for changing the party is one line: $collection(Parties).bindOne($scope, 'party', $stateParams.partyId, true, true); If you want lets say to add the invite method, it is also one line: But on the other way, we can change even that to like you suggested: Yes, that might be a good idea.. Would love to hear more thoughts |
I'm thinking if to add it as a chapter after we did everything in the controller (might be simpler for people to understand and start) or start with that approach from the beginning of the tutorial... |
Yes, that would make sense. Personally, I think the best tutorials are those that tell a story from simple through to advanced/complex, so as to have the space to describe why the simpler solutions are not optimal for larger problem areas. However, that said, imho, Meteor on its own seems a great fit for simpler problems without needing any of the structure or complexity (digest cycles, anyone?) of Angular. AngularJS is probably only worth the investment in learning and implementing when having to tackle a serious long-lifetime application, where the encapsulation of functionality, testing and separation of concerns across the MVC layers and beyond are essential to allow a large team to work on the same system. That's difficult to get across in a tutorial. Maybe think about extending the tutorial application requirements for problems that require changing several Mongo - managed Collections from higher-level user actions. e.g. updating an activity stream (who did what to which party), automatically posting twitter messages via server-side method, all triggered from a number of different UI user actions. Basically, any scenario where access to the angular-meteor API and state needs to be shared across multiple pages / controllers. That's a long-winded way of saying I agree with your approach! |
So your issue made me think more about the way we bind to a collection. Let's say we have a Parties service/provider:
And defining the Parties will be here:
My issue is binding the Parties collection with $scope.parties with this syntax: Right now with the $collaction.bind function we watch the scope. I'm not sure how to do that with this syntax.. would love help and ideas about that |
I have set up my first angular-meteor project and spent a couple of hours playing around with this problem. I tried out three solutions, described below with some code snippets. I hope this makes sense. Use $collection.$bind in Controllerangular.module('myApp')
.controller('myController', ['$scope', '$collection', function($scope, $collection){
$collection(MyCollection).bind($scope, 'controllerBoundCollection', true, true); This just works. But means all the Controllers need to directly use the angular-meteor Wrap $collection in a serviceA service like this: angular.module('myApp')
.factory('MyService', ['$collection', '$q', '$rootScope', function($collection, $q, $rootScope){
return {
/**
* Simply binds the Meteor $collection service query result to the given scopeProperty on the given scope
* Break separation of concerns between service and controller layer ... services should not use $scopes managed by view controllers
*/
bindCollectionToScope: function(scope, scopeProperty) {
$collection(MyCollection).bind(scope, scopeProperty, true, true);
}
};
}]) ... is used by the controller like this: angular.module('myApp')
.controller('MyController', ['$scope', 'MyService', function($scope, MyService){
// Here, we pass reference to this controller's $scope.
// The 'bindToScope' service method is a thin wrapper over $collection call
MyService.bindCollectionToScope($scope, 'serviceBoundCollection'); Here, we have encapsulated the knowledge of the Meteor-specific Broadcast events from a service to inform controllers of operations on the collectionangular.module('myApp')
.factory('MyService', ['$collection', '$q', '$rootScope', function($collection, $q, $rootScope){
var numberObservers = 0;
var observerHandle;
return {
/**
* As an alternative to the above,
* set up a live query handle on a Meteor collection cursor, broadcasting events on changes
* See: http://www.bennadel.com/blog/2694-my-first-look-at-using-the-firebase-backend-as-a-service-baas-in-angularjs.htm for a similar example wrapped around firebase
* All clients share same collection query.
*/
startObserving: function () {
numberObservers++;
console.log("Increasing observer count to:"+numberObservers);
if (numberObservers > 1)
return; // Already observing
observerHandle = MyCollection.find()
.observe({
added: function (data) {
console.log('Object added:'+data);
$rootScope.$broadcast( "addedToMyCollection", data );
},
changed: function (newData, oldData) {
console.log('Data changed:'+data);
$rootScope.$broadcast( "changeToMyCollection", newData, oldData );
},
removed: function (oldData) {
console.log('Data removed:'+oldData);
$rootScope.$broadcast( "removedFromMyCollection", oldData );
},
movedTo: function (data, fromIndex, toIndex, before) {
console.log('Data moved:'+data);
// Do something here!
}
});
},
stopObserving: function () {
if (numberObservers == 0)
// No action if there are no observers left anyway
return;
numberObservers--;
console.log("Decreasing observer count to:"+numberObservers);
if (numberObservers == 0)
// if number of interested observers fell to zero, stop live query
observerHandle.stop();
},
};
}]) Then the controller listens to the broadcast events to update its $scope: $scope.observedCollection = [];
var observerHandle;
// Set up live query of MyCollection
MyService.startObserving();
$scope.$on('addedToMyCollection', function (event, data) {
addDataToList(data);
});
$scope.$on('changeToMyCollection', function (event, newData, oldData) {
changeDataInList(newData);
});
$scope.$on('removedFromMyCollection', function (event, data) {
removeDataFromList(data);
});
$scope.$on('$destroy', function () {
// Call observer handle to stop querying and publishing any more events for this controller.
observerHandle.stop();
}) In this attempt, the MyService uses the find().observe() call from the Meteor cursor API, instead of the angular-meteor The encapsulation is now complete:
I can test MyController by stubbing the MyService calls (but now much simpler as there is no action that directly impacts the controller), and generating I would be very interested in anyone's thoughts on this. Clearly, #3 needs a lot more boilerplate code to get the benefits of encapsulation and testability. $100,000 question, is when is this likely to be worth it? |
angular-meteor is brilliant ! Meteor alone , although a splendid piece of technology, will never get momentum for large enterprise apps. Angular even given the 1.3-->2 saga has trust. |
@rjsmith @paulvanbladel please take a look at the new version: I think I've addressed this issue there |
Closing. |
Encapsulating interaction with Meteor collections / methods etc inside one or more Angular Services.
I prefer to keep my Angular controllers as simple as possible, focused only on defining the $scope models and UI - specific behaviour methods. Any backend - specific work should be encapsulated in a Service layer. This has several advantages, such as testing (mock out the Service when testing the Controller), and theoretical ability to swap out Meteor for another backend (hello, open-sourced Firebase in the future?!). So, a tutorial to demonstrate ways of using the angular-meteor services ($collection, $users etc) wrapped in Angular services, rather than directly attaching to a controller $scope, would be very useful for more advanced front-end architectures.
The text was updated successfully, but these errors were encountered: