I have a plain angular application which adds variables to the scope and simply display them. Which is simple and working.
But there is a part of the page that does not run in angular (practically it is a jQuery plugin), so I have to use $compile
to enable directive usage in the rendered HTML. However, in this HTML, the ng-if
directive does not work and always evaluates to false and disappeared. But when using ng-show
, things are displayed/hidden as expected.
What could be the reason and how is a fix possible?
Plunker: https://plnkr.co/edit/kWp4CaED9ZFaOmY36ixT?p=preview
<div ng-controller="GreeterController">
<strong>$scope</strong><br>
constTrue: {{constTrue}}<br>
ng-if="constTrue": <span ng-if="constTrue">Yes</span><br>
ng-show="constTrue": <span ng-show="constTrue">Yes</span><br>
returnTrue(): {{returnTrue()}}<br>
ng-if="returnTrue()": <span ng-if="returnTrue()">Yes</span><br>
ng-show="returnTrue()": <span ng-show="returnTrue()">Yes</span><br>
</div>
<hr>
<div id="outsideController">
<strong>$compile</strong><br>
constTrue: {{constTrue}}<br>
ng-if="constTrue": <span ng-if="constTrue">Yes</span><br>
ng-show="constTrue": <span ng-show="constTrue">Yes</span><br>
returnTrue(): {{returnTrue()}}<br>
ng-if="returnTrue()": <span ng-if="returnTrue()">Yes</span><br>
ng-show="returnTrue()": <span ng-show="returnTrue()">Yes</span><br>
</div>
app.controller('GreeterController', ['$scope', '$compile', '$timeout', function($scope, $compile, $timeout) {
$scope.constTrue = true;
$scope.returnTrue = function(){ return true }
var outsideController = document.getElementById('outsideController');
$compile(outsideController)($scope);
}]);
$scope
constTrue: true ng-if="constTrue": Yes ng-show="constTrue": Yes returnTrue(): true ng-if="returnTrue()": Yes ng-show="returnTrue()": Yes
$compile
constTrue: true ng-if="constTrue": ng-show="constTrue": Yes returnTrue(): true ng-if="returnTrue()": ng-show="returnTrue()": Yes
The problem occurs because the element is being compiled twice. Once when the app bootstraps and again with the $compile
service in the controller.
The solution is to declare to the AngularJS framework that the element not be compiled when bootstrapping.
Use the ng-non-bindable
directive:
<div ng-non-bindable>
<div id="outsideController">
<strong>$compile</strong><br>
constTrue: {{constTrue}}<br>
ng-if="constTrue": <span ng-if="constTrue">Yes</span><br>
ng-show="constTrue": <span ng-show="constTrue">Yes</span><br>
returnTrue(): {{returnTrue()}}<br>
ng-if="returnTrue()": <span ng-if="returnTrue()">Yes</span><br>
ng-show="returnTrue()": <span ng-show="returnTrue()">Yes</span><br>
</div>
</div>
Then when the controller compiles the element is will be clean and unmodified.
The DEMO on PLNKR
For more information, see