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

Общение с директивами братьев и сестер

Цель: создание поведения с использованием директив с обменом данными между двумя родственными элементами (каждая их собственная директива).

Поведение для использования в примере: содержимое статьи скрыто по умолчанию. Когда щелкнут заголовок, я хочу, чтобы содержимое соответствующей статьи отображалось.

Ловушка: связанные элементы статьи должны связываться друг с другом, не будучи вложенными в один родительский элемент или директиву.

<div article="article1">this is my header</div>
<div id="article1" article-content>this is content for the header above</div>

<div article="article2">this is my header</div>
<div id="article2" article-content>this is content for the header above</div>

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

Может ли директива content передавать ссылку на соответствующую директиву статьи?

Этот код не очень полезен, как сейчас, но это отправная точка. Как бы это сделать?

.directive('article', function(){
  return {
    restrict: "A",
    controller: function($scope) {
      $scope.contentElement = null;
      this.setContentElement = function(element) {
        $scope.contentElement = element;
      }
    },
    link: function(scope, element) {
      element.bind('click', function(){
        // Show article-content directives that belong
        // to this instance (article1) of the directive
      }
    }
  }
}
.directive('articleContent', function(){
  return {
    require: "article",
    link: function(scope, element, attrs, articleCtrl) {
      // Maybe reference the article i belong to and assign element to it?
      // I can't though because these are siblings.
    }
  }
}
4b9b3361

Ответ 1

Ни одна из опций директивы require не позволит вам требовать директивы sibling (насколько я знаю). Вы можете:

  • требуется элемент, используя require: "directiveName"
  • сообщите angular для поиска в дереве DOM с помощью require: "^directiveName"
  • или require: "^?directiveName", если вам не нужен родительский контроллер
  • или require: "^\?directiveName", если вам необязательно нужна родительская оболочка DOM

Если вы хотите общаться с братом и сестрой, вам придется разместить их в каком-то родительском элементе DOM с директивным контроллером, выступающим в качестве API для их общения. Как это реализовано, в значительной степени зависит от контекста.

Вот хороший пример из Angular JS (O Reilly)

app.directive('accordion', function() {
  return {
    restrict: 'EA',
    replace: true,
    transclude: true,
    template: '<div class="accordion" ng-transclude></div>',
    controller: function() {

      var expanders = [];

      this.gotOpened = function(selectedExpander) {
        angular.forEach(expanders, function(expander) {
          if(selectedExpander != expander) {
            expander.showMe = false;
          }
        });
      };

      this.addExpander = function(expander) {
        expanders.push(expander);
      }

    }
  }
});

app.directive('expander', function() {
  return {
    restrict: 'EA',
    replace: true,
    transclude: true,
    require: '^?accordion',
    scope: { title:'@' },
    template: '<div class="expander">\n  <div class="title" ng-click="toggle()">{{ title }}</div>\n  <div class="body" ng-show="showMe" \n       ng-animate="{ show: \'animated flipInX\' }"\n ng-transclude></div>\n</div>',
    link: function(scope, element, attrs, accordionController) {
      scope.showMe = false;
      accordionController.addExpander(scope);

      scope.toggle = function toggle() {
        scope.showMe = !scope.showMe;
        accordionController.gotOpened(scope);
      }
    }
  }
})

Использование (шаблон нефрита):

accordion
    expander(title="An expander") Woohoo! You can see mme
    expander(title="Hidden") I was hidden!
    expander(title="Stop Work") Seriously, I am going to stop working now.

Ответ 2

Или вы можете создать service только для директивной связи, одним из преимуществ специального service vs require является то, что ваши директивы не будут зависеть от их местоположения в структуре HTML.

Ответ 3

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

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

в службе поиска

$rootScope.$emit('mySearchResultsDone', {
  someData: 'myData'
}); 

в некоторых других директивах/контроллерах

$rootScope.$on('mySearchResultsDone', function(event, data) {
  vm.results = data;
});

Там определенная красота, насколько прост этот код. Тем не менее, важно иметь в виду, что логика emit/on/broadcast может стать очень неприятной, если у вас есть куча разных мест вещания и прослушивания. Быстрый поиск в Google может вызвать много мнений о том, когда это происходит, и не является анти-шаблоном.

Некоторая хорошая информация о emit/broadcast/on в этих сообщениях:

Ответ 4

Если список статей и его содержание мы можем сделать без какой-либо директивы, используя ng-repeat

<div ng-repeat="article in articles">
   <div article="article1" ng-click='showContent=true'>{{article.header}}</div>
   <div id="article1" article-content ng-show='showContent'>{{article.content}}</div>
</div>

Итак, вам нужно определить модель статьи в контроллере. Мы используем локальную область, созданную ng-repeat.

Обновить. На основе ваших отзывов вам нужно связать их вместе. Вы можете попробовать

<div article="article1" content='article1'>this is my header</div>
<div id="article1" article-content>this is content for the header above</div>

и в вашей директиве

использование

link: function(scope, element,attrs) {
      element.bind('click', function(){
        $('#'+attrs.content).show();
      }
    }

И последним методом может быть использование методов $rootScope.$broadcast и scope.$on для связи между контроллерами. Но в этом подходе вам нужно отслеживать, откуда пришло сообщение, и кто является предполагаемым получателем, которому необходимо его обработать.

Ответ 5

У меня была одна и та же проблема, и я смог ее решить.

Чтобы получить одну директиву, чтобы скрыть другие директивы sibling, я использовал родительскую директиву, чтобы действовать как API. Одна дочерняя директива сообщает родителю, что он не должен отображаться/скрываться, передавая ссылку на его элемент, а другой дочерний вызов вызывает функцию переключения родителя.

http://plnkr.co/edit/ZCNEoh

app.directive("parentapi", function() {
  return {
    restrict: "E",
    scope: {},
    controller: function($scope) {
      $scope.elements = [];

      var on = true;
      this.toggleElements = function() {
        if(on) {
          on = false;
          _.each($scope.elements, function(el) {
            $(el).hide();
          });
        } else {
          on = true;
          _.each($scope.elements, function(el) {
            $(el).show();
          });
        }
      }

      this.addElement = function(el) {
        $scope.elements.push(el);
      }
    }
  }
});

app.directive("kidtoggle", function() {
  return {
    restrict: "A",
    require: "^parentapi",
    link: function(scope, element, attrs, ctrl) {
      element.bind('click', function() {
        ctrl.toggleElements();  
      });
    }
  }
});

app.directive("kidhide", function() {
  return {
    restrict: "A",
    require: "^parentapi",
    link: function(scope, element, attrs, ctrl) {
      ctrl.addElement(element);
    }
  }  
});

Ответ 6

У меня была такая же проблема с директивой select all/select, которую я писал. Моя проблема была в том, что флажок "Выбрать все" был в строке заголовка таблицы, а элемент "Выбор" был в теле таблицы. Я обошел это, выполнив службу pub/sub notification, чтобы директивы могли разговаривать друг с другом. Таким образом, моя директива не волновала, как мой htlm был структурирован. Я действительно хотел использовать свойство require, но использование службы работало точно также.