Правильный способ передачи функций директиве для выполнения в link

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

.directive('myComponent', function () {
    return {
        scope:{
            foo: '&'
        }        
    };
})

и тогда в шаблоне мы можем назвать эту функцию так:

<button class="btn" ng-click="foo({ myVal: value })">Submit</button>

здесь myVal - это имя параметра, функция foo в родительской области принимает.

теперь, если я намерен использовать это из link функция вместо шаблона, я должен буду вызвать его с:scope.foo()(value) С scope.foo служит оберткой исходной функции. Этот мне это кажется немного скучным.

если я передам функцию myComponent директива с использованием =:

.directive('myComponent', function () {
    return {
        scope:{
            foo: '='
        }        
    };
})

тогда я смогу просто использовать scope.foo(value) из моей функции. Итак, это допустимый вариант использования для использования двухсторонней привязки к функциям, или я делаю какой-то хак, который я не должен делать?

2 ответов


вот почему я отклонил ответ.

во-первых, вы никогда не должны использовать '=' для передачи ссылок на функции директивам.

' = ' создает два вахты и использует их, чтобы гарантировать, что и область директивы и ссылки родительской области одинаковы (двусторонняя привязка). Это действительно плохая идея позволить директиве изменить определение функции в родительской области, что происходит при использовании этого типа привязки. Кроме того, часы должны быть сведено к минимуму-в то время как это будет работать, два дополнительных $часы не нужны. Так что это не нормально-часть голосования была за то, чтобы предположить, что это было.

второй-ответ искажает то, что делает"&". & не является "односторонней привязкой". Он получает это неправильное имя просто потому, что, в отличие от'=', он не создает никаких $watches и изменение значения свойства в области директивы не распространяется на родительский объект.

по документам:

& или &attr-предоставляет способ выполнения выражения в контексте родительская область

при использовании & в директиве создается функция, возвращающая значение выражения, вычисленного в родительской области. Выражение не обязательно должно быть вызовом функции. Это может быть любое допустимое угловое выражение. Кроме того, эта сгенерированная функция принимает аргумент объекта, который может переопределить значение любой локальной переменной, найденной в выражение.

чтобы расширить пример OP, предположим, что родитель использует эту директиву следующим образом:

<my-component foo="go()">

в директиве (шаблон или функция ссылки), если вы вызываете

foo({myVal: 42});

то, что вы делаете, это оценка выражения "go ()", которое вызывает функцию "go" в родительской области, не передавая никаких аргументов.

кроме того,

<my-component foo="go(value)">

вы оцениваете выражение "go (value)" на родительском область, которая будет в основном вызывать $parent.go ($родитель.значение)"

<my-component foo="go(myVal)">

вы оцениваете выражение " go(myVal)", но прежде чем выражение будет оценено, myVal будет заменен на 42, поэтому оцениваемое выражение будет "go (42)".

<my-component foo="myVal + value + go()">

в этом случае $scope.foo ({myVal: 42}) вернет результат:

42 + $parent.value + $parent.go()

по существу, этот шаблон позволяет директиве "вводить" переменные, которые потребитель директивы может необязательно использовать в выражении foo.

вы могли бы сделать это:

<my-component foo="go">

и в директиве:

$scope.foo()(42)

$scope.foo () оценит выражение "go", которое вернет ссылку на $parent.идут функции. Затем он назовет его $ parent.идти(42). Недостатком этого шаблона является то, что вы получите ошибку, если выражение не оценивает функцию.

окончательной причиной для голосования вниз было утверждение, что директивы ng-event используют &. Это не тот случай. Ни одна из встроенных директив не создает изолированные области с:

scope:{
}

реализация " &foo "(упрощена для ясности), сводится к:

$scope.foo = function(locals) {
    return $parse(attr.foo)($scope.$parent, locals);
}

реализация ng-click аналогична, но (также упрощена):

link: function(scope, elem, attr) {
    elem.on('click', function(evt) {
        $parse(attr.ngClick)(scope, {
             $event: evt
        }
    });
}

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


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

односторонняя привязка более эффективна и позволяет вызывать функцию с любым параметром и в любом порядке, а также обеспечивает большую видимость в HTML. Например, мы не могли себе представить!--3--> чтобы быть двусторонней привязкой: иногда вы хотите что-то вроде <div ng-click="doStuff(var1)"> до более сложных вещей, таких как

<div ng-click="doStuff('hardcoded', var1+4); var2 && doAlso(var2)">

см.: вы можете управлять параметрами непосредственно из HTML.

теперь я чувствую, что вы поняли, как использовать одностороннюю привязку. Если вы действительно определяете onFoo: '&' в вашей директиве, то из функции link Вы должны сделать, например:

// assume bar is already in your scope
scope.bar = "yo";
// Now you can call foo like this
scope.onFoo( {extra: 42} );

так что в вашем HTML вы можете использовать

<div on-foo="doSomething(bar, extra)">

обратите внимание, что у вас есть доступ не только ко всем свойствам изолированной области директивы (bar), но и дополнительные "местные жители" добавлено в момент вызова (extra).

ваша запись scope.foo()(value) выглядит как хак для меня, это не itended способ использовать односторонние привязки.

Примечание: односторонние привязки обычно используются с некоторыми функциями "события", такими как when-drop, on-leave, ng-click, when-it-is-loaded, etc.