I am new to Angular and trying to understand advanced directive API - I want to re-create directive template in compile function using directive element attributes. But when I don't have a template set (or template is empty string) instead accesing isolated directive scope I access parent (controller) scope. Also - this works on Angular 1.1 but not 1.2
here is HTML:
<div class="container" ng-app="app" ng-controller="AppController">
<sandbox title="Attribute Title"></sandbox>
</div>
JavaScript:
var app = angular.module('app', [], function () {});
app.controller('AppController', function ($scope) {
$scope.title = "AppController title";
});
app.directive('sandbox', function ($log, $compile) {
return {
restrict: 'E',
scope: {
title: "@"
},
controller: function ($scope, $element, $attrs) {
$scope.title = "Directive Controller title";
},
template: '<h1>Template</h1>', // change it to: '' and Run, than change Angular to 1.2.x
compile: function (tElement, tAttrs) {
tElement.append('<h2> Title = {{ title }}</h2>');
}
}
});
When you Run it you get:
Template
Title = Attribute Title
But when you change template to empty string you get with Angular 1.2:
Title = AppController Title
And with Angular 1.1.1:
Title = Attribute Title
My Questions:
Why there is a difference in accesing scope when template is set AND when it's not set?
Why there is a difference between Angular 1.1 and 1.2 (bug? - directive without 'template'and with isoleted scope acceses controller scope not directive scope ) ?
How can I build template that accesses isolated scope NOT parent scope in Angular 1.2 in compile function ?
Why directive controller function does not change 'title' using $scope.title = "..." - but when debugging 'scope' param in 'link' function 'title' value is 'Directive Controler Title' but it internally (WHERE to look for it) binds isoleted scope 'Attribute Value' ?
Here is JSFiddle to play with: http://jsfiddle.net/yoorek/zQ66L/4/
You've hit on a big change that happened in 1.2 (and a quirk in using "@").
1) When your template is `` Angular sees no template to apply the isolate scope to. The reason this is an issue in 1.2 is in the answer to your second question.
2) This is a result of this 1.2 breaking change- make isolate scope truly isolate:
Isolate scope is now available only to the isolate directive that requested it and its template.
So, without a template you're appending to an element that is outside the isolate scope.
From https://github.com/angular/angular.js/issues/4889:
As we can't distinguish markup that was originally in the html file and markup that was added in the compile function the latter also doesn't get the isolate scope.
and
...additional markup in the compile function should be replaced by using the template property.
The idea is that the template property can also be a function. If it is one, it will get the compile element and the compile attributes as parameters.
3) As you see above, Angular (post 1.2) is really trying to push you to use the template instead of the compile function here. Your best bet is likely to use a template function in the manner you were using compile. Alternatively you could use a link function with a $compile
- but that may be adding unneeded complexity.
By appending inside the compile function you're effectively just adding to the template so it's kind of getting around this.
4) This has to do with the way @
works. From Angulars guide to scopes:
use attrs.$observe('attr_name', function(value) { ... } in the linking function to get the interpolated value of isolate scope properties that use the '@' notation. E.g., if we have this in the linking function -- attrs.$observe('interpolated', function(value) { ... } -- value would be set to 11. (scope.interpolatedProp is undefined in the linking function. In contrast, scope.twowayBindingProp is defined in the linking function, since it uses the '=' notation.)
And you might also read this SO post on the difference between @ and =
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With