Say Hello to Partial Application!

There are different approaches to define a function outside of the return block of the directive. One of them is to use curried methods.

Currying methods is a well known pattern in functional programming and also applicable to JavaScript. I think the documentation of the Lo-Dash library has a good explanation for their _.curry method:

Creates a function which accepts one or more arguments of func that when invoked either executes func returning its result, if all func arguments have been provided, or returns a function that accepts one or more of the remaining func arguments, and so on.

Or, as a code example…

var curried = _.curry(function(a, b, c) {

console.log(a + b + c);

});



curried(1)(2)(3);

// → 6



curried(1, 2)(3);

// → 6



curried(1, 2, 3);

// → 6

The Lo-Dash _.curry method provides a some nice additional features, but since ECMAScript 5 you can do this simply using Function.prototype.bind.

function concat(a,b,c){ return [a,b,c].join('&'); } concat('one','two','three');

// → "one&two&three" var prefilled = concat.bind(null, 'four', 'five');

// → returns function wrapper prefilled('six')

// → "four&five&six" prefilled('seven')

// → "four&five&seven"

Currying your directive functions

Let’s take the directive from above and modify it to use currying.

app.directive('nameChanger', function() { function setName(scope, name) {

scope.model.name = name;

} return {

restrict: 'E',

templateUrl: 'template.html',

scope: {

model: '=model'

},

link: function(scope, element, attrs, ctrl) { // no spaghetti code here :)

scope.setName = setName.bind(null, scope); }

};

});

see Plunker example

As you can see, the setName function is now outside the return block.

It now takes two arguments (as it needs to know about the scope).

We could use a wrapper function for scope.setName to pass the scope like this:

scope.setName = function(name) {

setName(scope, name)

};

But instead we use our curried method to “prefill” the first argument once in the link function and leave the passing of the second parameter name to the template:

scope.setName = setName.bind(null, scope);

This way, we have all the information we need in the setName function, outside of the return block and only need one line of code in the link function to make the setName function accessible from the template.

Note that setName.bind(null, scope) only returns a wrapper function and does not execute the setName function immediately. This will be done in the template, because now these have the same effect:

scope.setName("Bob");

// results in the same as

setName.bind(null, scope)("Bob");

// results in the same as

setName(scope, "Bob");

It’s not a solution applicable to every directive, but I think it it’s an option to be considered, when you try to keep your directives code readable and clean.