2015-03-18 3 views
1
включен через

У меня есть директива с областью действий включено через что-то вроде этого:

<my-control> 
    Some Content: {{value}} 
</my-control> 

Где value исходит из родительской области.

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

<my-control> 
    Some Content: {{value}} 
    <button ng-click="$close()">Close</button> 
</my-control> 

Подобно тому, как ngRepeat добавляет свойства как $index в рамках ряда. Каков самый простой способ сделать это в моей директиве?

+0

Привет Павел! Будет ли что-то вроде этой работы для вас? http://plnkr.co/edit/SPSFGcB49qXmXROmNeHj?p=preview – sergiocruz

+0

@sergiocruz, мне нужна директива, чтобы иметь изолированный объем, см. Принятый ответ. – Paul

+0

True ... ну, я просто хочу указать, что конечный результат тот же, если вы добавите 'scope: {}' в объявление директивы. Плюс немного чище IMHO :) – sergiocruz

ответ

1

Если мы не укажем ни scope:true (новый масштаб), ни scope:{} (isolatedScope), а когда мы повторно используем директиву, свойства, определенные в области видимости, будут переопределены.

Для Ex:

<div ng-controller="AppCtrl"> 
    <my-control name="myControl1"> 
     Some Content: {{value}} 
     My Control Name: {{name}} 
    </my-control> 
    <my-control name="myControl2"> 
     Some Content: {{value}} 
     My Control Name: {{name}} 
    </my-control> 
</div> 

Вместо печати как myControl1 и myControl2 на экране, он будет печатать myControl2 два раза.

Plnkr

Чтобы преодолеть этот вопрос попробовать любой из приведенных ниже решений.

Solution1

transclde:true создаст новую область. установите свойства в этой области действия вместо области действия директивы.

app.directive('myControl', function() { 
    return { 
    restrict: 'E', 
    transclude: true, 
    template: '<div><p><strong>Welcome to the testMe directive!</strong></p> <div ng-transclude></div></div>', 
    link: function(scope, element, attrs) { 
     var transclusionTarget = element[0].querySelector('[ng-transclude]').firstChild; 
     var transclusionScope = angular.element(transclusionTarget).scope(); 
     transclusionScope.name = attrs.name; 
    } 
    } 
}); 

здесь элемент под ng-transclude DIV будет скомпилирован с transclusionScope, захватить его и обновить свойства в нем.

Plnkr

Solution2 Вместо использования ng-transclude вручную transclude содержание.

app.directive('myControl', function() { 
    return { 
    restrict: 'E', 
    transclude: true, 
    template: '<div><p><strong>Welcome to the testMe directive!</strong></p> <div transclude-target></div></div>', 
    link: function(scope, element, attrs, directiveCtrl, transcludeFn) { 

     var transclusionScope = scope.$new(), 
      transclusionTarget = element[0].querySelector('[transclude-target]'); 

     transclusionScope.name = attrs.name; 

     transcludeFn(transclusionScope, function (clone) { 
     angular.element(transclusionTarget).append(clone); 
     }); 
    } 
    } 
}); 

Здесь, создать new Scope расширения сферы дИРЕКТИВЫ, используя scope.$new(). И обновите свойства в нем.

Plnkr

Solution1 не может работать во всех случаях. К тому моменту, когда мы получаем доступ к firstChild, и если он не готов Решение1 не получится.

Решение2 является более чистым и будет работать во всех случаях.

+0

Большое спасибо, решение 2 сделало трюк. На самом деле, попробовал что-то похожее на это, но это казалось немного запутанным ворчанием с DOM, когда все, что я хотел сделать, это изменить область действия, и я был уверен, что делаю это неправильно. Я думаю, это действительно правильный и единственный вариант. – Paul

+0

Рад помочь вам. :) –

2

Вы можете приложить новые методы к своей сфере действия в своей функции связывания директивы (или даже в контроллере вашей директивы, если у вас есть).

Ради простоты я буду показывать здесь, как присоединить новый метод функции, связывающей директивы:

app.directive('myControl', function() { 
    return { 
     restrict: 'E', 
     transclude: true, 
     template: '<div ng-transclude></div>', 
     link: function postLink(scope) { 
      scope.$close = function close() { 
       console.log("Close function that lives in directive..."); 
      }; 
     } 
    }; 
}); 

В вашем HTML, вы должны быть в состоянии просто называть просто эту функцию:

<my-control> 
    Click <a href ng-click="$close();">close</a> things. 
</mycontrol> 

Также вы проверить это plunker с примером выше работает на практике: http://plnkr.co/edit/SPSFGcB49qXmXROmNeHj?p=preview

Я надеюсь, что это помогает и не стесняйтесь сообщить мне, если я пропустил что-либо, и я буду рад добавить любую дополнительную информацию.

+1

Если вы добавляете свои собственные методы, вам следует избегать префикса '$', чтобы соответствовать соглашениям AngularJS. –

+0

Эй, Стив, насколько я согласен, я просто пытался быть в соответствии с вопросом Павла, поэтому я бы не стал его путать (он спрашивал о методе $ close). – sergiocruz

+0

Также стоит упомянуть великие библиотеки, такие как угловые-ui, префикс их услуг с помощью $. – sergiocruz

0

ответ Вины является правильным, но я бы добавить поворот к нему, чтобы сделать его более «Угловым»

В «угловой» способе разоблачить API директивы лежит через контроллер. Я бы по той же схеме, что директива ngForm использует -

Что-то вроде этого:

app.directive('myControl', function() { 
    return { 
    restrict: 'E', 
    transclude: true, 
    controller: function($scope) { 
     this.$close = function(){ 
      //close me 
     } 
     this.$open = function() { 
      //open me 
     } 

    } 
    template: '<div><p><strong>Welcome to the testMe directive!</strong></p> <div transclude-target></div></div>', 
    link: function(scope, element, attrs, directiveCtrl, transcludeFn) { 

     transcludeFn(scope.$new(), function (clone, transclusionScope) { 
     //only expose the API to the scope if the name attribute is present 
      if(attrs.name) { 
       transclusionScope[name] = directiveCtrl; 
      } 
      angular.element(element[0].querySelector('[transclude-target]').append(clone); 
     }); 
    } 
    } 
}); 

С использованием:

<my-control name="myControl2"> 
    <button ng-click="myControl2.$close()>Close</button> 
</my-control> 
+0

Я думаю, что в моем случае использование имени элемента управления значительно увеличит вероятность столкновения с родительской областью. Плюс ngRepeat добавляет свойства, такие как $ index, непосредственно к области, поэтому есть пример «официального» API, который работает по-другому. – Paul

+0

Пункт использования атрибута «name» заключается в том, что он позволяет вам контролировать, как API добавляется в область действия - поэтому, если есть столкновение, вы просто меняете имя. Нулевой процент вероятности столкновения в этом случае. Я не думаю, что добавление метода (или свойств) непосредственно к области дочерних элементов также является «неправильным». Я только что нашел через опыт, что то, что я предложил, работает хорошо, особенно если API для директивы имеет более одного метода или свойства. –

Смежные вопросы