Reputation: 549
We have the following directive:
.directive("directiveToggleElement", function(FlagsService) {
return {
restrict: "E",
scope: {},
link: function(scope, element, attrs)
{
scope.showMe = FlagsService.isOn(attrs.toggleKey);
scope.featureText = attrs.featureText;
var htmlTag = '<div class="panel ng-scope" ng-class="{selected: selected}" ng-click="selected = !selected;"></div>';
var htmlComment = '<!-- TogglePlaceholder: ' + attrs.toggleKey +'-->';
element.replaceWith(scope.showMe ? htmlComment + htmlTag : htmlComment);
// Listen for change broadcast from flagsservice to toggle local store
scope.$on('LocalStorage.ToggleChangeEvent.' + attrs.toggleKey, function(event, parameters) {
console.log('stuff changed - ' + parameters.key + ' ' + parameters.newValue);
scope.showMe = parameters.newValue;
element.replaceWith(scope.showMe ? htmlComment + htmlTag : htmlComment);
});
}
}
})
The idea is that based on the value of a feature toggle (or feature flag) the directive will output the comment with the tag, or just the comment.
The initial element.replaceWith works as expected, but the call from inside the scope.$on generates a "TypeError: Cannot call method 'replaceChild' of null". We've inspected element before each call and can't spot an obvious difference.
Can anyone explain why the error would be thrown here, or advise of a potential workaround that will allow us to do the same thing?
We have a working directive that sets the value of ng-show:
.directive("directiveToggle", function(FlagsService) {
return {
restrict: "A",
transclude: true,
scope: {},
link: function(scope, element, attrs)
{
scope.showMe = FlagsService.isOn(attrs.toggleKey);
// Listen for change broadcast from flagsservice to toggle local store
scope.$on('LocalStorage.ToggleChangeEvent.' + attrs.toggleKey, function(event, parameters) {
console.log('stuff changed - ' + parameters.key + ' ' + parameters.newValue);
scope.showMe = parameters.newValue;
});
},
template: '<div class="panel ng-scope" ng-show="showMe" ng-class="{selected: selected}" ng-click="selected = !selected;"><span ng-transclude></span></div>',
replace: true
}
})
But we would prefer to remove the element from the DOM instead of setting display to none.
Upvotes: 2
Views: 613
Reputation: 300
You could still use the ng-if directive within your custom "directiveToggle" without the need for a controller. To achieve this you need to use the transclude option:
.directive("directiveToggle", function (FlagsService) {
return {
template: '<div ng-transclude ng-if="isEnabled"></div>',
transclude: true,
restrict: 'A',
scope: true,
link: function ($scope, $element, $attrs) {
var feature = $attrs.featureToggle;
$scope.isEnabled = FlagsService.isOn(feature);
$scope.$on('LocalStorage.ToggleChangeEvent.' + feature, function (event, parameters) {
$scope.isEnabled = parameters.newValue;
});
}
}
});
The above is taking the mark-up from inside the "directiveToggle" and then wrapping it inside of the template, where the "ng-transclude" directive marks the insertion point. Also included on the template is an "ng-if" directive that is watching the "isEnabled" member on the current scope for the "directiveTemplate".
One caveat with this approach is that you need the directive to create it's own scope/isolated scope if you have multiple instances of this directive, otherwise the "isEnabled" member will be shared between the directives.
Upvotes: 1