如何在没有隔离范围的情况下将变量从指令发送到父控制器

How to Send Variable to Parent Controller from Directive without Isolate Scope

如何在控制器中查看来自指令的变量AngularJS?

我有一个带有控制器和指令的组件

在我的指令中,我正在侦听滚动,如果元素在视图中则返回 true,否则返回 false。

我希望能够在控制器中访问我的 boolChangeClass 变量,以便只有当它为真时我才能调用我的函数。

我的问题是如何在控制器中访问 boolChangeClass

    'use strict';
    
    angular.module('myModule')
      .component('myComponent', {
        template: `
          <div class="component" inview ng-class="{min: boolChangeClass}">
          </div>
        `,
    
        controller: function ($scope) {
          let $ctrl = this;

          let func = () => {console.log('my function')}

      $scope.$watch('boolChangeClass', func);
    }
  })
  .directive('inview', ($window) => {
    return function(scope, element, attrs) {
        angular.element($window).bind("scroll", function() {
          let rect = element[0].getBoundingClientRect();
          scope.boolChangeClass =  rect.bottom > 0 &&
              rect.right > 0 &&
              rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
              rect.top < (window.innerHeight || document.documentElement.clientHeight)
          scope.$apply();
        });
    };
  })

tl;博士;

从使用 scope:false:

的指令向父控制器发送变量
<div on-scroll="$ctrl.fn($event)"></div>
scope.$apply( () => {
    scope.$eval(attrs.onScroll, {$event: event});
});

回答

不是用 ng-class 设置 class,而是让指令设置 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);
    });
  };
})

这避免了涉及 AngularJS 摘要循环、修改 $scope 和添加观察者。


更新

And what should I do in my controller? Check if class exists and then call he method?

使用属性指定在滚动事件上调用的函数:

<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-scroll 属性评估为一个 AngularJS 表达式,使用 $event 作为本地表达式。


更新 #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.

让控制器过滤事件会更明智:

<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;
    //
    // ...
};

这样指令更通用,测试也更容易。