AngularJS中数据保存逻辑的适当位置

时间:2013-11-14 17:03:36

标签: angularjs

应用程序设计问题。我有一个项目,它有很多高度自定义的输入。每个输入都是作为一个指令实现的(Angular使得开发成为一种绝对的快乐)。

输入会在模糊时保存数据,因此无法提交表单。这一直很有效。

每个输入都有一个名为“saveable”的属性,它驱动另一个由所有这些输入类型共享的指令。 Saveable指令使用$ resource将数据发布回API。

我的问题是,这个逻辑应该在一个指令中吗?我最初把它放在那里是因为我认为我需要多个控制器中的保存逻辑,但事实证明它们确实发生在同一个控制器中。另外,我读到了某个地方(丢失了引用),该指令是放置API逻辑的不好的地方。

此外,我需要尽快为这种保存逻辑引入单元测试,测试控制器似乎比测试指令更简单。

提前致谢; Angular的文档可能是......如果......但是社区中的人们都是巨型的。

[编辑]一个非功能性的,简化的看看我正在做的事情:

<input ng-model="question.value" some-input-type-directive saveable ng-blur="saveModel(question)">

.directive('saveable', ['savingService', function(savingService) {
return {
    restrict: 'A',
    link: function(scope) {
        scope.saveModel = function(question) {
            savingService.somethingOrOther.save(
                {id: question.id, answer: question.value}, 
                function(response, getResponseHeaders) {
                    // a bunch of post-processing
                }
           );
        }
    }
}
}])

3 个答案:

答案 0 :(得分:2)

不,我认为该指令不应该调用$http。我会创建一个服务(使用Angular中的factory)或(最好)模型。当它在模型中时,我更喜欢使用$resource服务来定义我的模型“类”。然后,我将$http / REST代码抽象为一个漂亮的活动模型。

答案 1 :(得分:2)

这方面的典型答案是您应该为此目的使用服务。以下是有关此问题的一般信息:http://docs.angularjs.org/guide/dev_guide.services.understanding_services

这是plunk with code modeled after your own starting example

示例代码:

var app = angular.module('savingServiceDemo', []);

app.service('savingService', function() {
  return {
    somethingOrOther: {
      save: function(obj, callback) {
        console.log('Saved:');
        console.dir(obj);
        callback(obj, {});
      }
    }
  };
});

app.directive('saveable', ['savingService', function(savingService) {
  return {
      restrict: 'A',
      link: function(scope) {
          scope.saveModel = function(question) {
              savingService.somethingOrOther.save(
                  {
                    id: question.id, 
                    answer: question.value
                  }, 
                  function(response, getResponseHeaders) {
                      // a bunch of post-processing
                  }
             );
          }
      }
  };
}]);

app.controller('questionController', ['$scope', function($scope) {
  $scope.question = {
    question: 'What kind of AngularJS object should you create to contain data access or network communication logic?',
    id: 1,
    value: ''
  };
}]);

相关的HTML标记:

<body ng-controller="questionController">
  <h3>Question<h3>
  <h4>{{question.question}}</h4>
  Your answer: <input ng-model="question.value" saveable ng-blur="saveModel(question)" />
</body>

仅使用factory和现有ngResource服务的替代方案:

但是,您也可以使用factory和ngResource,以便重复使用某些常见的“保存逻辑”,同时仍然可以为不同类型的对象/数据提供变体。你想保存或查询。而且,这种方式仍然只会导致您的特定对象类型的保护程序的单个实例化。

使用MongoLab集合的示例

我做过类似的事情,以便更容易使用MongoLab集合。

Here's a plunk.

这个想法的要点是这个片段:

  var dbUrl = "https://api.mongolab.com/api/1/databases/YOURDB/collections";
  var apiKey = "YOUR API KEY";

  var collections = [
    "user",
    "question",
    "like"
  ];  

  for(var i = 0; i < collections.length; i++) {
    var collectionName = collections[i];
    app.factory(collectionName, ['$resource', function($resource) {
      var resourceConstructor = createResource($resource, dbUrl, collectionName, apiKey);
      var svc = new resourceConstructor();
      // modify behavior if you want to override defaults
      return svc;
    }]);
  }

