При передаче функции в директиву, которая затем передается во вложенную дочернюю директиву, функция всегда считается определенной при проверке в области дочерней директивы, независимо от того, передана она или нет в родительской директиве.
Есть ли лучший способ либо передать указатели на функции, либо проверить, определены ли они при работе с вложенными директивами.
<body ng-app="myApp">
<div ng-controller="myController">
<dir1"></dir1>
</div>
<script type="text/ng-template" id="dir1">
<div>
<dir2 fun="fun()"></dir2>
</div>
</script>
<script type="text/ng-template" id="dir2">
<div>{{fun()}}</div>
<div>{{funDefined()}}</div> <!-- always true-->
</script>
</body>
var app = angular.module('myApp', []);
app.controller('myController', function($scope) {
$scope.fun = function() {
alert("function");
};
});
app.directive('dir1', function() {
return {
scope: {
fun: '&'
},
templateUrl: 'dir1'
};
});
app.directive('dir2', function() {
return {
scope: {
fun: '&'
},
link: function(scope, elem, attrs) {
scope.funDefined = function() {
return angular.isDefined(attrs.fun);
};
},
templateUrl: 'dir2'
};
});
Лучший способ, который я мог найти, основан на том, что @xersiee прокомментировал в другом ответе. Идея состоит в том, чтобы сделать параметр области необязательным в родительской директиве, а затем использовать angular.isUndefined(scope.$parent.$eval(attribute.myFun))
чтобы проверить, была ли эта функция передана или нет. Это не объясняется в официальной документации... Интересно, почему.
Как отмечали другие люди, это решение далека от идеала, потому что использование области. $ Parent - это анти-шаблон, но опять же, это лучший вариант, который я мог бы найти.
Plunker с этим решением: http://plnkr.co/edit/SUUMae?p=preview
Там нет элегантного способа, который я знаю, чтобы получить то, что вы хотите. Как это было упомянуто до этой строки:
angular.isDefined(attrs.fun)
выполняет проверку строки, поэтому возвращает true при каждом атрибуте fun
. И в вашем шаблоне директивы dir1 у вас есть <dir2 fun="fun()"></dir2>
так что, очевидно, будет определено fun
(и это строка). Если вы посмотрите на угловые источники:
case '&':
// Don't assign Object.prototype method to scope
parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
// Don't assign noop to destination if expression is not valid
if (parentGet === noop && optional) break;
destination[scopeName] = function(locals) {
return parentGet(scope, locals);
};
break;
вы увидите, что наличие атрибута всегда приведет к некоторой функции, назначенной области ($parse
возвращает функцию даже для строки, которая не имеет большого смысла).
Поэтому единственным решением, которое я могу придумать, является выполнение проверки в директиве первого уровня (возможно, поскольку атрибут действительно не определен) и имеют два <dir2>
(с атрибутом "без fun
) - каждый всегда исключает использование ng-if
. Что-то вроде этого. Опять же, я знаю, его уродливое решение.
Одностороннее примечание. Угловой источник также показывает, что свойство scope не будет установлено, если нет атрибута и привязки необязательно (с использованием &?
) - тогда вы можете проверить значение scope.fun
вместо attrs.fun
- некоторые могут показаться более элегантными.
Если вы установите отладчик внутри вашего метода scope.funDefined
dir2, вы увидите, что attrs.fun
равно строке "fun()"
. Это потому, что вы берете исходную ценность из атрибутов. И поскольку это не пустая строка, она всегда даст вам true
.
Здесь обновляется плункер