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

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

.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) из моей функции ссылок. Так ли это допустимый прецедент для использования двухсторонней привязки к функциям, или я делаю какой-то хак, который я не должен делать?

Ответ 1

Вот почему я отказался от ответа.

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

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

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

Согласно документам:

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

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

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

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

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

foo({myVal: 42});

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

В качестве альтернативы,

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

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

<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.go. Затем он будет называться $parent.go(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
        }
    });
}

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

Ответ 2

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

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

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

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

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

// 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), выглядит как взломать меня, то есть не является переработанным способом использования односторонних привязок.

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