【问题标题】:How to pass controller data to a custom directive in AngularJS?如何将控制器数据传递给 AngularJS 中的自定义指令?
【发布时间】:2018-07-26 20:23:59
【问题描述】:

我在 AngularJS 中有一个自定义指令,我想从我的控制器向它传递一个变量。

控制器:

angular.
    module('orderOverview').
    component('orderOverview', {
        templateUrl: 'home-page/order-overview/order-overview.template.html',
        controller: ['Orders',
            function ControllerFunction(Orders) {
        var self = this;

        // Order Info
        Orders.getOverview().$promise.then(function(data) {
          self.LineItems = data;
        });
        // Order Info
      }
    ]
  });

指令

angular.
    module('myApp').
    directive('gvInitializeOrderStatus', function() {
    return {
      scope: {
        field: '@',
        myData: '='
      },
      link: function(scope, element) {
        console.log('field:', scope.field);
        console.log('data:', scope.myData);
      }
    }
    });

HTML

<div gv-initialize-order-status field="InquiryDone" myData="$ctrl.LineItems">
    <span class="tooltiptext">Inquiry</span>
</div>

当我加载页面时,field 记录正常,但 data 未定义。

我已经尝试了很多方法,但如果它能让你知道我在想什么,这就是它应该在我脑海中发挥作用的方式。

在同一模板的另一点,我将 ng-repeat 数据传递给指令就好了,但在这种情况下,我特别不想 ng-repeat

ng-repeat 成功传递数据的 HTML

<li ng-repeat="lineItem in $ctrl.LineItems">
    <div class="status-circle" 
         ng-click="toggleCircle($event, lineItem, 'InquiryDone')"
         field="InquiryDone" item="lineItem" gv-initialize-statuses>
        <span class="tooltiptext">Inquiry</span>
    </div>
</li>

在我的另一个指令gv-initialize-statuses 中,我在我的scope 对象中使用了相同的概念,并且有类似scope: { 'field': '=' } 的东西,它工作得很好。

如何在不使用 ng-repeat 的情况下完成此操作?

【问题讨论】:

    标签: javascript angularjs angularjs-directive


    【解决方案1】:

    应避免使用= 进行双向绑定

    该指令需要在链接函数中使用$watch:

    app.directive('gvInitializeOrderStatus', function() {
        return {
          scope: {
            field: '@',
            ̶m̶y̶D̶a̶t̶a̶:̶ ̶'̶=̶'̶
            myData: '<'
          },
          link: function(scope, element) {
            console.log('field:', scope.field);
            console.log('data:', scope.myData);
            scope.$watch('myData', function(value) {
                console.log('data:', scope.myData);
            });
          }
        }
    });
    

    ng-repeat 等指令会自动使用观察者。

    同样出于性能原因,应避免使用= 进行双向绑定。单向绑定&lt; 效率更高。


    如需更高效的代码,请在控制器中使用$onChanges life-cycle hook

    app.directive('gvInitializeOrderStatus', function() {
        return {
          scope: {
            field: '@',
            ̶m̶y̶D̶a̶t̶a̶:̶ ̶'̶=̶'̶
            myData: '<'
          },
          bindToController: true,
          controllerAs: "$ctrl",
          controller: function() {
            console.log('field:', this.field);
            console.log('data:', this.myData);
            this.$onChanges = function(changes) {
                if (changes.myData)
                    console.log('data:', changes.myData.currentValue);
                };
            });
          }
        }
    });
    

    这样做将使代码更高效,并且更容易迁移到 Angular 2+。


    手表有不同的等级:

    ng-repeat 指令实际上使用了$watchCollection

    指令可能需要使用$doCheck Life-Cycle hook

    有关详细信息,请参阅

    【讨论】:

    • 我喜欢关于单向绑定的提示,但这实际上不是一个承诺问题。我在玩,发现没有 ng-repeat 我什至无法传递诸如self.testVar = 400 之类的变量。我尝试了这两种解决方案,但 myData 仍然未定义。
    • 下一步是创建一个PLNKR 来重现问题。
    • 谢谢,我会设置提醒,稍后再试,以供学习。但是,我找到了一个我刚刚提出的可行解决方案,我现在必须继续。
    【解决方案2】:

    如果您只想要指令中的数据,请执行此操作

    Orders.getOverview().$promise.then(function(data) {
              self.LineItems = data;
              $rootScope.$broadcast('myData', data);
            });
    

    在您的指令中,只需使用回调函数捕获此事件

    $scope.$on('myData', function(event, args) {
    
        var anyThing = args;
        // do what you want to do
    });
    

    【讨论】:

      【解决方案3】:

      问题是$promise

      self.LineItems 在指令激活时尚未准备好。这就是为什么数据是undefined

      也许ng-if 可以帮助你:

      <div ng-if="$ctrl.LineItems" gv-initialize-order-status field="InquiryDone" myData="$ctrl.LineItems">
          <span class="tooltiptext">Inquiry</span>
      </div>
      

      希望这会有所帮助。祝你好运!

      【讨论】:

        【解决方案4】:

        因此,当我在文档中阅读有关 $compile 的内容时,我找到了一个有效的答案。我意识到你可以得到内插的属性值,所以我从范围对象中删除了myData 字段,而是通过attrs 对象访问该值,就像这样。

        指令

        angular.
            module('myApp').
            directive('gvInitializeOrderStatus', function() {
            return {
              scope: {
                field: '@'
              },
              link: function(scope, element, attrs) {
                console.log('field:', scope.field);
                console.log('attrs:', attrs);
                attrs.$observe('lineItems', function(value) {
                  console.log(value);
                })
              }
            }
            });
        

        HTML

        <div gv-initialize-order-status field="InquiryDone" lineItems="{{$ctrl.LineItems}}">
             <span class="tooltiptext">Inquiry</span>
        </div>
        

        注意将{{ }} 添加到 lineItems 属性。 attrs.$observe 块也让我可以收到值更改的通知。

        【讨论】:

        • 如果这对你有用,那么完全摆脱隔离作用域并使用attrs.field 作为field 属性。然后你有一个属性指令,可以很好地与其他属性指令配合使用。
        【解决方案5】:

        在指令中

        angular.
        module('myApp').
        directive('gvInitializeOrderStatus', function() {
        return {
          scope: {
            field: '@',
            ngModel: '='     // <- access data with this (ngModel to ng-model in view)
          },
          link: function(scope, element) {
            console.log('field:', scope.field);
            console.log('data:', scope.ngModel);
          }
        }
        });
        

        在视图中

        <div gv-initialize-order-status field="InquiryDone" ng-model="$ctrl.LineItems">
          <span class="tooltiptext">Inquiry</span>
        </div>
        

        【讨论】:

        • 顺便说一句可能重复的副本:this link
        • ng-model 属性实例化了ngModelController。除非您打算将 &lt;div&gt; 元素转换为表单控件,否则应避免使用该名称。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-01
        • 1970-01-01
        相关资源
        最近更新 更多