【问题标题】:Why are my component bindings undefined in its controller?为什么我的组件绑定在其控制器中未定义?
【发布时间】:2016-11-30 05:00:01
【问题描述】:

我正在编写一个简单的角度组件。我将参数作为绑定传递并在屏幕上显示其值。一切正常:我可以看到屏幕上显示的参数。

组件:

var app = angular.module("test", []);
app.component("test", {
  bindings: {
    "contactId": "<"
  },
  controllerAs: "model",
  controller: () => {
    //output: 'contact id from controller: undefined'
    console.log(`contact id from controller: ${this.contactId}`);
  },
  template: "<div>Contact id from view: {{model.contactId}}</div>"
});

HTML:

<test contact-id="8"></test>

但是,当我尝试从控制器内部访问绑定时(请参阅 console.log),绑定值为 undefined。我不明白它如何在视图中可用,但在控制器中不可用。

我做错了什么?

这是一个plnkr 说明问题。

【问题讨论】:

  • 我缺少一些简单的东西。为什么“contactId”用引号括起来?

标签: javascript angularjs angularjs-1.5


【解决方案1】:

导致“未定义”错误的代码有两个问题。

  1. 如上所述,应首先到达 $onInit 生命周期钩子,当所有绑定都完成后会触发 onInit。

来自官方文档:AngularJs Documentation

$onInit() - 在一个控制器上的所有控制器之后在每个控制器上调用 元素已经被构造并初始化了它们的绑定(和 在此指令的前后链接功能之前 元素)。这是放置初始化代码的好地方 控制器。

  1. 您可能会遇到的第二个问题是,当使用“() =>”箭头符号作为控制器函数的参数时,您的控制器将无法到达生命周期钩子。

问题是箭头符号没有它自己的范围,而是使用它的封闭范围。 这意味着当使用“this”时将引用窗口对象而不是组件。 所以调用 this.$onInit() 会在窗口上被调用,并且不会被触发,因为它在窗口上不存在。

