Search code examples
angularjsangularjs-scopeangularjs-components

How can I pass a function with argument to a component?


I want to pass a function from the parent component to the child component and give it an argument that has also been given from the parent component to the child. (showOrHideSub="item.showOrHideSub(item.id)" ) I have tried different ways and it doesn't work.

This is my html (parent component) in which I want to use the child component tag. vm is the controller of this scope:

<li ng-repeat="item in vm.menuItems">
<menu-item-comp id="item.id" showOrHideSub="item.showOrHideSub(item.id)" />
</li>

Here is the child component template. itemVm is the controller of this component:

<div id="{{itemVm.id}}" ng-mouseover="itemVm.showOrHideSub(itemVm.id)">
<div id="itemVm.subId" class="menuItemImgText">{{ itemVm.label }}</div>

Here is the child component js:

    module.component('menuItemComp', {
        templateUrl: '/webapp/app/components/menu/menuItemComponent.html',
        bindings: {
            id: '<',
            showOrHideSub: '&',
            label: '<',
            submenuId: '<',
        },
        controllerAs: 'itemVm',
        controller: ['LogService', menuCtrl]
    });

    function menuCtrl($scope, LogService) {

        var itemVm = this;
    }

And here is the showOrHideSub() function in the parent controller:

    vm.showOrHideSub = function (submenu) {
        console.log(submenu);
        switch (submenu) {
            case 'menuItemDivPositions':
                console.log('position');
                break;
            case 'menuItemDivOther':
                console.log('other');
                break;
        }
    }

I know that in directives the way to do it is by object mapping such as showOrHideSub="item.showOrHideSub({item: item.id})" but it doesn't seem to work in component.


Solution

  • If you're working with components, you have to do it the components way. It looks like you have a hierarchy of components (child / parent).

    Functions and attributes inside the parent can be inherited by children using require.

    require: {
      parent: '^^parentComponent'
    }
    

    This way, if the parent defines a function showOrHideSub, the children can call it directly using this.parent.showOrHideSub(xxx)

    This is not the only way to solve your issue but this is the right way™ for the architecture you chose.

    var parentComponent = {
        bindings: {},
        controller: ParentController,
        template: `
          <li ng-repeat="item in $ctrl.menuItems">
            <child-component item="item"></child-component>
          </li>
        `
    };
    var childComponent = {
        bindings: {
          item: '<'
        },
        require: {
          parent: '^^parentComponent'
        },
        controller: ChildController,
        template: '<button ng-click="$ctrl.buttonClick($ctrl.item.id);">{{$ctrl.item.name}}</button>'
    };
    function ParentController() {
      this.menuItems = [{id:1, name:"item1"},{id:2, name:"item2"}];
      this.showOrHideSub = function(param) {
         console.log("parent function called with param: " + param);
      }
    }
    function ChildController() {
      var vm = this;
      this.buttonClick = function(id) {
        vm.parent.showOrHideSub(id);
      }
    }
    
    angular.module('app', []);
    angular.module('app')
        .component('parentComponent', parentComponent)
        .component('childComponent', childComponent);
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
    <div ng-app="app">
      <parent-component></parent-component>
    </div>