tl;dr;
To send variable to parent controller from a directive that uses scope:false:
<div on-scroll="$ctrl.fn($event)"></div>
scope.$apply( () => { scope.$eval(attrs.onScroll, {$event: event}); });
Answer
Instead of setting the class with ng-class, have the directive set the class:
<div class="component" inview-class="min" ̶n̶g̶-̶c̶l̶a̶s̶s̶=̶"̶{̶m̶i̶n̶:̶ ̶b̶o̶o̶l̶C̶h̶a̶n̶g̶e̶C̶l̶a̶s̶s̶}̶" > </div>
app.directive('inviewClass', ($window) => { return function(scope, elem, attrs) { angular.element($window).on("scroll", function(event) { let rect = elem[0].getBoundingClientRect(); let boolChangeClass = rect.bottom > 0 && rect.right > 0 && rect.left < (window.innerWidth || document.documentElement.clientWidth) && rect.top < (window.innerHeight || document.documentElement.clientHeight); let className = attrs.inviewClass; boolChangeClass ? elem.addClass(className) : elem.removeClass(className); }); }; })
This avoids involving the AngularJS digest cycle, modifying $scope, and adding a watcher.
Update
And what should I do in my controller? Check if class exists and then call he method?
Use an attribute to specify the function to be called on a scroll event:
<div class="component" inview-class="min" on-scroll="$ctrl.fn($event)" > </div>
app.directive('inviewClass', ($window) => { return function(scope, elem, attrs) { angular.element($window).on("scroll", function(event) { let rect = elem[0].getBoundingClientRect(); let boolChangeClass = rect.bottom > 0 && rect.right > 0 && rect.left < (window.innerWidth || document.documentElement.clientWidth) && rect.top < (window.innerHeight || document.documentElement.clientHeight); let className = attrs.inviewClass; boolChangeClass ? elem.addClass(className) : elem.removeClass(className); // event.inview = {}; event.inview[className] = boolChangeClass; scope.$apply( () => { scope.$eval(attrs.onScroll, {$event: event}); }); }); }; })
On each scroll event, the directive will evaluate the on-scroll attribute as an AngularJS expression using $event as a local.
Update #2
But what if I want to call method in controller on scroll but only once when my variable is true? I tried to use one-time binding but it doesn't really work.
It would be wiser to have the controller filter the events:
<div class="component" inview-class="min" on-scroll="$ctrl.fn($event)" > </div>
var minOnce; this.fn = (event) => { if (minOnce || !event.inview.min) return; //ELSE minOnce = true; // // ... };
This way the directive is more versatile and testing is easier.