【讨论】:

    【解决方案2】:

    我将添加另一个答案作为@jusopi 和已接受答案的跟进,仅针对那些可能遇到我的问题的人。关于组件,即使在$onInit 钩子之后,我的数据仍然是空的,因为仍然没有收到来自服务器的值。为了解决这个问题(尽管可能有更好的方法来处理这种情况),我还利用了$onChanges 钩子。 $onChanges 将在传递时返回已更改的数据,您可以解析该信息,或者简单地将绑定调用为this.contactId,它将被更新。

    更多详情请见documentation:https://docs.angularjs.org/guide/component

    【讨论】:

      【解决方案3】:

      对于那些使用指令(假设组件)的用户,如果指定了绑定{},则似乎将这些相同的参数添加到范围{} 有效:

      /*bindings: {
          modalInstance: '<',
          resolve: '<'
      },*/
      scope: {
          modalInstance: '<',
          resolve: '<'
      },
      

      *在我写完上面的内容后发现,一个额外的范围参数 foo 在我从 $scope.resolve 分配之前在 $scope 上不可用。所以我不得不在 $scope.init() 中这样做:$scope.foo = $scope.resolve.foo。不知道为什么。猜测它与我的 UI Bootstrap Modal + Directives 用法有关

      这对其他人来说可能很明显,但对我来说不是相对较新的 AnguluarJS。

      我的问题是使用带有 UI-Bootstrap 模式的指令,这些指令与指令兼容,但设计和记录是为了与组件一起使用。

      【讨论】:

        【解决方案4】:

        我会建议一些您真正需要的更改以避免这些不寻常的错误。

        app.component("test", {
          bindings: {
            "myContactId": "<"
          },
          controller:function(){
           var self=this;
           this.$onInit=function(){
            // do all your initializations here.
            // create a local scope object for this component only. always update that scope with bindings. and use that in views also.
        
               self.myScopeObject=self.myContactId
           }
          },
           template:'<p>{{$ctrl.myScopeObject}}</p>'
         }
        
        <test my-contact-id="8"></test>
        

        几点:

        1. 将绑定传递给 html 中的组件始终是 kebab 大小写 ex my-contact-id 并且其各自的 javascript 变量将是 cammal 大小写:myContactId。

        2. 如果您要传递对象的 insted 值,请在绑定中使用“@”。 如果您正在使用一个对象并将该对象传递给 bindigs,请使用 '<.>

         bindings:{
              value:'@',
              object:'<', // also known as one-way
              twoWay:'='
            }
        

        【讨论】:

          【解决方案5】:

          这可能不是最佳做法,但您可以更轻松地访问这些值:

          $scope.$ctrl.contactId
          

          您可以在 $scope 中获取属性 $ctrl 中的所有绑定。

          希望对你有所帮助

          【讨论】:

            【解决方案6】:

            确保在 HTML 中使用 连字符 进行绑定,在 Javascript 中使用 camelCase 进行绑定。

            app.component("test", {
              bindings: {
                "myContactId": "<"
              }
            }
            
            <test my-contact-id="8"></test>
            

            这就是我总是忘记做的事情。

            【讨论】:

            【解决方案7】:

            关键字 this 似乎不适用于箭头功能,this 适用于

            controller: function() {
               alert('contact id from controller: ' + this.contactId);
            }
            

            当使用箭头函数时,this,似乎指的是窗口对象,因为

            箭头函数不会创建它自己的这个上下文,而是它 捕获封闭上下文的 this 值

            【讨论】:

              【解决方案8】:

              contactId 的值在您的控制器中的 $scope 上可用:

              var app = angular.module("test", []);
              app.component("test", {
                bindings: {
                  "contactId": "<"
                },
                controllerAs: "model",
                controller: ($scope) => {
                  var model = $scope.model;
                  alert(`contact id from controller: ${model.contactId}`);
                },
                template: "<div>Contact id from view: {{model.contactId}}</div>"
              });
              

              链接到另一个版本的 Plunker here

              【讨论】:

              • 为什么我们在使用箭头函数样式的时候不能使用这个关键字?
              • 您可以将this 与箭头函数样式一起使用,但我认为在这种情况下,this.contactId 尚不可用,原因是@jusopi 提到的。如果你有 API 方法,例如 someController.prototype.someMethod,this 应该有你所期望的。正如@jusopi 提到的,执行顺序不会像预期的那样在构造函数(非原型)中填充this
              • 我尝试了超时,但它不起作用。正如我上面所说,箭头函数不会创建它自己的 this 上下文,而是捕获封闭上下文的 this 值。所以使用箭头函数 this 指的是窗口对象,而不是控制器。
              • 有趣。我不知道 - 谢谢!在这种情况下,无论函数语法如何,$scope 注入都会解决 OP 提出的问题。
              • 用户 1058612 - 谢谢你的例子,学到了很多。但是,按照您的说明完全(希望)尝试了它 - 不适合我。 1.7.9 是问题的任何机会 - 这个版本有变化,但我是新手。任何帮助都可以 - 谢谢
              【解决方案9】:

              当使用 Angular 的组件时,控制器没有通过内部链接连接起来。如果您尝试在控制器的构造函数中执行此操作,则您尚未链接 到绑定。组件 API 公开了一些您可以定义的生命周期钩子,这些钩子将在特定时间触发。您正在寻找 $onInit 挂钩。

              $onInit() - 在元素上的所有控制器都已构建并初始化其绑定之后(以及在此元素上的指令的前后链接函数之前)在每个控制器上调用。这是放置控制器初始化代码的好地方。

              每个文档 - https://docs.angularjs.org/guide/component

              【讨论】:

              • 我认为你是对的,应该使用$onInit。这对我有用:` var vm = this; vm.$onInit = function() { console.log('loaded on init'); console.log('vm', vm.YOUR_BINDING); console.log('def yes?', angular.isDefined(vm.YOUR_BINDINGj)); }; `
              • 在某些情况下,您的绑定可以稍后更新,并且在调用钩子 $onInit 时它的值还没有准备好。在这种情况下,您应该使用$onChanges 并跳过第一次更改。工作示例:jsfiddle.net/auxcoder/4hq5gaq0
              • @aUXcoder 那是我的问题。我试图在绑定到组件的数据准备好之前创建组件。放置一个ng-if 指令以确保我在组件初始化之前拥有数据(例如&lt;custom-component ng-if="!$ctrl.data" data="$ctrl.data"&gt;&lt;/custom-component&gt;)解决了这个问题。
              • "...在一个元素上的所有控制器都已构建完成后",这是否意味着一个组件可以有多个控制器,只需使用一次组件标签?有例子吗?
              • @Buksy 我认为它们的意思是,对于给定的 HTML 元素,它可能对应于一个组件,也可能不对应于一个组件,它上面可以有多个指令。例如&lt;custom-component ng-model="" nx-save=""&gt;&lt;/&gt;。在这种情况下,您只有一个元素,其中包含一个组件控制器、一个 ngModelController 和一些其他可能具有控制器的指令。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-10-13
              • 2018-06-11
              • 2023-03-21
              • 1970-01-01
              相关资源
              最近更新 更多