feat(ngShow,ngHide) bind to scope event expression evaluation #7531
Description
Request Type: feature, performance
Component(s): ngShow,ngHide and others
Impact: small
Complexity: small
Detailed Description:
In a simple use case such as handling user authorization control over certain elements in a page, menu and partials, we might use ngShow and ngHide to bind to a function or a variable to control the visibility based on the current users permissions.
The matter of fact is that the expression in ngShow or ngHide will evaluate countless times even if it is not going to change except when the user logs out or changes role at runtime.
<div ng-show="checkPermission('readContent')">
<!-- content here -->
</div>
One solution is to use angular-once, and it is great, but for this example the user might change roles without requiring to refresh the app.
I'm sure there are many other examples where it is unnecessary to watch an expression (especially if it is a function call) and where we know exactly when it is going to need a re-evaluation.
One such solution would be for the expression to be evaluated only on a certain $scope event that the proper service would dispatch when needed (logout, login, changeuser).
This would require to change ngShow, ngHide directive implementation by extending their functionality and keeping backward compatibility.
so changing the above example in:
<div ng-show="checkPermission('readContent')" ng-show-on="scope_event_name">
<!-- content here -->
</div>
by adding the optional attribute ng-show-on or ng-hide-on we instruct the ng-show directive to listen to the $scope for 'scope_event_name' and evaluate only when it triggers instead of creating a watch.
This would eliminate needless watch expressions boosting the performance and provide a useful simple way of dealing targetet evaluation of expressions only when needed.
The quasi-pseudo-code for the ng-show directive would then be:
var ngShowDirective = ['$animate', function($animate) {
return function(scope, element, attr) {
if(!attr.ngShowOn){
scope.$watch(attr.ngShow, function ngShowWatchAction(value){
$animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide');
});
} else {
scope.$on(attr.ngShowOn,function(event,obj){
var val=scope.$eval(attr.ngShow);
$animate[toBoolean(val) ? 'removeClass' : 'addClass'](element, 'ng-hide');
});
}
};
}];
Of course the same principle could be extended to other directives as needed.
in the above example i opted for the extra attribute to keep things simple, but we could extend the syntax of the ng-show/ng-hide/etc to include an optional prefix wich would instruct the directive to not watch the expression but bind it to the event.
While we are at it, it could be unified with this feature request
#7486
and use eventname::expression or ::expression to bind to an event or just bind once.