(First off, sorry for the long winded post title. It is basically what I google every time I’m trying to remember the functionality.)
Let’s say you have a directive that allows a user to enter data. When that user is done entering data, the directive processes the data, then sends the result back up to the parent. If we were to do this with two-way data binding, we’d have to set up watchers to know when the values were changed. There is a way to do it without the watchers though. We can use the parent scopeĀ binding type: “&”.
In the world of Angular Directives, a lot has been written about two way scope variable binding, but I always seem to have difficulty when using scope bound variables. That is, scope variables that are bound with “&*. The Angular docs define it as:
& or &attr – provides a way to execute an expression in the context of the parent scope. If no attr name is specified then the attribute name is assumed to be the same as the local name. Given <widget my-attr=”count = count + value”> and widget definition of scope: { localFn:’&myAttr’ }, then isolate scope property localFn will point to a function wrapper for the count = count + value expression. Often it’s desirable to pass data from the isolated scope via an expression to the parent scope, this can be done by passing a map of local variable names and values into the expression wrapper fn. For example, if the expression is increment(amount) then we can specify the amount value by calling the localFn as localFn({amount: 22}).
This means we can call a function in our directive’s isolate scope that maps to the directive’s parent scope without having to know anything about it!
Let’s setup a parent controller and a basic input directive that will return an uppercase value when completed:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
angular | |
.module('myApp', []) | |
.controller('MyController', MyController) | |
.controller('MyDirectiveController', MyDirectiveController) | |
.directive('myAwesomeDirective', myAwesomeDirective); | |
function MyController() { | |
var vm = this; | |
vm.myValue = 'Initial Value'; | |
vm.updateValue = function(value) { | |
vm.myValue = value; | |
}; | |
} | |
function MyDirectiveController() { | |
var vm = this; | |
vm.buttonClick = function() { | |
var value = vm.directiveValue.toUpperCase(); | |
// Note: param is `valueToUpdate` not `value` since we | |
// defined it as `valueToUpdate` in the html parameter | |
vm.updateFunction({valueToUpdate: value}); | |
} | |
} | |
function myAwesomeDirective() { | |
var template = '<input class="form-control" type="text" ng-model="vm.directiveValue">'; | |
template += '<button class="btn btn-warning" ng-click="vm.buttonClick()">Update!</button>'; | |
return { | |
restrict: 'E', | |
controller: 'MyDirectiveController', | |
controllerAs: 'vm', | |
bindToController: true, | |
scope: { | |
updateFunction: '&' | |
}, | |
template: template | |
}; | |
} |
Our implementation looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html ng-app="myApp"> | |
<head> | |
<script data-require="angular.js@1.4.7" data-semver="1.4.7" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.7/angular.min.js"></script> | |
<link rel="stylesheet" href="style.css" /> | |
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" /> | |
<script src="script.js"></script> | |
</head> | |
<body> | |
<div ng-controller="MyController as vm"> | |
<h1>My Value: {{ vm.myValue }}</h1> | |
<my-awesome-directive update-function="vm.updateValue(valueToUpdate)"> | |
</my-awesome-directive> | |
</div> | |
</body> | |
</html> |
The main takeaway here is the update-function="vm.updateValue(valueToUpdate)"
line. This is what passes the parent’s updateValue function to the directive. Also, you’ll see that I’ve used “valueToUpdate” in the html file, while the parameter in the main controller is just “value”. This was just to emphasize the linkage between the directive controller and the directive parameter. The “updateValue” function in the main controller still keeps the parameter named “value”.
There is also a working plunker example of our code below: