Decoupled Directives
Another talk about directives?
Sorry about that.
(But they are the best part of Angular.)
.directive('myDirective', function() {
return {
// ...
scope: {
'value': '=ngModel',
'minValue': '@',
'slideStart': '&'
},
//...
}
}
What the fudge does it mean?
Huh? Scope
Is related, but not the same as $scope.
- But it has lots of smarts built in...
- To makes directives reusable...
- By making the coupling declarative.
Example!
Let's make a slider UI widget.
Start with some boilerplate (Coffeescript from now)
.directive 'slider', () ->
template: """
<div class="slider">
<div class="nib"></div>
</div>
"""
scope:
value: '=ngModel'
replace: true
restrict: 'E'
link: ($scope, $element, $attrs) ->
return
Usage in view
<slider ng-model="digits"></slider>
Bidirectional binding:
$scope.value in directive <=> digits in view
Happens due to { value: '=ngModel' }
Make it reflect a value
<div class="slider">
<div class="nib"
ng-style="nibStyle()">
</div>
</div>
link: ($scope, $element, $attrs) ->
$scope.nibStyle = () ->
value = parseFloatDefault $scope.value, 0
proportion = clamp(value, 0, 1)
return {
marginLeft: "#{100 * proportion}%"
}
Make it move
<div class="slider">
<div class="nib" ng-style="nibStyle()"
ng-mousedown="startSliding($event)">
</div>
</div>
$scope.startSliding = (event) ->
sliding = true
angular.element(document)
.bind 'mousemove', (event) ->
return if not sliding
# ... calculate new value ...
$scope.$apply () ->
$scope.value = newValue
.bind 'mouseup', () ->
sliding = false
The cool bit: adding options
scope:
value: '=ngModel'
minValue: '@'
maxValue: '@'
<slider ng-model="digits"
min-value="0"
max-value="100">
</slider>
Sets $scope.minValue to min-value attribute
The options can be expressions!
<slider ng-model="digits"
min-value="{{min}}"
max-value="{{max}}">
</slider>
<input type="text" ng-model="max"/>
<input type="text" ng-model="min"/>
So what?
Yo Dawg.
<slider ng-model="digits"
min-value="{{min}}"
max-value="{{max}}">
</slider>
<slider ng-model="min"
min-value="-100"
max-value="{{max}}">
</slider>
<slider ng-model="max"
min-value="{{min}}"
max-value="100">
</slider>
& to finish off
Get it?
Publish a function to scope.
scope:
# ...
slideStart: '&'
slideStop: '&'
<slider ng-model="digits"
slide-start="isSliding = true"
slide-stop="isSliding = false">
</slider>
$scope.startSliding = (event) ->
# ...
$scope.slideStart()
$document.bind 'mouseup', () ->
# ...
$scope.$apply () ->
$scope.slideStop()
And in the view...
<img src="slide.gif" ng-show="isSliding" />
You can also get a value out of the function bound with &
<slider ng-model="digits"
min-value="{{min}}"
max-value="{{max}}"
scale-value="log(value)"
scale-proportion="exp(proportion)">
</slider>
newValue = $scope.scaleProportion({
proportion: newProportion
})
# ...
newProportion = $scope.scaleValue({
value: newValue
})
Recap: the main parts
Scope is like a function signature
scope:
value: '=ngModel'
minValue: '@'
maxValue: '@'
slideStart: '&'
slideStop: '&'
<slider ng-model="digits"
min-value="{{min}}"
max-value="{{max}}"
slide-start="isSliding = true"
slide-stop="isSliding = false">
</slider>
- '=' sets up two-way data binding
- '@' sets up one-way expression evaluation
- '&' sets up functions you can call