1

I can't seem to figure out which way to go about this problem:

I got multiple angular controllers, they all need a user-object, which they can manipulate with, and each time it gets manipulated the "new" data needs to be out in the other controllers aswell. At the same time i need to save this user-object in the database for retrievel afterwards (so a auto save function when data is changed on user-object)

But i can't seem to figure out whether i should using a factory or a service to hold this user-object, and how i can get this factory/service to save the user on change via a API.

I read this Sharing ajax data between multiple controllers in angular using service example where he wants to accomplish some of the same things, but the API part and async automatic saving is really giving me trouble!

Bottom line is, i have multiple controllers which needs a user-object from the serverside. And these controllers manipulates with the same user-object, and after each manipulation(maybe with some debounce), i want to save the object to the database via API.

(first of, i thought of watching the object in the service/factory, but found out i couldn't :( )

Can you guys point me in the right direction?

4 Answers 4

1
  1. You create a User object (either via a service or a factory).
  2. You inject that object into any controller that wants to interact with it.
  3. You $watch that object (from inside the service itself if you consider that functionality to be a core part of the User entity) and update the backend when a change is detected (optionally with a debounce).
  4. For $watching something from inside a service, you can inject the $rootScope service.
  5. Keep in mind that (in a real app) you need to account for errors and corner-cases. E.g. what happens if the user navigates away from the page before the debounce period has passed etc.

Below is a naive POC implementation. In the real world, you'd probably want to use module (like the ones mentioned by Mikke) and wrap yur service around it, in order to save a lot of boilerplate code while interacting with your RESTful data source.


A demo service might look like this:

.factory('User', function ($q, $rootScope, $timeout) {
    var apiEndpoint = '/api/user';
    var guest = {
        username: 'Guest',
        email   : null,
        loggedIn: false
    };
    var user = {};

    user.login = function (credentials) {
        user.logout();

        // In real app: Make a login request
        var request = $q.defer();
        $timeout(function () {
            user.username = 'ExpertSystem';
            user.email    = 'expert@system';
            user.loggedIn = true;
            request.resolve(user);
        }, 1000);

        return request.promise;
    };

    user.logout = function () {
        // In real app: Make a logout request
        angular.extend(user, guest);
    };

    user.update = function () {
        // In real app: Make a request to update user info
        alert('Updating the backend...');
    };

    var updateTimeout;
    user.updateWithDebounce = function () {
        $timeout.cancel(updateTimeout);
        updateTimeout = $timeout(function () {
            user.update();
        }, 1000);
    };

    $rootScope.$watchCollection(function () {
        return user;
    }, function (newValue, oldValue) {
        var shouldUpdate = newValue && newValue.loggedIn &&
                           oldValue && oldValue.loggedIn;
        // Immediate backend update
        //if (shouldUpdate) user.update();

        // Debounced backend update (1 sec debounce)
        if (shouldUpdate) user.updateWithDebounce();
    });

    user.login();

    return user;
});

See, also, this short demo.

Sign up to request clarification or add additional context in comments.

1 Comment

Great example.. After trying to mix it i started all over agian, and took your code and mixed mine in. And it worked perfectly. I just gotta get used to the though of my user object in the controllers has both properties and methods on it. Thanks alot!
1

Whether to create a service or factory is really a design issue.

You probably want to look at libraries such as $resource, Restangular or ThickM, which helps you create services which relate your models to API endpoints for fetching and saving.

Asynchronous automatic saving could be solved either in the service or on controllers.

Disclosure: I'm the author of ThickM.

Comments

0

When sharing data between controllers a service is the way to go. You seem to have a good idea about this already.

For saving the user after each manipulation I don't understand what the issue is. Create a modify user method after which you could do an object comparison. If there is a difference send the PUT request to the api.

2 Comments

But where should i create this comparison? In my little fantasy would i'd like to do something like this in the controller: $scope.user.name.lastName = "Hansen"; (or even a ng-model bind to a input field) And then in the service 'catches' the change and saves the data through a factory i suppose. But how can the service see that there has been a change? i a controller i would $watch, but i can't in the service.
if the service is injected into your controller you can access it easily. ServiceName.user.name.lastName = 'abc' or maybe even better a user object ServiceName.user.editName({lastName:'abc'}). The object could contain a comparison function which the edit function could call after applying the change
0

I recommend you always share data between your controllers (and also directives) using a Service/Factory and injecting it on both sides.

After that, just use your promise resolution function to update the service value.

app.factory('SharedService', function() {
  return {
    sharedObject: {
      value: '',
      value2: ''
    }
  };
});

app.controller('FirstCtrl', function($scope, SharedService) {
  $scope.model = SharedService.sharedObject;
});

app.directive('myDirective',['SharedService', function(SharedService){
  return{
    restrict: 'E',
    link: function(scope){
      scope.model = SharedService.sharedObject;
    },
    template: '<div><input type="text" ng-model="model.value"/></div>'
  }
}]);

Here is a plunkr showing how it can be done: http://plnkr.co/edit/Q1VdKJP2tpvqqJL1LF6m

1 Comment

seems legit. Though i got a problem that the factory data is fetched async, and it seems that the controllers can't get the fetched data from my factory's $http.get.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.