Может ли угловая директива передавать аргументы функциям в выражениях, указанных в атрибутах директивы?

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

scope: { callback: '&' }

Он сидит внутри ng-repeat поэтому выражение, которое я передаю, включает в себя id объекта в качестве аргумента для функции обратного вызова:

<directive ng-repeat = "item in stuff" callback = "callback(item.id)"/>

Когда я закончил с директивой, она вызывает $scope.callback() от его функции контроллера. В большинстве случаев это хорошо, и это все, что я хочу сделать, но иногда я хотел бы добавить еще один аргумент изнутри directive сам.

Есть ли угловое выражение, которое позволило бы это: $scope.callback(arg2), в результате чего callback вызывается с arguments = [item.id, arg2]?

Если нет, то какой самый лучший способ сделать это?

Я обнаружил, что это работает:

<directive 
  ng-repeat = "item in stuff" 
  callback = "callback" 
  callback-arg="item.id"/>

С

scope { callback: '=', callbackArg: '=' }

и директива вызова

$scope.callback.apply(null, [$scope.callbackArg].concat([arg2, arg3]) );

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

Есть ли способ лучше?

Здесь есть плункерная площадка (консоль открыта).

4 ответа

Решение

Если вы объявите свой обратный вызов как упомянуто @lex82 как

callback = "callback(item.id, arg2)"

Вы можете вызвать метод обратного вызова в области действия директивы с картой объекта, и он будет выполнять привязку правильно. подобно

scope.callback({arg2:"some value"});

не требуя для разбора $. Смотрите мою скрипку (консольный журнал) http://jsfiddle.net/k7czc/2/

Обновление: есть небольшой пример этого в документации:

& или &attr - предоставляет способ выполнить выражение в контексте родительской области. Если имя атрибута не указано, то предполагается, что имя атрибута совпадает с локальным именем. Для данного виджета и определения видимости области: { localFn:'&myAttr' }, то свойство изоляции области localFn будет указывать на функцию-оболочку для выражения count = count + value. Часто желательно передавать данные из изолированной области видимости через выражение и в родительскую область видимости, это можно сделать, передав карту имен и значений локальных переменных в оболочку выражения fn. Например, если выражение является приращением (количеством), то мы можем указать значение суммы, вызвав localFn как localFn({amount: 22}).

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

Не включайте скобки при включении директивы в ваш HTML:

<my-directive callback="someFunction" />

Затем "разверните" функцию в ссылке или контроллере вашей директивы. вот пример:

app.directive("myDirective", function() {

    return {
        restrict: "E",
        scope: {
            callback: "&"                              
        },
        template: "<div ng-click='callback(data)'></div>", // call function this way...
        link: function(scope, element, attrs) {
            // unwrap the function
            scope.callback = scope.callback(); 

            scope.data = "data from somewhere";

            element.bind("click",function() {
                scope.$apply(function() {
                    callback(data);                        // ...or this way
                });
            });
        }
    }
}]);    

Шаг "разворачивания" позволяет вызывать функцию с использованием более естественного синтаксиса. Это также гарантирует, что директива работает должным образом, даже если она вложена в другие директивы, которые могут передавать функцию. Если вы не делали развертывание, то если у вас есть такой сценарий:

<outer-directive callback="someFunction" >
    <middle-directive callback="callback" >
        <inner-directive callback="callback" />
    </middle-directive>
</outer-directive>

Тогда вы получите что-то подобное в своей внутренней директиве:

callback()()()(data); 

Который потерпит неудачу в других сценариях вложенности.

Я адаптировал эту технику из отличной статьи Дэна Уолина на http://weblogs.asp.net/dwahlin/creating-custom-angularjs-directives-part-3-isolate-scope-and-function-parameters

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

В директиве (myDirective):

...
directive.scope = {  
    boundFunction: '&',
    model: '=',
};
...
return directive;

В директиве шаблона:

<div 
data-ng-repeat="item in model"  
data-ng-click='boundFunction({param: item})'>
{{item.myValue}}
</div>

В источнике:

<my-directive 
model='myData' 
bound-function='myFunction(param)'>
</my-directive>

...где myFunction определяется в контроллере.

Обратите внимание, что param в шаблоне директивы аккуратно привязывается к param в источнике, и установлен в item,


Позвонить изнутри link свойство директивы ("внутри"), использовать очень похожий подход:

...
directive.link = function(isolatedScope) {
    isolatedScope.boundFunction({param: "foo"});
};
...
return directive;

Да, есть лучший способ: вы можете использовать службу $ parse в вашей директиве для оценки выражения в контексте родительской области, связывая определенные идентификаторы в выражении со значениями, видимыми только внутри вашей директивы:

$parse(attributes.callback)(scope.$parent, { arg2: yourSecondArgument });

Добавьте эту строку в функцию ссылки директивы, где вы можете получить доступ к атрибутам директивы.

Ваш атрибут обратного вызова может быть установлен как callback = "callback(item.id, arg2)" потому что arg2 связан с yourSecondArgument службой $ parse внутри директивы. Директивы как ng-click позволит вам получить доступ к событию клика через $event идентификатор внутри выражения, переданного директиве с использованием именно этого механизма.

Обратите внимание, что вам не нужно делать callback член вашей изолированной области с этим решением.

У меня работает следующее:

в директиве объявить это так:

.directive('myDirective', function() {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            myFunction: '=',
        },
        templateUrl: 'myDirective.html'
    };
})  

В шаблоне директивы используйте его следующим образом:

<select ng-change="myFunction(selectedAmount)">

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

<data-my-directive
    data-my-function="setSelectedAmount">
</data-my-directive>

Вы передаете функцию по ее объявлению, и она вызывается из директивы, а параметры заполняются.

Другие вопросы по тегам