Search code examples
javascriptangularjsangularjs-components

Angular 1.5.x - Issue with nested components


First of all, I'm using components.

I have this "parent" component:

(function() {
  'use strict';

  angular
    .module('parentModule', [])
    .component('parent', {
      templateUrl: 'parent.tpl.html',
      controller: ParentCtrl,
        transclude: true,
        bindings: {
            item: '='
        }
    });

  function ParentCtrl() {
    var vm = this;
    vm.item = {
      'id': 1,
      'name': 'test'
    };
  }
})();

And I'm simply trying to share the object item with another component, like this:

(function() {
  'use strict';

  angular
    .module('childModule', [])
    .component('child', {
      templateUrl: 'child.tpl.html',
      controller: ChildCtrl,
      require: {
        parent: '^item'
      }
    });

  function ChildCtrl() {
    console.log(this.parent)
    var vm = this;

  }
})();

View (Parent):

Parent Component:

<h1 ng-bind='$ctrl.item.name'></h1>
<child></child>

View (Child):

Child component:

Here I want to print the test that is in the parent component
<h2 ng-bind='$ctrl.item.name'></h2>

Actually I'm getting the following error:

Expression 'undefined' in attribute 'item' used with directive 'parent' is non-assignable!

Here's the DEMO to illustrate better the situation

Can you explain to me how I can make it work?


Solution

  • You need to remove the bindings from yor parent component. bindings binds to the component controller like scope binds to a directive's scope. You're not passing anything to <parent></parent> So you have to remove it.

    Then your child component requires a parent component, not an item. So

      require: {
        parent: '^parent'
      }
    

    Of course the child template should be modified to:

    <h2 ng-bind='$ctrl.parent.item.name'></h2>
    

    Finally, if from the child controller you want to log the item that is inside the parent, you will have to write:

      function ChildCtrl($timeout) {
        var vm = this;
        $timeout(function() {
          console.log(vm.parent.item);
        });
      }
    

    I never need the timeout in my components, so there might be something obvious that I missed.

    http://plnkr.co/edit/0DRlbedeXN1Z5ZL45Ysf?p=preview

    EDIT:

    Oh I forgot, you need to use the $onInit hook:

    this.$onInit = function() {
      console.log(vm.parent.item);
    }