Why are my component bindings undefined in its controller?

51,838

Solution 1

When using angular's components, there is a point where the controller hasn't been wired up via the internal linking. If you're trying to do this in the constructor of your controller, you haven't been linked to the bindings. The Component API exposes a few life-cycle hooks that you can define that will fire at certain times. You're looking for the $onInit hook.

$onInit() - Called on each controller after all the controllers on an element have been constructed and had their bindings initialized (and before the pre & post linking functions for the directives on this element). This is a good place to put initialization code for your controller.

per docs - https://docs.angularjs.org/guide/component

Solution 2

Make sure you use hyphens for bindings in HTML and camelCase for bindings in Javascript.

app.component("test", {
  bindings: {
    "myContactId": "<"
  }
}

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

That's what I always forget to do.

Solution 3

The value for contactId is available on the $scope in your controller:

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

Link to another version of your Plunker here.

Solution 4

The keyword this doesn't seem to works with arrow function, this works with

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

When using arrow function, this, seems to refer to the window object because

An arrow function does not create it's own this context, rather it captures the this value of the enclosing context

Solution 5

i will suggest some changes which you would really need to avoid these unusual bugs.

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>

some points :

  1. passing bindings to a component in html is always going to be kebab cased ex my-contact-id and its respective javascript variable will be cammal cased : myContactId.

  2. if you are passing the value insted of the object use '@' in bindings. if you are using an object and passing the object to bindigs use '<. if you want 2-way-binding to that object use '=' in the bindings config

 bindings:{
      value:'@',
      object:'<', // also known as one-way
      twoWay:'='
    }
Share:
51,838
fikkatra
Author by

fikkatra

Software engineer with a passion for Front End and all things Web

Updated on July 08, 2020

Comments

  • fikkatra
    fikkatra almost 4 years

    I'm writing a simple angular component. I'm passing a parameter as a binding and display its value on the screen. All works fine: I can see the parameter being displayed on the screen.

    Component:

    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>
    

    However, when I try to access the binding from within the controller (see the console.log), the binding value is undefined. I don't understand how it can be available in the view, but not in the controller.

    What am I doing wrong?

    Here's a plnkr illustrating the problem.

  • Olivier Boissé
    Olivier Boissé almost 8 years
    why can't we use this keyword when we use the arrow function style ?
  • User 1058612
    User 1058612 almost 8 years
    You can use this with the arrow function style, but I think in this situation, this.contactId is not available yet, for reasons that @jusopi mentioned. If you had API methods, such as someController.prototype.someMethod, this should have what you expect. As @jusopi mentioned, the execution order won't populate this in the constructor (non-prototype) as expected.
  • Olivier Boissé
    Olivier Boissé almost 8 years
    I tried with a timeout and it doesn't work. As I said above, An arrow function does not create it's own this context, rather it captures the this value of the enclosing context. So with arrow function this refers to the window object, not the controller.
  • User 1058612
    User 1058612 almost 8 years
    Interesting. I wasn't aware of that - thanks! In this situation, regardless of function syntax, $scope injection will solve the problem the the OP asked.
  • escapedcat
    escapedcat over 7 years
    I think you are right, $onInit should be used. This works for me: ` 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)); }; `
  • aUXcoder
    aUXcoder almost 7 years
    In some scenarios your bindings can be updated later and its values are not ready when the hook $onInit is call. In such cases you should use $onChanges and skip the first changes. Working example: jsfiddle.net/auxcoder/4hq5gaq0
  • Xchai
    Xchai over 6 years
    @aUXcoder That was my issue. I tried to create the component before the data to bind to component was ready. Putting an ng-if directive to ensure I had the data before the component was initialized (eg <custom-component ng-if="!$ctrl.data" data="$ctrl.data"></custom-component>) fixed the issue.
  • Buksy
    Buksy over 6 years
    "... after all the controllers on an element have been constructed", does this mean that one component can have multiple controllers with a single use of component tag? Is there an example?
  • jusopi
    jusopi over 6 years
    @Buksy I think they mean that for a given HTML element, that may or may not correspond to a component, there can be more than one directive on it. For instance <custom-component ng-model="" nx-save=""></>. In this case, you have a single element, with a component controller, an ngModelController and some other directive that could have a controller.
  • jfroy
    jfroy about 6 years
    For reference, and a few special cases where this could bite at you: docs.angularjs.org/guide/directive#normalization
  • Voltan
    Voltan about 3 years
    User 1058612 - thanks for the example, learned a lot. However, tried it exactly (hopefully) as you laid it out - not working for me. Any chance that 1.7.9 is the problem - there were changes to this version, but I'm new to this. Any help will do - thank you