3

I have a bunch of data types : users, products, categories. I have a single view/controller for CRUDing each type of data. Therefore, my view allows to create an item, it displays a list, and for each item you can edit and update, or delete it.
The controller manages all UI interactions. For example, when an item is deleted, the user has access to a cancel button for several seconds. Into the controller all CRUD operations are done through services.

angular.module('myApp').controller(
'UsersManagerController', 
function UsersManagerController($scope, $timeout, userService) {

$scope.deleteUser = function(user) {    
    user.deleting = true;
    user.deletionCancelable = true;
    user.deletePromise = $timeout( function() {
        user.deletionCancelable = false;
        userService.deleteUser(user.id);
    }, 2000);       
};

$scope.isUserDeleting = function(user) {
    return user.deleting;
}

$scope.cancelDelete = function(user) {
    $timeout.cancel(user.deletePromise);
    user.deletePromise = null;
    user.deleting = false;
    user.deletionCancelable = false;
}   

Now, I would like to avoid repeating this controller code for each type of data (products, categories...), and only write several views. It means I would like a single controller which could receive dynamically a service exposing the CRUD methods for a particular data type. I have managed to do this, using the $injector. But I don't know how to give the controller the service name from the view. I have tried this :

<div ng-controller="ItemsManagerController" ng-init="setItemService('userService')">

And into the controller :

    var _crudService;
    $scope.setCrudService = function(serviceName) {
        _crudService = $injector.get(serviceName);
}

But the setCrudService() is called at runtime, managed by the AngularJs binding. It means I cannot known exactly when it will be called, and some other methods of my controller are called before, trying to use the service that has not been set yet.

Does anyone knows how to do this ?
Maybe my design has a flaw ?


UPDATE :
I found a solution : scope inheritance.

<div ng-controller="UsersManagerController">
<div ng-controller="ItemsManagerController">
    <div ng-repeat="user in items">
        <span ng-click="deleteItem(user.id)">Remove {{user.name}}</span>
    </div>
</div>
</div>

1) UsersManagerController will be initialized first.
2) ItemsManagerController scope will inherit UsersManagerController scope.

angular.module('myApp').controller('UsersManagerController', 
['$scope', 'userService', function UsersManagerController($scope, userService) {    
    $scope.crudService = userService;
}]);    

angular.module('myApp').controller('ItemsManagerController', 
['$scope', function ItemsManagerController($scope) {    

    $scope.deleteItem = function(itemId) {
        $scope.crudService.delete(itemId);
    }

    $scope.items = crudService.getItems();
}]);    

It works, but I have to write a controller for each data type. I am still looking for a more elegant solution.

user3173982
  • 131
  • 7
  • 2
    The way i've done this before is to create a directive to manage the CRUD operations of an entity, the entity is set in an init function of the directive. Why not have a directive and have 'EntityType' as an attribute and do something similar? The posts back to server can be decided based on the entity. I can elaborate more if this sounds right to you? – Daniel Dawes Jan 21 '14 at 17:23
  • Thank you for your answer, and yes, I would like to know more :-) How does the controller will interact with the directive ? If you click the delete button, the controller $scope.deleteItem() is called. How does it know how to delete an item which can be a user, a product or a category ? Also, the views are not the same if the data type is an user or a product. My initial idea is to see the controller as an API provider, and the views use this API. – user3173982 Jan 22 '14 at 10:19
  • You can pass events around into the directive, and hook up callbacks if you wish. I would have a directive for each entity, something like . In terms of how the data is loaded and saved, I use restangular and each entity has it's own controller, so I can do Restangular.all(entityName).getList()... So the directive itself manages the delete/update/create of the entites. The data looks different for different entities, so I use a meta data controller that returns the fields for an entity, and the crud grid builds itself based on that. – Daniel Dawes Jan 23 '14 at 17:31
  • Another way to do that would be to directly instantiate ItemsManagerController inside UsersManagerController. See http://stackoverflow.com/questions/18461263/can-an-angularjs-controller-inherit-from-another-contoller-in-the-same-module – gwendall Feb 25 '14 at 08:34

1 Answers1

0

I can think of this way

Setup CRUD generating service

app.service('CRUDService', function($timeout) {
  return {

      setCRUD: function(scope, service, objName) {

          var funcName = "delete" + objName;
          scope[funcName] = function(obj) {    
              obj.deleting = true;
              obj.deletionCancelable = true;
              obj.deletePromise = $timeout( function() {
                  obj.deletionCancelable = false;
                  service.deleteUser(user.id);
              }, 2000);       
          };

          funcName = "is" + objName + "Deleting";
          scope[funcName] = function(obj) {
              return obj.deleting;
          }

          scope.cancelDelete = function(obj) {
              $timeout.cancel(obj.deletePromise);
              obj.deletePromise = null;
              obj.deleting = false;
              obj.deletionCancelable = false;
          }    

      }

}

Then, in controller, use it

   CRUDService.setCRUD($scope, userService, "User");
allenhwkim
  • 27,270
  • 18
  • 89
  • 122
  • I like the way you create dynamic functions in the setCRUD object :-) But... It does not fit my needs : how do I give the controller the userService, or the productService ? With your code I have to write a controller for each data type, and the UI interactions code will be repeated in each of them. Right now I have a single controller which is able to manage all kind of view (the views have to respect the controller API) and all kind of data type through services. How I initialize the controller with the wanted service for a given data type ? – user3173982 Jan 22 '14 at 10:14