【问题标题】:How to access component methods from parent controller in Angular1.5如何在 Angular 1.5 中从父控制器访问组件方法
【发布时间】:2017-01-25 18:16:09
【问题描述】:

我有子组件 MyStats,它以列表作为输入数据并显示有关该列表的统计信息。当 myData 中的列表更新时,我需要在我的组件中调用 rebuildStats 函数。由于组件中没有 $scope,我无法添加 $watch。有没有办法从 MainCtrl 调用rebuildStats?最好的解决方法是什么?

module.controller('MainCtrl', ['$scope', '$http', function ($scope, $http) {
  $scope.statData = {};
  $http.get('..some url..').then(function(resp) {
    $scope.statData.list = resp.list;
  });
}]);

module.component('myStats', {
    'templateUrl': '...',
    'bindings': {
        'myData': '<ngModel'
    },
    'controller': function() {
      this.totalActive = 0;
      this.rebuildStats = function() {
        var cntr = 0;
        angular.forEach(this.myData.list, function (item) {
          if (item.isActive) {
            cntr++;
          }
        });
        this.totalActive = cntr;
      };

    }
});

<my-stats ng-model="statData"></my-stats>

编辑:我现在使用两种解决方案: 1)在数据块中,我更改为 2-way binding 并引入了从组件内部创建的 setter 函数,然后父级可以调用此函数。它不使用 $scope 但仍然......不确定这是否是 Angular 创建者的意图。

module.controller('MainCtrl', ['$scope', '$http', function ($scope, $http) {
  $scope.statData = {};
  $http.get('..some url..').then(function(resp) {
    $scope.statData.setList(resp.list);
  });
}]);

module.component('myStats', {
    'templateUrl': '...',
    'bindings': {
        'myData': '=ngModel'
    },
    'controller': function() {
      var ctrl = this;

      this.totalActive = 0;
      this.rebuildStats = function() {
        var cntr = 0;
        angular.forEach(ctrl.myData.list, function (item) {
          if (item.isActive) {
            cntr++;
          }
        });
        ctrl.totalActive = cntr;
      };
      this.myData.setList = function (list) {
        ctrl.myData.list = list;
        ctrl.rebuildStats();
      };
    }
});

第二种方法是通过元素访问控制器。像这样:

    var getController = function(elementSelector, controllerName) {
      var el = angular.element(document.querySelector(elementSelector));
      if (el) {
        return el.controller(controllerName);
      }
      return null;
    };

所以现在如果我有带有标签“my-test-controller”并且 id="id1" 的带有 MyTestController 的控制器,那么我可以像这样访问它:

var ctrl = getController('my-test-controller#id1', 'MyTestController');
ctrl.MyTestControllerMethod(); // access any exposed method

【问题讨论】:

    标签: angularjs components


    【解决方案1】:

    如果认为使用 ES2015 getter/setter 语法将是最好的解决方案。

    class MyStats{
        get myData() {
            return this.$value;
        }
        set myData(value) {
           this.$value = value;
           this.rebuildStats();
        }
    
        ...
    }
    
    
    module.component('myStats', {
        'templateUrl': '...',
        'bindings': {
            'myData': '<ngModel'
        },
        'controller': MyStats
    });
    

    或者只是使用 $inject 语法将 $scope 注入到您的控制器函数中:

    MyStatsController.$inject = ['$scope'];
    function MyStatsController($scope) {
      ...
    }
    

    【讨论】:

    • 我必须在旧的 ES5 领域进行操作。我可以 $inject 但这听起来很骇人(因为从组件中取出 $scope 是有原因的)。我觉得很奇怪,我可以使用像 'onUpdate': '&' 这样的函数绑定从组件到父级进行通信,但是没有明显的方法(如果甚至存在的话)从父级调用到组件。
    【解决方案2】:

    将经典控制器+范围与“现代”组件和单向数据绑定混合在一起是很奇怪的。

    首先想到的是您需要您的组件来调用服务本身。您不需要外部控制器和绑定。

    var vm = this;
    this.$onInit = function() {
      $http.get(...).then(function() { vm.rebuildStats(); })
    }
    

    更好的是,将该逻辑放入服务中并在 $onInit 中调用该服务。 当 Promise 解决并且您的数据准备就绪时,调用您的函数。

    这样您就拥有了一个完全独立的组件,并且您的代码变得更轻巧更好。

    如果您真的需要在属性中传递数据,可以使用 $onChanges 来检查模型更改:

    var vm = this;
    this.$onChanges = function (changes) {
      if (changes.myData) {
        vm.myData= angular.copy(changes.myData.currentValue);
        vm.rebuilsStats();
      }
    };
    

    【讨论】:

    • 该组件将用于来自不同控制器的数据分析:用户评论、cmets 和书评的统计数据。由于它非常通用,我无法将其绑定到特定的数据源。所以在这方面数据必须作为参数传递。我尝试了 $onChanges 但它仅在组件创建期间调用,而不是在我从 $http 更新列表时调用
    • 不,不对,是绑定数据变化时调用。 toddmotto.com/angular-1-5-lifecycle-hooks#onchanges
    【解决方案3】:

    我遇到了类似的问题,这样的事情对我有用:

    var scope = $('my-stats').children().scope();
    scope.$ctrl.rebuildStats();
    

    $('my-stats').scope() 是组件的“外部”范围,您必须调用 children() 才能进入“内部”(前提是它有任何子级)。

    我认为这不是正确的方法。文档说scope() 仅用于调试。

    【讨论】:

      猜你喜欢
      • 2016-12-15
      • 2017-04-18
      • 1970-01-01
      • 2016-10-31
      • 2015-06-28
      • 2018-01-16
      • 2018-02-27
      • 2014-04-21
      • 2017-05-09
      相关资源
      最近更新 更多