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

时间:2018-08-03 11:18:41

标签: angularjs angularjs-directive angularjs-components

如何从控制器AngularJS中的指令监视变量?

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

在我的指令中,我正在侦听滚动,如果可见则返回true,否则返回false。

我希望能够访问控制器中的boolChangeClass变量,以便仅在函数为true时才能调用我的函数。

我的问题是如何在控制器中访问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();
        });
    };
  })

1 个答案:

答案 0 :(得分:1)

tl; dr;

要从使用scope:false的指令向父控制器发送变量:

<div on-scroll="$ctrl.fn($event)"></div>
scope.$apply( () => {
    scope.$eval(attrs.onScroll, {$event: event});
});

答案

不是使用ng-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和添加观察程序的情况。


更新

  

我应该在控制器中做什么?检查类是否存在,然后调用he方法?

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

<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});
      });
    });
  };
})

在每个滚动事件上,指令将使用$ event作为本地变量,将on-scroll属性作为AngularJS表达式求值。


更新#2

  

但是,如果我想在滚动中调用控制器中的方法,但仅在变量为true时调用一次,该怎么办?我尝试使用一次性绑定,但实际上并没有用。

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

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

通过这种方式,指令更具通用性,并且测试更加容易。