javascriptangularjsangularjs-directiveisolate-scope

How to call parent's scope function with mixed arguments?


This is my case -- I have directive with isolated scope and I would like to call function from parent's scope with mixed arguments. Mixed -- meaning, one argument comes from the directive, the other comes from the parent.

In case of arguments coming from directive, I could bind that function with < and use it, in case of arguments coming from parent's scope, I could bind entire function call with &.

I am thinking about two approaches -- one, would simulate currying, call the function with parent's arguments which would return a function accepting directive arguments. Second -- somehow introduce directive variables in the parent's scope, so I could write:

<my-directive on-alarm="emergency(parent_var,dir_var)"/>

I like the second one better. But I don't know how to do it, i.e. how to introduce directive variables into parent's scope -- without doing a manual "reverse" binding, like:

<my-directive for_sake_of_calling="dir_var" on-alarm="emergency(parent_var,dir_var)"/>

But those are more like my guesses -- the main question is: how to call parent's function with mixed arguments?


Solution

  • You can achieve this by doing the following:

    First, setup up the main application HTML,

    <body ng-app="app">
        <div ng-controller="MainCtrl as vm">
            Emergency text: {{vm.emergencyText}}
            <my-directive on-alarm="vm.emergency(vm.parentVar, directiveVar)"></my-directive>
        </div>
    </body>
    

    You'll notice that the on-alarm callback contains a reference to the vm.parentVar variable which just refers to MainCtrl.parentVar, and directiveVar which will come from the directive itself.

    Now we can create our main controller:

    angular.module('app', []);
    
    angular
      .module('app')
      .controller('MainCtrl', function () {
        // Initialise the emergency text being used in the view.
        this.emergencyText = '';
        // Define our parent var, which is a parameter called to the emergency function.
        this.parentVar = 'This is an emergency';
    
        // Define the emergency function, which will take in the parent 
        // and directive text, as specified from the view call 
        // vm.emergency(vm.parentVar, directiveVar).
        this.emergency = function (parentText, directiveText) {
          this.emergencyText = parentText + ' ' + directiveText;
        }.bind(this);
      });
    

    Finally, we will create the directive.

    angular
      .module('app')
      .directive('myDirective', function () {
        return {
          scope: {
            onAlarm: '&'
          },
          link: function (scope, element, attrs) {
            scope.onAlarm({ directiveVar: 'from myDirective' });
          }
        }
      });
    

    The magic happens after we call scope.onAlarm({ directiveVar: 'from myDirective' });. This call tells angular that the alarm callback function (emergency) will have access to directiveVar, which we referenced earlier in the view through on-alarm="vm.emergency(vm.parentVar, directiveVar)". Behind the scenes, angular will correctly resolve the parentVar scope to MainCtrl and the directiveVar scope to the directive through its $parse service.

    Here's a full plunkr.