Angular 为使用异步方法动态生成对象的库服务

Angular service for a library that dynamically generates objects with async methods

我想为以下具有经典异步 success/error 处理程序的非 Angular 库编写一个 Angular 服务包装器,以便它可以很好地集成到 Angular应用使用 promises 并 Angular-aware.

在典型情况下,这只是编写一个用 $q promise:

包装异步调用的外观的问题
doSomething: function(param){
  return $q(function(resolve, reject){
     nonAngularSvc.doSomethingAsync(
        function onSuccess(result) { resolve(result); },
        function onError(error) { reject(error); }
     );
  });
}

问题不仅在于查询后端和 returns 包含 item 对象的 table 对象的库,还有动态创建的 tableitem 个对象,具有异步方法。例如:

nonAngularSvc.query(onSuccess, onError); // onSuccess returns a table object

tableObj.update(onSuccess, onError); // updates all pending changes
tableObj.addItem(item, onSuccess, onError); // adds a new record to the table

itemObj.update(onSuccess, onError); // updates only the current item

// etc...

所以,现在,即使我包装了 query 方法,我仍然使用非 Angular 异步方法获取对象。

理想情况下,我想直接从视图调用 update 函数:

<div ng-repeat="item in table">
  <button ng-click="item.update()">update</button>
  <input ng-model="item.data">
</div>
<button ng-click="table.update()">Save All</button>

当然,这不会触发摘要,任何更新的属性都不会反映到下一个摘要。

或者,我可以在控制器中处理这个:

$scope.update = function(item){
  item.update(function(){
    $scope.$apply(); // this is ugly
  });
}

但我想避免$scope.$apply到处洒。

如何包装动态创建的 tableitem 对象的这些异步方法?有没有好的做法来处理这个问题?

这是一个plunker for illustration

实际上,在编写问题和构建说明性示例时,我认为 Javascript 作为一种动态语言,实际上可以使其相对容易 - 我只需要为每个对象创建一个派生对象动态接收对象并覆盖其异步方法,同时保留所有其他属性的原型继承:

这是一个高级方法:

// wrapper for query
query: function(input) {
  return $q(function(resolve, reject) {

    // SPHelper is the non-Angular service referred in the question
    SPHelper.query(input, function(result) {

      var wrappedResults = [];    
      angular.forEach(result, function(item) {
        var newItem = wrapItem(item); // create a derived object
        this.push(newItem);
      }, wrappedResults);

      resolve(wrappedResults);
    });
  });
}

function wrapItem(item) {
  var newItem = Object.create(item);
  var updateFn = item.update;
  // overwrite the async update function with a promise-base async
  newItem.update = function() {
    return $q(function(resolve) {
      // invoke the original
      updateFn.call(newItem, function(result) {
        resolve(result);
      });
    }).then(); // .then seems to be required to actually trigger a digest
  };
  return newItem;
}

然后我可以完成以下操作:

<button ng-click="item.update()">update</button>

并让视图反映任何 VM 更改。

已更新plunker