如何在 angular js 中管理来自控制器的指令

how to manage a directive from your controller in angular js

我正在寻找 angular 模式来实现离子库使用其 delegate 模式所做的事情。

slide box component为例:

当您在视图中使用它的指令 (<ion-slide-box>) 时,您可以自动在控制器中注入某种与指令对象关联的服务 ($ionicSlideBoxDelegate),您可以使用它来控制它的方法如下:

$ionicSlideBoxDelegate.start()    // Start sliding again if the slideBox was stopped
$ionicSlideBoxDelegate.stop()     // Stop sliding
$ionicSlideBoxDelegate.next()     // Go to the next slide.
$ionicSlideBoxDelegate.previous() // Go to the previous slide
//...

我最好的尝试:

  1. 创建一个仅接收一个参数(委托服务对象)的独立作用域的指令
  2. 在控制器中实例化此委托服务的新实例并将其附加到 $scope
  3. 在视图中将实例传递给指令的委托服务

下面是一些代码来说明我的解决方案。在这个例子中,我试图创建一个 假进度条 指令,它将像一个正常的进度条一样工作,但它会自动前进,除非我们告诉它 stop。它永远不会达到 100%,除非通过调用 complete.

告知这样做

指令及其附加的处理程序(或委托):

angular.module("fakeProgressbar", [])
.directive 'fakeProgressbar', ->
  scope:
    handler: '='
  restrict: 'E'
  replace: false
  template: '
      <progressbar class="progress-striped active" max="handler.config.max" type="{{handler.config.type}}" value="handler.config.value">
        {{ handler.config.value }} %
      </progressbar>
      '
.factory 'FakeProgressbarHandler', ($interval, $timeout, $q) ->
  class FakeProgressbar
    start: -> # ...
    success: -> # ...
    complete: -> # ...
    stop: -> # ...

在你的控制器中:

angular.module("app", ["ui.bootstrap", "fakeProgressbar"])
.controller "fakeProgressbarExampleController", ($scope, FakeProgressbarHandler) ->
  $scope.fakeProgressbarHandler = new FakeProgressbarHandler()
  $scope.fakeProgressbarHandler.start() # start animation inmediately

  # now you can do `$scope.fakeProgressbarHandler.stop()` or `$scope.fakeProgressbarHandler.complete()` 

在您看来:

<fake-progressbar handler="fakeProgressbarHandler"></fake-progressbar>

可在此处找到此工作代码的完整示例:http://plnkr.co/edit/Ehsuu1nKxeu2W7ktTVFC?p=preview

问题:

当然,问题是如何摆脱必须将 fakeProgressbarHandler 附加到作用域以便稍后将其传回指令。

ionic 委托模式 似乎自动实现了这一点。

离子代表并没有太多的魔力。每当使用该指令时,控制器都会向委托服务注册一个实例。例如在 slideBox 中:

https://github.com/driftyco/ionic/blob/master/js/angular/directive/slideBox.js#L127

var deregisterInstance = $ionicSlideBoxDelegate._registerInstance(
  slider, $attrs.delegateHandle, function() {
    return $ionicHistory.isActiveScope($scope);
  }
);

委托代码主要在这里找到:

https://github.com/driftyco/ionic/blob/master/js/utils/delegateService.js

  DelegateService.prototype._registerInstance = function(instance, handle, filterFn) {
    var instances = this._instances;
    instance.$$delegateHandle = handle;
    instance.$$filterFn = filterFn || trueFn;
    instances.push(instance);

    return function deregister() {
      var index = instances.indexOf(instance);
      if (index !== -1) {
        instances.splice(index, 1);
      }
    };
  };

这里是一个简单的注册示例(未经测试):

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

app.controller('firstController', function($scope, DelegateService) {
  $scope.start = function(handle) {
    DelegateService.getInstance(handle).start();
  };
  $scope.stop = function(handle) {
    DelegateService.getInstance(handle).stop();
  };
});

app.controller('secondController', function($scope, DelegateService) {
  $scope.startAll = function() {
    Object.keys(DelegateService._instances).forEach(function(handle) {
      DelegateService._instances[handle].start();
    });
  };
  $scope.stopAll = function(handle) {
    Object.keys(DelegateService._instances).forEach(function(handle) {
      DelegateService._instances[handle].stop();
    });
  };
});

app.directive('someDirective', function(SomeDirectiveDelegate, DelegateService) {
  return {
    scope: {},
    controller: function($scope, $element, $attrs) {
      $scope.progress = {
        amount: 0
      };
      var delegate = new SomeDirectiveDelegate($scope.progress);
      DelegateService._registerInstance(delegate, $attrs.handle);
    },
    template: '<div>{{ progress.amount }}</div>'
  };
});

app.factory('SomeDirectiveDelegate', function($interval) {
  function SomeDirectiveDelegate(progress) {
    var interv;

    function stop() {
      if (interv) {
        $interval.cancel(interv);
        interv = null;
      }
    }

    function start() {
      stop();
      interv = $interval(function() {
        progress.amount += 1;
      }, 100);
    }

    return {
      start: start,
      stop: stop,
    };
  }
  return SomeDirectiveDelegate;
});

app.service('DelegateService', function() {
  this._instances = {};
  this._registerInstance = function(instance, handle) {
    this._instances[handle] = instance;
  };
  this.getInstance = function(handle) {
    return this._instances[handle];
  };
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<div ng-app='app'>
  <div ng-controller='firstController'>
    <some-directive handle="1"></some-directive>
    <button ng-click="start(1)">start1</button>
    <button ng-click="stop(1)">stop1</button>
    <some-directive handle="2"></some-directive>
    <button ng-click="start(2)">start2</button>
    <button ng-click="stop(2)">stop2</button>
  </div>
  <hr>
  <div ng-controller='secondController'>
    <button ng-click="startAll()">start all</button>
    <button ng-click="stopAll()">stop all</button>
  </div>
</div>