Code Monkey home page Code Monkey logo

Comments (11)

Urigo avatar Urigo commented on May 21, 2024

Have you looked at the new $methods service?
also the $subscribe service?

Can you also write a simple code of how would you want the service usage to look like?

from angular-meteor.

rjsmith avatar rjsmith commented on May 21, 2024

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

http://www.bennadel.com/blog/2694-my-first-look-at-using-the-firebase-backend-as-a-service-baas-in-angularjs.htm

Sorry - that's all I have right now....

from angular-meteor.

Urigo avatar Urigo commented on May 21, 2024

I've looked at the links you sent.
I think I understand.

So for example a user can create a 'Parties' service.
And then he can do Parties.add, Parties.remove, Parties.update, Parties.invite, Parties.rsvp
and inside it will encapsulate what is going on?

It fine. but I think that with angular-meteor there is less of a need for those then the other services.
you can directly manipulate the scope and that's it.. no need to worry about server

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);
that's it. no add, update etc..

If you want lets say to add the invite method, it is also one line:
$methods.call('invite', $scope.party._id, user._id);

But on the other way, we can change even that to like you suggested:
$scope.party = Parties.bindOne($stateParams.partyId);
$scope.party.invite($scope.party._id, user._id);

Yes, that might be a good idea..

Would love to hear more thoughts

from angular-meteor.

Urigo avatar Urigo commented on May 21, 2024

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

from angular-meteor.

rjsmith avatar rjsmith commented on May 21, 2024

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!

from angular-meteor.

Urigo avatar Urigo commented on May 21, 2024

So your issue made me think more about the way we bind to a collection.
How about this type of usage:

Let's say we have a Parties service/provider:

angular.module("socially").controller("PartiesListCtrl", ['$scope', Parties,
  function($scope, Parties){

    $scope.parties = Parties.get();

    $scope.remove = function(party){
      $scope.parties.splice( $scope.parties.indexOf(party), 1 );
    };

    $scope.outstandingInvitations = function (party) {
      Parties.getOutstandingInvitations(party);
    };

    $scope.rsvp = function(partyId, rsvp){
      Parties.rsvp(partyId, rsvp).then(
        function(data){
          console.log('success responding', data);
        },
        function(err){
          console.log('failed', err);
        }
      );
    };
  }]);



angular.module("socially").controller("PartyDetailsCtrl", ['$scope', '$stateParams', 'Parties',
  function($scope, $stateParams, Parties){

    $scope.party = Parties.get($stateParams.partyId)l

    $scope.invite = function(user){
      Parties.invite($scope.party._id, user._id).then(
        function(data){
          console.log('success inviting', data);
        },
        function(err){
          console.log('failed', err);
        }
      );
    };

    $scope.canInvite = function () {
      if (!$scope.party)
        return false;

      return !$scope.party.public &&
              $scope.party.owner === $rootScope.currentUser._id;
    };
  }]);

And defining the Parties will be here:

angular.module('socially', []).config(function(Parties) {
  Parties.setCollection(Parties);
});

My issue is binding the Parties collection with $scope.parties with this syntax:
$scope.parties = Parties.get();

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

from angular-meteor.

rjsmith avatar rjsmith commented on May 21, 2024

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 Controller

angular.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 $controller service. This could be mitigated by having a top-level app-wide controller (attached to the body DOM element), so all child scopes can access the $scope.controllerBoundCollection property. This might also make the controllers more difficult to test, because you have to stub out the $controller.bind calls (which would need to include setting the $scope property to an expected value to enable the rest of the controller code to be tested properly)

Wrap $collection in a service

A 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 $collection service in an application Service called myService. We pass the $scope from the controller to the Service method, which is not great for separation of concerns ... ideally the $scope of the controller should stay entirely within the controller. I would then to stub out the myService.bindCollectionToScope method when testing the controller.

Broadcast events from a service to inform controllers of operations on the collection

angular.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 $collection. This pattern is very similar to that sketched out in the firebase example I linked to in an earlier post on this thread.

The encapsulation is now complete:

  • MyController has no knowledge of $collection. Its $scope does not leak outside its boundary
  • MyService encapsulates the calls to the $collection angular-meteor service API.

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 $rootscope.broadcast events. Similarly, I can test MyService by stubbing out the MyCollection global variable (edit: hmm, not sure how I would do that .. might need to wrap the MyCollection variable in a Angular Value provider, so I can inject it into MyService and therefore mock it out more easily).

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?

from angular-meteor.

Urigo avatar Urigo commented on May 21, 2024

@rjsmith please have a look at the beginning of the work @asafdav is doing, we are just starting this approach but for me it looks like the right way to go:
5499030

from angular-meteor.

paulvanbladel avatar paulvanbladel commented on May 21, 2024

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.
Therefor, I believe that being able to isolate the client 'proxy' specific material in an angular service/factory is a extremely important feature.

from angular-meteor.

Urigo avatar Urigo commented on May 21, 2024

@rjsmith @paulvanbladel please take a look at the new version:
https://github.com/Urigo/angular-meteor/releases/tag/0.6.0-alpha

I think I've addressed this issue there

from angular-meteor.

Urigo avatar Urigo commented on May 21, 2024

Closing.
Please re-open if you think I haven't addressed everything here

from angular-meteor.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.