Подтвердить что ты не робот

Угловая связь между директивами

Я новичок в Angular.js, мне нужно, чтобы в моем приложении была какая-то связь между директивами, я прочитал некоторую документацию о ссылке и требую, но не могу точно понять, как это работает.

Для простого примера у меня есть: live fiddle: http://jsfiddle.net/yw235n98/5/

  • 2 директивы: firstDir, secondDir:: с некоторыми данными
  • firstDir имеет функцию щелчка, которая изменит значение данных
  • При срабатывании функции щелчка firsDir Я хочу также изменить данные в secondDir.

HTML:

<body ng-app="myApp">
First Directive :   
<first-dir >
    <h3>{{firstCtrl.data}}</h3>
    <button ng-click="firstCtrl.set('NEW VALUE')">Change Value</button>
</first-dir>
Second Directive : 
<second-dir>
    <h3>{{secondCtrl.data}}</h3>
</second-dir>

Javascript:

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

    app.directive("firstDir", function(){
        return {
            restrict : 'E',
            controller : function(){        
                this.data = 'init value';
                this.set = function(value){
                    this.data = value;
                    // communication with second Directive ???
                }       
            },
            controllerAs : 'firstCtrl'
        };  
    });

    app.directive("secondDir", function(){
        return {
            restrict : 'E',
            controller : function(){        
                this.data = 'init value';   
            },
            controllerAs : 'secondCtrl'
        };  
    });
})();
4b9b3361

Ответ 1

Один из способов общения между ними, используя так называемый eventing.

Одна директива может испускать событие на корнетопере, которое затем может слушать любой, кто хочет. Вы можете использовать $rootScope.$emit или $rootScope.$broadcast для публикации событий с данными и использовать $scope.$on для прослушивания события. В вашем случае вы можете просто сделать $scope.$emit.

app.directive("firstDir", function(){
    return {
        restrict : 'E',
        controller : function($scope){        
            this.data = 'init value';

            this.set = function(value){
             //EMIT THE EVENT WITH DATA
              $scope.$emit('FIRST_DIR_UPDATED', value);
                this.data = value;
                // communication with second Directive ???
            }       
        },
        controllerAs : 'firstCtrl'
    };  
});

app.directive("secondDir", function(){
    return {
        restrict : 'E',
        controller : function($scope){    
          var _that = this;
          //LISTEN TO THE EVENT 
          $scope.$on('FIRST_DIR_UPDATED', function(e, data){
                 _that.data = data;
          });
          this.data = 'init value';   
        },
        controllerAs : 'secondCtrl'
    };  
});

Демо Demo2

____________________________________________________________________________

Говоря о том, что иногда требуется ввести $rootScope, чтобы включить eventing в другой node в вашем приложении. Вместо этого вы можете создать паб/вспомогательный механизм, который легко встроен в ваше приложение и использовать прототипное наследование.

Здесь я добавляю 2 метода publish и subscribe на прототипе $rootScope's во время инициализации приложения. Таким образом, любая область содержимого или изолированная область будет иметь эти методы, и общение будет таким простым, не беспокоясь о том, следует ли использовать $emit, $broadcast, нужно ли мне вводить $rootScope для связи из изолированной директивы и т.д.

app.service('PubSubService', function () {


   return {Initialize:Initialize};

     function Initialize (scope) {
        //Keep a dictionary to store the events and its subscriptions
        var publishEventMap = {};

         //Register publish events
          scope.constructor.prototype.publish =  scope.constructor.prototype.publish 
           || function () {
                var _thisScope = this,
                    handlers, 
                    args, 
                    evnt;
                //Get event and rest of the data
                args = [].slice.call(arguments);
                evnt = args.splice(0, 1);
                //Loop though each handlerMap and invoke the handler
                angular.forEach((publishEventMap[evnt] || []), function (handlerMap) {
                    handlerMap.handler.apply(_thisScope, args);
                })
            }

         //Register Subscribe events
         scope.constructor.prototype.subscribe = scope.constructor.prototype.subscribe 
            || function (evnt, handler) {
                var _thisScope = this,
                    handlers = (publishEventMap[evnt] = publishEventMap[evnt] || []);

                //Just keep the scopeid for reference later for cleanup
                handlers.push({ $id: _thisScope.$id, handler: handler });
              //When scope is destroy remove the handlers that it has subscribed.  
             _thisScope.$on('$destroy', function () {
                for(var i=0,l=handlers.length; i<l; i++){
                  if (handlers[i].$id === _thisScope.$id) {
                        handlers.splice(i, 1);
                        break;
                    }
                }
            });
        }

    }
}).run(function ($rootScope, PubSubService) {
    PubSubService.Initialize($rootScope);
});

и вы могли бы просто разместить любое место в своем приложении, опубликовать событие, не требуя rootScope.

$scope.publish('eventName', data);

и слушать в любом месте приложения, не беспокоясь об использовании $rootScope или $emit или $broadcast: -

$scope.subscribe('eventName', function(data){
    //do somthing
});

Демо-версия PubSub

Ответ 2

В вашем примере структура директивы не является родительским-дочерним. Поэтому вы не можете делиться методами через свои контроллеры. Я использовал бы $rootScope.$broadcast. (См. DOCS)

Одна директива вызывает:

$rootScope.$broadcast('someEvent', [1,2,3]);

Вторая директива прослушивает:

 scope.$on('someEvent', function(event, mass) {
    console.log(mass)}
  );

Демо Fiddle


Исправлены директивы:

app.directive("firstDir", function ($rootScope) {
    return {
        restrict: 'E',
        link: function (scope, element, attrs) {
            scope.dataToPass = 'empty';
            scope.doClick = function (valueToPass) {
                scope.dataToPass = valueToPass;
                $rootScope.$broadcast('someEvent', {
                    data: valueToPass
                });
            }
        }
    };
});

app.directive("secondDir", function () {
    return {
        restrict: 'E',
        link: function (scope, element, attrs) {
            scope.receivedData = 'none';

            scope.$on('someEvent', function (event, result) {
                scope.receivedData = result.data;
            });
        }
    }
});

Ответ 3

Я использую экспортированный контроллер директивы. Скажем, у меня есть следующая директива:

app.directive('mainDirective', function () {
  return {
    require: 'mainDirective'
    restrict: 'E',
    scope: {
      controller: '='
    },
    controller: [
      '$scope',
      function ($scope) {
        // controller methods
        this.doSomething = function () { ... },

        $scope.controller = this
        return this
      }
    ],
    link: function (scope, element, attrs, mainDirective) {
      // some linking stuff
    }
  }
});

Мой html будет выглядеть примерно так:

<main-directive controller="mainDirective"></main-directive>
<sub-directive main-directive="mainDirective"></sub-directive>

Если бы я хотел контролировать главную директиву из под-директивы, я могу легко извлечь ее из нее и сделать все, что захочу...

app.directive('subDirective', function () {
  return {
    restrict: 'E',
    scope: {
      mainDirective: '='
    }
    link: function (scope, element, attrs) {
      // do something with main directive
      scope.mainDirective.doSomething();
    }
  }
});