备注:

  • dbUrlapiKey当然是特定于您自己的MongoLab信息
  • 在这种情况下,数组是一组不同的集合,您希望单独的ngResource派生的实例
  • 定义了createResource函数(您可以在plunk和下面的代码中看到),它实际上处理了使用ngResource原型创建构造函数。
  • 如果需要,可以修改svc实例以按集合类型
  • 更改其行为
  • 当您模糊输入字段时,这将调用虚拟consoleLog函数,并将一些调试信息写入控制台以供说明。
  • 这也打印了createResource函数本身被调用的次数,作为一种证明,即使实际上有两个控制器,questionControllerquestionController2要求同样的注射,工厂总共被召唤3次。
  • 注意:updateSafe是我喜欢与MongoLab一起使用的一个函数,它允许您应用部分更新,基本上是PATCH。否则,如果您只发送一些属性,整个文档将仅被这些属性覆盖!不好!<​​/ li>

完整代码:

HTML:

  <body>
    <div ng-controller="questionController">
      <h3>Question<h3>
      <h4>{{question.question}}</h4>
      Your answer: <input ng-model="question.value" saveable ng-blur="save(question)" />
    </div>

    <div ng-controller="questionController2">
      <h3>Question<h3>
      <h4>{{question.question}}</h4>
      Your answer: <input ng-model="question.value" saveable ng-blur="save(question)" />
    </div>    
  </body>

JavaScript的:

(function() {
  var app = angular.module('savingServiceDemo', ['ngResource']);

  var numberOfTimesCreateResourceGetsInvokedShouldStopAt3 = 0;

  function createResource(resourceService, resourcePath, resourceName, apiKey) { 
    numberOfTimesCreateResourceGetsInvokedShouldStopAt3++;
    var resource = resourceService(resourcePath + '/' + resourceName + '/:id', 
      {
        apiKey: apiKey
      }, 
      {
        update: 
        {
          method: 'PUT'
        }
      }
    );

    resource.prototype.consoleLog = function (val, cb) {
      console.log("The numberOfTimesCreateResourceGetsInvokedShouldStopAt3 counter is at: " + numberOfTimesCreateResourceGetsInvokedShouldStopAt3);
      console.log('Logging:');
      console.log(val);
      console.log('this =');
      console.log(this);
      if (cb) {
        cb();
      }
    };

    resource.prototype.update = function (cb) {
      return resource.update({
              id: this._id.$oid
          },
          angular.extend({}, this, {
              _id: undefined
          }), cb);
    };

    resource.prototype.updateSafe = function (patch, cb) {
      resource.get({id:this._id.$oid}, function(obj) {
          for(var prop in patch) {
              obj[prop] = patch[prop];
          }
          obj.update(cb);
      });
    };

    resource.prototype.destroy = function (cb) {
      return resource.remove({
          id: this._id.$oid
      }, cb);
    };

    return resource;  
  }


  var dbUrl = "https://api.mongolab.com/api/1/databases/YOURDB/collections";
  var apiKey = "YOUR API KEY";

  var collections = [
    "user",
    "question",
    "like"
  ];  

  for(var i = 0; i < collections.length; i++) {
    var collectionName = collections[i];
    app.factory(collectionName, ['$resource', function($resource) {
      var resourceConstructor = createResource($resource, dbUrl, collectionName, apiKey);
      var svc = new resourceConstructor();
      // modify behavior if you want to override defaults
      return svc;
    }]);
  }

  app.controller('questionController', ['$scope', 'user', 'question', 'like', 
    function($scope, user, question, like) {
      $scope.question = {
        question: 'What kind of AngularJS object should you create to contain data access or network communication logic?',
        id: 1,
        value: ''
      };

      $scope.save = function(obj) {
        question.consoleLog(obj, function() {
          console.log('And, I got called back');
        });
      };
  }]);

  app.controller('questionController2', ['$scope', 'user', 'question', 'like', 
    function($scope, user, question, like) {
      $scope.question = {
        question: 'What is the coolest JS framework of them all?',
        id: 1,
        value: ''
      };

      $scope.save = function(obj) {
        question.consoleLog(obj, function() {
          console.log('You better have said AngularJS');
        });
      };
  }]);  
})();

答案 2 :(得分:0)

通常,与UI相关的事物属于指令,与输入和输出(来自用户或服务器)的绑定相关的事物属于控制器,与业务/应用程序逻辑相关的事物属于在服务(某种程度上)。我发现这种分离导致我的代码非常干净。