Reputation: 31580
I'm trying to inject 2 templates into an element and operate on them:
<div
ic-first="foo"
ic-second="bar"
ic-third="baz"
ic-fourth="qux"
>
</div>
icFirst should inject via a template an empty div as a child of its element. icSecond should inject a second div (with a bunch of content) as the second child of its element, so the resulting html would look like:
<div
ic-first="foo" // priority: 100
ic-second="bar" // priority: 50
ic-third="baz" // priority: 0
ic-fourth="qux" // priority: 0
>
<div id="foo"></div>
<div> <!-- a bunch of stuff from the templateUrl --> </div>
</div>
Both icFirst and icSecond will inject other elements into the newly created containers.
When I specify a directive template property on both directives, I get an error:
Error: Multiple directives [icFirst, icSecond] asking for template on:
<div ic-first
…
When I add transclude: true
to both directives, icFirst executes just fine…but then the other directives on the same element are not executed. When I set transclude: 'element'
, the other directives execute but I get an error that the first child ($scope.firstObj
) is undefined.
All four directives need access to each other's scope, so I'm doing most of my work in their controllers:
app.directive('icFirst', ['ic.config', function (icConfig) {
return {
restrict: 'A',
priority: 100,
template: '<div id="{{firstId}}"></div>',
replace: false,
transclude: 'element',
controller: function icFirst($scope, $element, $attrs) {
// …
$scope.firstId = $scope.opts.fooId;
$scope.firstElm = $element.children()[0];
$scope.firstObj = {}; // this is used by the other 3 directives
},
link: function(scope, elm, attrs) { … } // <- event binding
}
);
app.directive('icSecond', ['ic.config', function (icConfig) {
return {
restrict: 'A',
priority: 0,
templateUrl: 'views/foo.html',
replace: false,
transclude: 'element',
controller: function icSecond($scope, $element, $attrs) {
// …
$scope.secondElm = $element.children()[1];
$scope.secondObj = new Bar( $scope.firstObj );
// ^ is used by the remaining 2 directives & requires obj from icFirst
},
link: function(scope, elm, attrs) { … } // <- event binding
}
);
Note I have corrected the behaviour of replace: false
to match the documented behaviour, as described in pull request #2433.
I tried instantiating $scope.firstObj
in the controller, and setting it in the linkFn (hoping the transclusion would have completed by the time the linkFn executes), but I get the same problem. It appears first-child is actually a comment.
Upvotes: 7
Views: 8532
Reputation: 31580
The only reason I can come up with that explains throwing this error is that the AngularJS team was trying to avoid needless overwrites/DOM manipulation:
Considering the actual behaviour of replace: false
vs the documented behaviour, I think the actual is in fact the intended behaviour. If this is true, then allowing multiple templates/templateUrls to be used on the same element will cause subsequent templates to overwrite previous ones.
Since I already modified the source to match the documented behaviour, as a quick fix†, I modified the source again (/src/ng/compile.js:700) to remove the assertNoDuplicate
check (which corresponds to angular.js:4624
). Now I return the following 2 objects, and it works, and I can't find any negative repercussions:
// directive icFirst
return {
restrict: 'A',
priority: 100,
replace: false,
template: '<div id="{{firstId}}"></div>',
require: ["icFirst"],
controller: Controller,
link: postLink
};
// directive icSecond
return {
restrict: 'A',
require: ['icFirst'],
replace: false,
templateUrl: 'views/bar.html',
priority: 50,
controller: Controller,
link: postLink
};
† If made permanent, the check should probably be
if (directive.templateUrl && directive.replace)
(and similar for directive.template)
Upvotes: 2