We have two components, <hello></hello>
and <goodbye></goodbye>
. Both components have transclusion, allowing them to be used in manners such as <hello>World</hello>
or <goodbye>World</goodbye>
.
angular
.module('myApp', [])
.component('hello', {
template: '<h1>Hello, <ng-transclude></ng-transclude>!</h1>',
transclude: true
})
.component('goodbye', {
template: '<h1>Goodbye, <ng-transclude></ng-transclude>!</h1>',
transclude: true
});
Now, we want the ability to switch between using the <hello></hello>
component or the <goodbye></goodbye>
. One way that this can be done is using ng-if
. (Fiddle)
script.js
...
.controller('MyController', ['$scope', function($scope) {
$scope.component = 'hello';
}]);
index.html
<div ng-controller="MyController">
<hello ng-if="component === 'hello'">World</hello>
<goodbye ng-if="component === 'goodbye'">World</goodbye>
</div>
However, what if our transclusion included significantly more lines? Instead of simply <hello>World</hello>
, we may have <hello><!-- Many lines which we'd rather not repeat twice --></hello>
. If we use the same method to do this, we would end up with a lot of repeated code. So it would be nice if we can simply "switch out" components. (Fiddle)
<div ng-controller="MyController">
<hello ng-if="component === 'hello'">
<goodbye ng-if="component === 'goodbye'">
Lorem Ipsum.........
</goodbye>
</hello>
</div>
Unfortunately, this doesn't work as intended. Setting $scope.component = 'hello'
would yield Hello, !
, and setting $scope.component = 'goodbye'
would yield a blank page. My interpretation of this behavior is that angularjs is parsing this as <goodbye></goodbye>
nested within <hello></hello>
, rather than switching between using <hello></hello>
or <goodbye></goodbye>
. The desired behavior is more along the lines of an if-else if statement.
I have also tried using ng-switch on
. (Fiddle)
<div ng-controller="MyController">
<div ng-switch on="component">
<hello ng-switch-when="hello">
<goodbye ng-switch-when="goodbye">
Lorem Ipsum.........
</goodbye>
</hello>
</div>
</div>
However, this yields a Multiple Directive Resource Contention error.
Error: $compile:multidir
Multiple Directive Resource Contention
Multiple directives [ngSwitchWhen, hello] asking for transclusion on: <hello ng-switch-when="hello">
From the question Using ng-transclude inside ng-switch, the Github Issue claims to have fixed a similar bug. However, the fix only applies when the ng-transclude
is nested within a <div></div>
block as follows:
<div ng-controller="MyController">
<div ng-switch on="component">
<div ng-switch-when="hello">
<hello>Lorem Ipsum.........</hello>
</div>
<div ng-switch-when="goodbye">
<goodbye>Lorem Ipsum.........</goodbye>
</div>
</div>
</div>
Unfortunately, this will not solve the issue of repeated code that I described earlier.
Is there a way to switch out components while keeping the transcluded code the same, but without having to rewrite the transcluded code multiple times?
If not, what alternate methods can I use to achieve my goal while keeping the amount of repeated code to a minimal?
One work around to this problem is to make the <!-- Many lines which we'd rather not repeat twice -->
into another component. (Fiddle)
.component('common', {
template: 'Lorem Ipsum.........'
})
Now, the only repeated code would be the tag for the component.
<div ng-controller="MyController">
<hello ng-if="component === 'hello'"><common></common></hello>
<goodbye ng-if="component === 'goodbye'"><common></common></goodbye>
</div>
If an additional component is undesirable, it is also possible to do the same with ng-include
.
<div ng-controller="MyController">
<hello ng-if="component === 'hello'"><ng-include src="'common.html'"></ng-include></hello>
<goodbye ng-if="component === 'goodbye'"><ng-include src="'common.html'"></ng-include></goodbye>
</div>