Чи може кутова директива передавати аргументи функціям у виразах, визначених в атрибутах директиви?


160

У мене є директива форми, яка використовує вказаний 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]) );

Але я не думаю, що це особливо охайно, і це передбачає розміщення зайвих речей в ізоляторі.

Чи є кращий спосіб?

Тут є ігровий майданчик Plunker (консоль відкрита).


Атрибут іменування "callback =" вводить в оману. Це дійсно оцінка зворотного дзвінка, а не сам зворотний дзвінок.
Дмитро Зайцев

@DmitriZaitsev - це кутове вираження зворотного виклику, яке буде оцінено на функцію JavaScript. Я думаю, що це досить очевидно, що це не сама функція JavaScript. Це просто вподобання, але я вважаю за краще не суфіксувати всі мої атрибути "-expression". Це узгоджується з ngAPI, наприклад ng-click="someFunction()", це вираз, який оцінює виконання функції.
Ед Гінчліфф

Я ніколи не бачив кутового вираження під назвою "зворотний дзвінок". Це завжди функція, яку ви передаєте, щоб її називали, звідки ім'я. Ви навіть використовуєте у своєму прикладі функцію, звану "зворотний виклик", щоб зробити речі ще більш заплутаними.
Дмитро Зайцев

Я не впевнений, чи ти розгубився чи я. У моєму прикладі $scope.callbackвстановлено callback="someFunction"атрибут та scope: { callback: '=' }властивість об'єкта визначення директиви. $scope.callback це функція, яку потрібно викликати пізніше. Фактичне значення атрибута , очевидно, є рядком - це завжди в HTML.
Ед Гінчліфф

Ви називаєте і атрибут, і функцію однакову - "зворотний дзвінок". Ось рецепт плутанини. Легко уникнути насправді.
Дмитро Зайцев

Відповіді:


215

Якщо ви оголосите зворотний дзвінок, як згадує @ lex82, як

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

Ви можете викликати метод зворотного виклику в області директиви за допомогою об'єктної карти, і це зробить прив'язку правильно. Подібно до

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

не вимагаючи $ розбору. Дивіться мою скрипку (консольний журнал) http://jsfiddle.net/k7czc/2/

Оновлення : в документації є невеликий приклад цього :

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


4
Дуже хороша! Це десь задокументовано?
ач

12
Я не думаю, що це гарне рішення, тому що в дефініції директиви іноді ви не знаєте, який параметр потрібно передати.
OMGPOP

Це хороше рішення, і дякую за це, але я вважаю, що відповідь потребує трохи прийомів. Хто такий lex82 і що він згадав?
Wtower

Цікавий підхід. Хоча що відбувається, коли ви хочете дозволити передачу будь-якої функції з будь-яким параметром (або декількома)? Ви нічого не знаєте ні про функцію, ні про її параметри, і потрібно її виконувати в якійсь події всередині директиви. Як це робити? Наприклад, в директиві у вас може бути onchangefunc = 'myCtrlFunc (dynamicVariableHere)'
trainoasis

58

З іншими відповідями нічого поганого, але я використовую наступну техніку при передачі функцій в атрибуті директиви.

Залиште дужки, включаючи директиву у свій 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-izolate-scope-and-function-parameters

Я додав крок розгортання, щоб зробити виклик функції більш природним і вирішити проблему вкладання, з якою я стикався в проекті.


2
Приємний підхід, але я не в змозі використовувати thisвказівник всередині методу зворотного виклику, оскільки він використовує область директиви. Я використовую Typescript, і зворотний дзвінок виглядає приблизно так:public validateFirstName(firstName: string, fieldName: string): ng.IPromise<boolean> { var deferred = this.mQService.defer<boolean>(); ... .then(() => deferred.resolve(true)) .catch((msg) => { deferred.reject(false); }); return deferred.promise; }
ndee

1
Зверніть увагу: якщо ви вклали директиви і хочете поширити зворотний виклик вгору, вам потрібно розгорнути кожну директиву, а не лише одну, що викликає зворотний виклик.
Episodex

43

У директиві ( 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;

Хоча в In source: linked-function = 'myFunction (obj1.param, obj2.param)'> тоді як діяти?
Анкіт Пандей

15

Так, є кращий спосіб: Ви можете використовувати службу $ parse у своїй директиві для оцінки виразу в контексті батьківської області, при цьому прив’язуючи певні ідентифікатори до виразу до значень, видимих ​​лише всередині вашої директиви:

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

Додайте цей рядок до функції посилання директиви, де ви можете отримати доступ до атрибутів директиви.

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

Зауважте, що вам не потрібно складати callbackчлен окремої області за допомогою цього рішення.


3
Використання scope.$parentробить директиву "герметичною" - вона "знає" надто багато зовнішнього світу, що добре розроблений інкапсульований компонент не повинен.
Дмитро Зайцев

3
Ну, воно знає, що він має батьківський обсяг, але він не має доступу до певного поля в області, тому я вважаю, що це допустимо.
lex82

0

Для мене працювали наступні:

в директиві оголосити це так:

.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>

Ви передаєте функцію її декларацією, і вона викликається з директиви, а параметри заповнюються.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.