chore(compiler): change default restriction to attribute only for directives

This commit is contained in:
Misko Hevery 2012-03-07 22:47:01 -08:00
parent 6aa3cfc31b
commit 6a98c52c84
13 changed files with 302 additions and 234 deletions

View file

@ -17,13 +17,14 @@ list of some of the possible directive names: `ng:bind`, `ng-bind`, `ng_bind`, `
`data-ng-bind`. `data-ng-bind`.
The directives can be placed in element names, attributes, class names, as well as comments. Here The directives can be placed in element names, attributes, class names, as well as comments. Here
are some equivalent examples of invoking `ngBind`. are some equivalent examples of invoking `myDir`. (However, most directives are restricted to
attribute only.)
<pre> <pre>
<span ng-bind="exp"></span> <span my-dir="exp"></span>
<span class="ng-bind: exp;"></span> <span class="my-dir: exp;"></span>
<ng-bind></ng-bind> <my-dir></my-dir>
<!-- directive: ng-bind exp --!> <!-- directive: my-dir exp -->
</pre> </pre>
Directives can be invoked in many different ways, but are equivalent in the end result as shown in Directives can be invoked in many different ways, but are equivalent in the end result as shown in
@ -37,13 +38,12 @@ the following example.
} }
</script> </script>
<div ng-controller="Ctrl1"> <div ng-controller="Ctrl1">
Hello <input ng-model='name'> <hr/> Hello <input ng-model='name' ng-model-instant> <hr/>
&ltspan ng:bind="name"&gt <span ng:bind="name"></span> <br/> &ltspan ng:bind="name"&gt <span ng:bind="name"></span> <br/>
&ltspan ng_bind="name"&gt <span ng_bind="name"></span> <br/> &ltspan ng_bind="name"&gt <span ng_bind="name"></span> <br/>
&ltspan ng-bind="name"&gt <span ng-bind="name"></span> <br/> &ltspan ng-bind="name"&gt <span ng-bind="name"></span> <br/>
&ltspan data-ng-bind="name"&gt <span data-ng-bind="name"></span> <br/> &ltspan data-ng-bind="name"&gt <span data-ng-bind="name"></span> <br/>
&ltspan x-ng-bind="name"&gt <span x-ng-bind="name"></span> <br/> &ltspan x-ng-bind="name"&gt <span x-ng-bind="name"></span> <br/>
&ltspan class="ng-bind: name;"&gt <span class="ng-bind: name;"></span> <br/>
</div> </div>
</doc:source> </doc:source>
<doc:scenario> <doc:scenario>
@ -239,7 +239,7 @@ The full skeleton of the directive is shown here:
templateUrl: 'directive.html', templateUrl: 'directive.html',
replace: false, replace: false,
transclude: false, transclude: false,
restrict: 'EACM', restrict: 'A',
scope: false, scope: false,
local: {}, local: {},
compile: function compile(tElement, tAttrs, transclude) { compile: function compile(tElement, tAttrs, transclude) {
@ -312,50 +312,49 @@ compiler}. The attributes are:
* `scope` - If set to: * `scope` - If set to:
* `true` - then a new scope will be created for this directive. It is an error to have two * `true` - then a new scope will be created for this directive. If multiple directives on the
directives on the same element both requesting new scope. The new scope rule does not apply same element request new scope, only one new scope is created. The new scope rule does not
for the root of the template since the root of the template always gets a new scope. apply for the root of the template since the root of the template always gets a new scope.
* `{}` (object hash) - then a new 'isolate' scope is created. The 'isolate' scope differs from * `{}` (object hash) - then a new 'isolate' scope is created. The 'isolate' scope differs from
normal scope that it does not prototypically inherit from the parent scope. This is useful normal scope that it does not prototypically inherit from the parent scope. This is useful
when creating reusable widgets, which should not accidentally read or modify data in parent when creating reusable components, which should not accidentally read or modify data in
scope. <br/> parent scope. <br/>
The 'isolate' scope takes an object hash which defines a set of local scope properties derived The 'isolate' scope takes an object hash which defines a set of local scope properties
from the parent scope. These local properties are usefull for aliasing values for derived from the parent scope. These local properties are useful for aliasing values for
templates. Locals definition is a hash of normalized element attribute name to their templates. Locals definition is a hash of normalized element attribute name to their
coresponding binding strategy. Valid binding strategies are: corresponding binding strategy. Valid binding strategies are:
* `attribute` - one time read of element attribute value and save it to widget scope. <br/> * `attribute` - one time read of element attribute value and save it to widget scope. <br/>
Given `<widget my-attr='abc'>` and widget definition of `locals: {myAttr:'attribute'}`, then Given `<widget my-attr='abc'>` and widget definition of `locals: {myAttr:'attribute'}`,
widget scope property `myAttr` will be `"abc"`. then widget scope property `myAttr` will be `"abc"`.
* `evaluate` - one time evaluation of expression stored in the attribute. <br/> * `evaluate` - one time evaluation of expression stored in the attribute. <br/> Given
Given `<widget my-attr='name'>` and widget definition of `locals: {myAttr:'evaluate'}`, and `<widget my-attr='name'>` and widget definition of `locals: {myAttr:'evaluate'}`, and
parent scope `{name:'angular'}` then widget scope property `myAttr` will be `"angular"`. parent scope `{name:'angular'}` then widget scope property `myAttr` will be `"angular"`.
* `bind` - Set up one way binding from the element attribute to the widget scope. <br/> * `bind` - Set up one way binding from the element attribute to the widget scope. <br/>
Given `<widget my-attr='{{name}}'>` and widget definition of `locals: {myAttr:'bind'}`, and Given `<widget my-attr='{{name}}'>` and widget definition of `locals: {myAttr:'bind'}`,
parent scope `{name:'angular'}` then widget scope property `myAttr` will be `"angular"`, but and parent scope `{name:'angular'}` then widget scope property `myAttr` will be
any changes in the parent scope will be reflected in the widget scope. `"angular"`, but any changes in the parent scope will be reflected in the widget scope.
* `accessor` - Set up getter/setter function for the expression in the widget element attribute * `accessor` - Set up getter/setter function for the expression in the widget element
to the widget scope. <br/> attribute to the widget scope. <br/> Given `<widget my-attr='name'>` and widget definition
Given `<widget my-attr='name'>` and widget definition of `locals: {myAttr:'prop'}`, and of `locals: {myAttr:'prop'}`, and parent scope `{name:'angular'}` then widget scope
parent scope `{name:'angular'}` then widget scope property `myAttr` will be a function such property `myAttr` will be a function such that `myAttr()` will return `"angular"` and
that `myAttr()` will return `"angular"` and `myAttr('new value')` will update the parent `myAttr('new value')` will update the parent scope `name` property. This is useful for
scope `name` property. This is usefull for treating the element as a data-model for treating the element as a data-model for reading/writing.
reading/writing.
* `expression` - Treat element attribute as an expression to be exectude in form of an event. * `expression` - Treat element attribute as an expression to be executed in form of an event.
<br/> <br/>
Given `<widget my-attr='doSomething()'>` and widget definition of Given `<widget my-attr='doSomething()'>` and widget definition of `locals:
`locals: {myAttr:'expression'}`, and parent scope `{doSomething:function() {}}` then calling {myAttr:'expression'}`, and parent scope `{doSomething:function() {}}` then calling the
the widget scope function `myAttr` will execute the expression against the parent scope. widget scope function `myAttr` will execute the expression against the parent scope.
* `controller` - Controller constructor function. The controller is instantiated before the * `controller` - Controller constructor function. The controller is instantiated before the
pre-linking phase and it is shared with directives, if they request it by name. This allows the pre-linking phase and it is shared with other directives if they request it by name (see
directives to communicate with each other and augment each other behavior. The controller is `require` attribute). This allows the directives to communicate with each other and augment
injectable with the following locals: each other behavior. The controller is injectable with the following locals:
* `$scope` - Current scope associated with the element * `$scope` - Current scope associated with the element
* `$element` - Current element * `$element` - Current element
@ -363,8 +362,16 @@ compiler}. The attributes are:
* `$transclude` - A transclude linking function pre-bound to the correct transclusion scope: * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
`function(cloneLinkingFn)`. `function(cloneLinkingFn)`.
* `require` - Require another controller be passed into current directive linking function. The
`require` takes a name of the directive controller to pass in. If no such controller can be
found an error is raised. The name can be prefixed with:
* `?` - Don't raise an error. This makes the require dependency optional.
* `^` - Look for the controller on parent elements as well.
* `inject` (object hash) - Specifies a way to inject bindings into a controller. Injection * `inject` (object hash) - Specifies a way to inject bindings into a controller. Injection
definition is a hash of normalized element attribute name to their coresponding binding definition is a hash of normalized element attribute name to their corresponding binding
strategy. Valid binding strategies are: strategy. Valid binding strategies are:
* `attribute` - inject attribute value. <br/> * `attribute` - inject attribute value. <br/>
@ -389,16 +396,8 @@ compiler}. The attributes are:
injecting `myAttr` will inject a function which when called will execute the expression injecting `myAttr` will inject a function which when called will execute the expression
against the parent scope. against the parent scope.
* `require` - Require the another controller be passed into current directive linking function.
The `require` takes a name of the directive controller to pass in. If no such controller
can be found an error is raised. The name can be prefixd with:
* `?` - Don't reaise an error. This makes the require dependency optional.
* `^` - Look for the controller on parent elements as well.
* `restrict` - String of subset of `EACM` which restricts the directive to a specific directive * `restrict` - String of subset of `EACM` which restricts the directive to a specific directive
declaration style. declaration style. If omitted directives are allowed on attributes only.
* `E` - Element name: `<my-directive></my-directive>` * `E` - Element name: `<my-directive></my-directive>`
* `A` - Attribute: `<div my-directive="exp"></div>` * `A` - Attribute: `<div my-directive="exp"></div>`
@ -534,8 +533,8 @@ function linkingFn(scope, elm, attrs, ctrl) {
# Understanding Transclusion and Scopes # Understanding Transclusion and Scopes
It is often desirable to have reusable components, which we will refer to as widgets. Below is a It is often desirable to have reusable components. Below is a pseudo code showing how a simplified
pseudo code showing how a simplified dialog widget may work. dialog component may work.
<pre> <pre>
<div> <div>
@ -570,7 +569,9 @@ This will not render properly, unless we do some scope magic.
The first issue we have to solve is that the dialog box template expect `title` to be defined, but The first issue we have to solve is that the dialog box template expect `title` to be defined, but
the place of instantiation would like to bind to `username`. Furthermore the buttons expect `onOk` the place of instantiation would like to bind to `username`. Furthermore the buttons expect `onOk`
as well as `onCancel` functions to be present in the scope. This limits the usefulness of the as well as `onCancel` functions to be present in the scope. This limits the usefulness of the
widget. To solve the mapping issue we use the `locals` to create local variables which the template expects as follows widget. To solve the mapping issue we use the `locals` to create local variables which the
template expects as follows
<pre> <pre>
locals: { locals: {
title: 'bind', // set up title to accept data-binding title: 'bind', // set up title to accept data-binding
@ -606,8 +607,7 @@ Therefore the final directive definition looks something like this:
<pre> <pre>
transclude: true, transclude: true,
scope: 'isolate', scope: {
locals: {
title: 'bind', // set up title to accept data-binding title: 'bind', // set up title to accept data-binding
onOk: 'exp', // create a delegate onOk function onOk: 'exp', // create a delegate onOk function
onCancel: 'exp', // create a delegate onCancel function onCancel: 'exp', // create a delegate onCancel function
@ -615,7 +615,7 @@ locals: {
} }
</pre> </pre>
# Creating Widgets # Creating Components
It is often desirable to replace a single directive with a more complex DOM structure. This It is often desirable to replace a single directive with a more complex DOM structure. This
allows the directives to become a short hand for reusable components from which applications allows the directives to become a short hand for reusable components from which applications
@ -635,6 +635,7 @@ Following is an example of building a reusable widget.
angular.module('zippyModule', []) angular.module('zippyModule', [])
.directive('zippy', function(){ .directive('zippy', function(){
return { return {
restrict: 'C',
// This HTML will replace the zippy directive. // This HTML will replace the zippy directive.
replace: true, replace: true,
transclude: true, transclude: true,

View file

@ -365,15 +365,50 @@ Doc.prototype = {
html_usage_directive: function(dom){ html_usage_directive: function(dom){
var self = this; var self = this;
dom.h('Usage', function() { dom.h('Usage', function() {
dom.tag('pre', {'class':"brush: js; html-script: true;"}, function() { var restrict = self.restrict || 'AC';
dom.text('<' + self.element + ' '); if (restrict.match(/E/)) {
dom.text(self.shortName); dom.text('as element');
if (self.param.length) { dom.code(function() {
dom.text('="' + self.param[0].name + '"'); dom.text('<');
} dom.text(self.shortName);
dom.text('>\n ...\n'); (self.param||[]).forEach(function(param){
dom.text('</' + self.element + '>'); dom.text('\n ');
}); dom.text(param.optional ? ' [' : ' ');
dom.text(param.name);
dom.text(BOOLEAN_ATTR[param.name] ? '' : '="..."');
dom.text(param.optional ? ']' : '');
});
dom.text('></');
dom.text(self.shortName);
dom.text('>');
});
}
if (restrict.match(/A/)) {
var element = self.element || 'ANY'
dom.text('as attribute');
dom.code(function() {
dom.text('<' + element + ' ');
dom.text(self.shortName);
if (self.param.length) {
dom.text('="' + self.param[0].name + '"');
}
dom.text('>\n ...\n');
dom.text('</' + element + '>');
});
}
if (restrict.match(/C/)) {
dom.text('as class');
var element = self.element || 'ANY'
dom.code(function() {
dom.text('<' + element + ' class="');
dom.text(self.shortName);
if (self.param.length) {
dom.text(': ' + self.param[0].name + ';');
}
dom.text('">\n ...\n');
dom.text('</' + element + '>');
});
}
self.html_usage_directiveInfo(dom); self.html_usage_directiveInfo(dom);
self.html_usage_parameters(dom); self.html_usage_parameters(dom);
}); });
@ -427,46 +462,6 @@ Doc.prototype = {
}); });
}, },
html_usage_widget: function(dom){
var self = this;
dom.h('Usage', function() {
dom.h('In HTML Template Binding', function() {
dom.code(function() {
if (self.shortName.match(/^@/)) {
dom.text('<');
dom.text(self.element);
dom.text(' ');
dom.text(self.shortName.substring(1));
if (self.param.length) {
dom.text('="');
dom.text(self.param[0].name);
dom.text('"');
}
dom.text('>\n ...\n</');
dom.text(self.element);
dom.text('>');
} else {
dom.text('<');
dom.text(self.shortName);
(self.param||[]).forEach(function(param){
dom.text('\n ');
dom.text(param.optional ? ' [' : ' ');
dom.text(param.name);
dom.text(BOOLEAN_ATTR[param.name] ? '' : '="..."');
dom.text(param.optional ? ']' : '');
});
dom.text('></');
dom.text(self.shortName);
dom.text('>');
}
});
});
self.html_usage_directiveInfo(dom);
self.html_usage_parameters(dom);
});
},
html_usage_directiveInfo: function(dom) { html_usage_directiveInfo: function(dom) {
var self = this; var self = this;
var list = []; var list = [];

View file

@ -25,6 +25,7 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
$compileProvider.directive('docExample', ['$injector', '$log', '$browser', '$location', $compileProvider.directive('docExample', ['$injector', '$log', '$browser', '$location',
function($injector, $log, $browser, $location) { function($injector, $log, $browser, $location) {
return { return {
restrict: 'E',
terminal: true, terminal: true,
compile: function(element, attrs) { compile: function(element, attrs) {
var module = attrs.module; var module = attrs.module;
@ -238,6 +239,7 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
'</div>'; '</div>';
return { return {
restrict: 'EA',
compile: function(element, attrs) { compile: function(element, attrs) {
var tabs = angular.element(HTML_TPL.replace('{show}', attrs.show || 'false')), var tabs = angular.element(HTML_TPL.replace('{show}', attrs.show || 'false')),
nav = tabs.find('ul'), nav = tabs.find('ul'),
@ -268,35 +270,38 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
$compileProvider.directive('docTutorialNav', function() { $compileProvider.directive('docTutorialNav', function() {
return function(scope, element, attrs) { return {
var prevStep, codeDiff, nextStep, restrict: 'EA',
content, step = attrs.docTutorialNav; link:function(scope, element, attrs) {
var prevStep, codeDiff, nextStep,
content, step = attrs.docTutorialNav;
step = parseInt(step, 10); step = parseInt(step, 10);
if (step === 0) { if (step === 0) {
prevStep = ''; prevStep = '';
nextStep = 'step_01'; nextStep = 'step_01';
codeDiff = 'step-0~7...step-0'; codeDiff = 'step-0~7...step-0';
} else if (step === 11){ } else if (step === 11){
prevStep = 'step_10'; prevStep = 'step_10';
nextStep = 'the_end'; nextStep = 'the_end';
codeDiff = 'step-10...step-11'; codeDiff = 'step-10...step-11';
} else { } else {
prevStep = 'step_' + pad(step - 1); prevStep = 'step_' + pad(step - 1);
nextStep = 'step_' + pad(step + 1); nextStep = 'step_' + pad(step + 1);
codeDiff = 'step-' + step + '...step-' + step; codeDiff = 'step-' + step + '...step-' + step;
}
content = angular.element(
'<li><a href="#!/tutorial/' + prevStep + '">Previous</a></li>' +
'<li><a href="http://angular.github.com/angular-phonecat/step-' + step + '/app">Live Demo</a></li>' +
'<li><a href="https://github.com/angular/angular-phonecat/compare/' + codeDiff + '">Code Diff</a></li>' +
'<li><a href="#!/tutorial/' + nextStep + '">Next</a></li>'
);
element.attr('id', 'tutorial-nav');
element.append(content);
} }
content = angular.element(
'<li><a href="#!/tutorial/' + prevStep + '">Previous</a></li>' +
'<li><a href="http://angular.github.com/angular-phonecat/step-' + step + '/app">Live Demo</a></li>' +
'<li><a href="https://github.com/angular/angular-phonecat/compare/' + codeDiff + '">Code Diff</a></li>' +
'<li><a href="#!/tutorial/' + nextStep + '">Next</a></li>'
);
element.attr('id', 'tutorial-nav');
element.append(content);
}; };
function pad(step) { function pad(step) {

View file

@ -158,6 +158,6 @@ angular.module('ngdocs', ['ngdocs.directives'], function($locationProvider, $fil
}); });
$compileProvider.directive('code', function() { $compileProvider.directive('code', function() {
return { terminal: true }; return { restrict: 'E', terminal: true };
}); });
}); });

View file

@ -60,16 +60,13 @@ function publishExternalAPI(angular){
angularModule('ng', ['ngLocale'], ['$provide', angularModule('ng', ['ngLocale'], ['$provide',
function ngModule($provide) { function ngModule($provide) {
$provide.service('$anchorScroll', $AnchorScrollProvider);
$provide.service('$browser', $BrowserProvider);
$provide.service('$cacheFactory', $CacheFactoryProvider);
$provide.service('$compile', $CompileProvider). $provide.service('$compile', $CompileProvider).
directive({ directive({
a: htmlAnchorDirective, a: htmlAnchorDirective,
input: inputDirective, input: inputDirective,
textarea: inputDirective, textarea: inputDirective,
form: ngFormDirective, form: formDirective,
script: scriptTemplateLoader, script: scriptDirective,
select: selectDirective, select: selectDirective,
style: styleDirective, style: styleDirective,
onload: onloadDirective, onload: onloadDirective,
@ -84,7 +81,7 @@ function publishExternalAPI(angular){
ngClassOdd: ngClassOddDirective, ngClassOdd: ngClassOddDirective,
ngCloak: ngCloakDirective, ngCloak: ngCloakDirective,
ngController: ngControllerDirective, ngController: ngControllerDirective,
ngForm: ngFormDirective, ngForm: formDirective,
ngHide: ngHideDirective, ngHide: ngHideDirective,
ngInclude: ngIncludeDirective, ngInclude: ngIncludeDirective,
ngInit: ngInitDirective, ngInit: ngInitDirective,
@ -106,30 +103,36 @@ function publishExternalAPI(angular){
ngModelInstant: ngModelInstantDirective, ngModelInstant: ngModelInstantDirective,
required: requiredDirective, required: requiredDirective,
ngRequired: requiredDirective ngRequired: requiredDirective
}). }).
directive(ngEventDirectives). directive(ngAttributeAliasDirectives).
directive(ngAttributeAliasDirectives); directive(ngEventDirectives);
$provide.service('$controller', $ControllerProvider); $provide.service({
$provide.service('$cookies', $CookiesProvider); $anchorScroll: $AnchorScrollProvider,
$provide.service('$cookieStore', $CookieStoreProvider); $browser: $BrowserProvider,
$provide.service('$defer', $DeferProvider); $cacheFactory: $CacheFactoryProvider,
$provide.service('$document', $DocumentProvider); $controller: $ControllerProvider,
$provide.service('$exceptionHandler', $ExceptionHandlerProvider); $cookies: $CookiesProvider,
$provide.service('$filter', $FilterProvider); $cookieStore: $CookieStoreProvider,
$provide.service('$interpolate', $InterpolateProvider); $defer: $DeferProvider,
$provide.service('$http', $HttpProvider); $document: $DocumentProvider,
$provide.service('$httpBackend', $HttpBackendProvider); $exceptionHandler: $ExceptionHandlerProvider,
$provide.service('$location', $LocationProvider); $filter: $FilterProvider,
$provide.service('$log', $LogProvider); $interpolate: $InterpolateProvider,
$provide.service('$parse', $ParseProvider); $http: $HttpProvider,
$provide.service('$resource', $ResourceProvider); $httpBackend: $HttpBackendProvider,
$provide.service('$route', $RouteProvider); $location: $LocationProvider,
$provide.service('$routeParams', $RouteParamsProvider); $log: $LogProvider,
$provide.service('$rootScope', $RootScopeProvider); $parse: $ParseProvider,
$provide.service('$q', $QProvider); $resource: $ResourceProvider,
$provide.service('$sanitize', $SanitizeProvider); $route: $RouteProvider,
$provide.service('$sniffer', $SnifferProvider); $routeParams: $RouteParamsProvider,
$provide.service('$templateCache', $TemplateCacheProvider); $rootScope: $RootScopeProvider,
$provide.service('$window', $WindowProvider); $q: $QProvider,
}]); $sanitize: $SanitizeProvider,
$sniffer: $SnifferProvider,
$templateCache: $TemplateCacheProvider,
$window: $WindowProvider
});
}
]);
}; };

View file

@ -1,5 +1,15 @@
'use strict'; 'use strict';
function ngDirective(directive) {
if (isFunction(directive)) {
directive = {
link: directive
}
}
directive.restrict = directive.restrict || 'AC';
return valueFn(directive);
};
/** /**
* @ngdoc directive * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.ng:init * @name angular.module.ng.$compileProvider.directive.ng:init
@ -26,7 +36,7 @@
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
var ngInitDirective = valueFn({ var ngInitDirective = ngDirective({
compile: function() { compile: function() {
return { return {
pre: function(scope, element, attrs) { pre: function(scope, element, attrs) {
@ -179,14 +189,14 @@ var ngControllerDirective = ['$controller', '$window', function($controller, $wi
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
var ngBindDirective = valueFn(function(scope, element, attr) { var ngBindDirective = ngDirective(function(scope, element, attr) {
element.addClass('ng-binding').data('$binding', attr.ngBind); element.addClass('ng-binding').data('$binding', attr.ngBind);
scope.$watch(attr.ngBind, function(value) { scope.$watch(attr.ngBind, function(value) {
element.text(value == undefined ? '' : value); element.text(value == undefined ? '' : value);
}); });
}); });
var ngBindHtmlUnsafeDirective = valueFn(function(scope, element, attr) { var ngBindHtmlUnsafeDirective = ngDirective(function(scope, element, attr) {
element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe);
scope.$watch(attr.ngBindHtmlUnsafe, function(value) { scope.$watch(attr.ngBindHtmlUnsafe, function(value) {
element.html(value == undefined ? '' : value); element.html(value == undefined ? '' : value);
@ -572,7 +582,7 @@ forEach(
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
var ngSubmitDirective = valueFn(function(scope, element, attrs) { var ngSubmitDirective = ngDirective(function(scope, element, attrs) {
element.bind('submit', function() { element.bind('submit', function() {
scope.$apply(attrs.ngSubmit); scope.$apply(attrs.ngSubmit);
}); });
@ -581,7 +591,7 @@ var ngSubmitDirective = valueFn(function(scope, element, attrs) {
function classDirective(name, selector) { function classDirective(name, selector) {
name = 'ngClass' + name; name = 'ngClass' + name;
return valueFn(function(scope, element, attr) { return ngDirective(function(scope, element, attr) {
scope.$watch(attr[name], function(newVal, oldVal) { scope.$watch(attr[name], function(newVal, oldVal) {
if (selector === true || scope.$index % 2 === selector) { if (selector === true || scope.$index % 2 === selector) {
if (oldVal && (newVal !== oldVal)) { if (oldVal && (newVal !== oldVal)) {
@ -754,7 +764,7 @@ var ngClassEvenDirective = classDirective('Even', 1);
</doc:example> </doc:example>
*/ */
//TODO(misko): refactor to remove element from the DOM //TODO(misko): refactor to remove element from the DOM
var ngShowDirective = valueFn(function(scope, element, attr){ var ngShowDirective = ngDirective(function(scope, element, attr){
scope.$watch(attr.ngShow, function(value){ scope.$watch(attr.ngShow, function(value){
element.css('display', toBoolean(value) ? '' : 'none'); element.css('display', toBoolean(value) ? '' : 'none');
}); });
@ -793,7 +803,7 @@ var ngShowDirective = valueFn(function(scope, element, attr){
</doc:example> </doc:example>
*/ */
//TODO(misko): refactor to remove element from the DOM //TODO(misko): refactor to remove element from the DOM
var ngHideDirective = valueFn(function(scope, element, attr){ var ngHideDirective = ngDirective(function(scope, element, attr){
scope.$watch(attr.ngHide, function(value){ scope.$watch(attr.ngHide, function(value){
element.css('display', toBoolean(value) ? 'none' : ''); element.css('display', toBoolean(value) ? 'none' : '');
}); });
@ -831,7 +841,7 @@ var ngHideDirective = valueFn(function(scope, element, attr){
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
var ngStyleDirective = valueFn(function(scope, element, attr) { var ngStyleDirective = ngDirective(function(scope, element, attr) {
scope.$watch(attr.ngStyle, function(newStyles, oldStyles) { scope.$watch(attr.ngStyle, function(newStyles, oldStyles) {
if (oldStyles && (newStyles !== oldStyles)) { if (oldStyles && (newStyles !== oldStyles)) {
forEach(oldStyles, function(val, style) { element.css(style, '');}); forEach(oldStyles, function(val, style) { element.css(style, '');});
@ -894,7 +904,7 @@ var ngStyleDirective = valueFn(function(scope, element, attr) {
</doc:example> </doc:example>
* *
*/ */
var ngCloakDirective = valueFn({ var ngCloakDirective = ngDirective({
compile: function(element, attr) { compile: function(element, attr) {
attr.$set(attr.$attr.ngCloak, undefined); attr.$set(attr.$attr.ngCloak, undefined);
element.removeClass('ng-cloak'); element.removeClass('ng-cloak');
@ -935,6 +945,7 @@ ngAttributeAliasDirective(null, 'src');
angular.module('transclude', []) angular.module('transclude', [])
.directive('pane', function(){ .directive('pane', function(){
return { return {
restrict: 'E',
transclude: true, transclude: true,
scope: 'isolate', scope: 'isolate',
locals: { title:'bind' }, locals: { title:'bind' },
@ -962,7 +973,7 @@ ngAttributeAliasDirective(null, 'src');
</doc:example> </doc:example>
* *
*/ */
var ngTranscludeDirective = valueFn({ var ngTranscludeDirective = ngDirective({
controller: ['$transclude', '$element', function($transclude, $element) { controller: ['$transclude', '$element', function($transclude, $element) {
$transclude(function(clone) { $transclude(function(clone) {
$element.append(clone); $element.append(clone);

View file

@ -162,7 +162,7 @@ function $CompileProvider($provide) {
directive.priority = directive.priority || 0; directive.priority = directive.priority || 0;
directive.name = directive.name || name; directive.name = directive.name || name;
directive.require = directive.require || (directive.controller && directive.name); directive.require = directive.require || (directive.controller && directive.name);
directive.restrict = directive.restrict || 'EACM'; directive.restrict = directive.restrict || 'A';
directives.push(directive); directives.push(directive);
} catch (e) { } catch (e) {
$exceptionHandler(e); $exceptionHandler(e);

View file

@ -117,7 +117,7 @@ FormController.prototype.registerWidget = function(widget, alias) {
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.form * @name angular.module.ng.$compileProvider.directive.form
* *
* @scope * @scope
@ -204,7 +204,7 @@ FormController.prototype.registerWidget = function(widget, alias) {
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
var ngFormDirective = [function() { var formDirective = [function() {
return { return {
name: 'form', name: 'form',
restrict: 'E', restrict: 'E',

View file

@ -599,7 +599,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.textarea * @name angular.module.ng.$compileProvider.directive.textarea
* *
* @description * @description
@ -623,7 +623,7 @@ function checkboxInputType(scope, element, attr, ctrl) {
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.input * @name angular.module.ng.$compileProvider.directive.input
* *
* @description * @description

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.select * @name angular.module.ng.$compileProvider.directive.select
* *
* @description * @description
@ -424,6 +424,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
var optionDirective = ['$interpolate', function($interpolate) { var optionDirective = ['$interpolate', function($interpolate) {
return { return {
restrict: 'E',
priority: 100, priority: 100,
compile: function(element, attr) { compile: function(element, attr) {
if (isUndefined(attr.value)) { if (isUndefined(attr.value)) {

View file

@ -1,8 +1,9 @@
'use strict'; 'use strict';
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.ng:include * @name angular.module.ng.$compileProvider.directive.ng:include
* @restrict EA
* *
* @description * @description
* Fetches, compiles and includes an external HTML fragment. * Fetches, compiles and includes an external HTML fragment.
@ -42,22 +43,22 @@
</select> </select>
url of the template: <tt><a href="{{template.url}}">{{template.url}}</a></tt> url of the template: <tt><a href="{{template.url}}">{{template.url}}</a></tt>
<hr/> <hr/>
<div class="ng-include" src="template.url"></div> <div ng-include src="template.url"></div>
</div> </div>
</doc:source> </doc:source>
<doc:scenario> <doc:scenario>
it('should load template1.html', function() { it('should load template1.html', function() {
expect(element('.doc-example-live .ng-include').text()). expect(element('.doc-example-live [ng-include]').text()).
toBe('Content of template1.html\n'); toBe('Content of template1.html\n');
}); });
it('should load template2.html', function() { it('should load template2.html', function() {
select('template').option('1'); select('template').option('1');
expect(element('.doc-example-live .ng-include').text()). expect(element('.doc-example-live [ng-include]').text()).
toBe('Content of template2.html\n'); toBe('Content of template2.html\n');
}); });
it('should change to blank', function() { it('should change to blank', function() {
select('template').option(''); select('template').option('');
expect(element('.doc-example-live .ng-include').text()).toEqual(''); expect(element('.doc-example-live [ng-include]').text()).toEqual('');
}); });
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
@ -65,6 +66,7 @@
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile',
function($http, $templateCache, $anchorScroll, $compile) { function($http, $templateCache, $anchorScroll, $compile) {
return { return {
restrict: 'EA',
compile: function(element, attr) { compile: function(element, attr) {
var srcExp = attr.src, var srcExp = attr.src,
scopeExp = attr.scope || '', scopeExp = attr.scope || '',
@ -117,8 +119,9 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
}]; }];
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.ng:switch * @name angular.module.ng.$compileProvider.directive.ng:switch
* @restrict EA
* *
* @description * @description
* Conditionally change the DOM structure. * Conditionally change the DOM structure.
@ -176,6 +179,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
*/ */
var NG_SWITCH = 'ng-switch'; var NG_SWITCH = 'ng-switch';
var ngSwitchDirective = valueFn({ var ngSwitchDirective = valueFn({
restrict: 'EA',
compile: function(element, attr) { compile: function(element, attr) {
var watchExpr = attr.ngSwitch || attr.on, var watchExpr = attr.ngSwitch || attr.on,
cases = {}; cases = {};
@ -203,7 +207,7 @@ var ngSwitchDirective = valueFn({
} }
}); });
var ngSwitchWhenDirective = valueFn({ var ngSwitchWhenDirective = ngDirective({
transclude: 'element', transclude: 'element',
priority: 500, priority: 500,
compile: function(element, attrs, transclude) { compile: function(element, attrs, transclude) {
@ -213,7 +217,7 @@ var ngSwitchWhenDirective = valueFn({
} }
}); });
var ngSwitchDefaultDirective = valueFn({ var ngSwitchDefaultDirective = ngDirective({
transclude: 'element', transclude: 'element',
priority: 500, priority: 500,
compile: function(element, attrs, transclude) { compile: function(element, attrs, transclude) {
@ -312,7 +316,7 @@ var htmlAnchorDirective = valueFn({
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
var ngRepeatDirective = valueFn({ var ngRepeatDirective = ngDirective({
transclude: 'element', transclude: 'element',
priority: 1000, priority: 1000,
terminal: true, terminal: true,
@ -436,7 +440,7 @@ var ngRepeatDirective = valueFn({
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.ng:non-bindable * @name angular.module.ng.$compileProvider.directive.ng:non-bindable
* *
* @description * @description
@ -466,12 +470,13 @@ var ngRepeatDirective = valueFn({
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
var ngNonBindableDirective = valueFn({ terminal: true }); var ngNonBindableDirective = ngDirective({ terminal: true });
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.ng:view * @name angular.module.ng.$compileProvider.directive.ng:view
* @restrict ECA
* *
* @description * @description
* # Overview * # Overview
@ -560,6 +565,7 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
function($http, $templateCache, $route, $anchorScroll, $compile, function($http, $templateCache, $route, $anchorScroll, $compile,
$controller) { $controller) {
return { return {
restrict: 'ECA',
terminal: true, terminal: true,
link: function(scope, element) { link: function(scope, element) {
var changeCounter = 0, var changeCounter = 0,
@ -619,8 +625,9 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.ng:pluralize * @name angular.module.ng.$compileProvider.directive.ng:pluralize
* @restrict EA
* *
* @description * @description
* # Overview * # Overview
@ -788,44 +795,49 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
*/ */
var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
var BRACE = /{}/g; var BRACE = /{}/g;
return function(scope, element, attr) { return {
var numberExp = attr.count, restrict: 'EA',
whenExp = element.attr(attr.$attr.when), // this is becaues we have {{}} in attrs link: function(scope, element, attr) {
offset = attr.offset || 0, var numberExp = attr.count,
whens = scope.$eval(whenExp), whenExp = element.attr(attr.$attr.when), // this is becaues we have {{}} in attrs
whensExpFns = {}; offset = attr.offset || 0,
whens = scope.$eval(whenExp),
whensExpFns = {};
forEach(whens, function(expression, key) { forEach(whens, function(expression, key) {
whensExpFns[key] = whensExpFns[key] =
$interpolate(expression.replace(BRACE, '{{' + numberExp + '-' + offset + '}}')); $interpolate(expression.replace(BRACE, '{{' + numberExp + '-' + offset + '}}'));
}); });
scope.$watch(function() { scope.$watch(function() {
var value = parseFloat(scope.$eval(numberExp)); var value = parseFloat(scope.$eval(numberExp));
if (!isNaN(value)) { if (!isNaN(value)) {
//if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
//check it against pluralization rules in $locale service //check it against pluralization rules in $locale service
if (!whens[value]) value = $locale.pluralCat(value - offset); if (!whens[value]) value = $locale.pluralCat(value - offset);
return whensExpFns[value](scope, element, true); return whensExpFns[value](scope, element, true);
} else { } else {
return ''; return '';
} }
}, function(newVal) { }, function(newVal) {
element.text(newVal); element.text(newVal);
}); });
}
}; };
}]; }];
/** /**
* @ngdoc widget * @ngdoc directive
* @name angular.module.ng.$compileProvider.directive.script * @name angular.module.ng.$compileProvider.directive.script
* *
* @description * @description
* Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the
* template can be used by `ng:include`, `ng:view` or directive templates. * template can be used by `ng:include`, `ng:view` or directive templates.
* *
* @restrict E
*
* @example * @example
<doc:example> <doc:example>
<doc:source> <doc:source>
@ -844,8 +856,9 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
var scriptTemplateLoader = ['$templateCache', function($templateCache) { var scriptDirective = ['$templateCache', function($templateCache) {
return { return {
restrict: 'E',
terminal: true, terminal: true,
compile: function(element, attr) { compile: function(element, attr) {
if (attr.type == 'text/ng-template') { if (attr.type == 'text/ng-template') {

View file

@ -8,6 +8,7 @@ describe('$compile', function() {
$compileProvider.directive('log', function(log) { $compileProvider.directive('log', function(log) {
return { return {
restrict: 'CAM',
priority:0, priority:0,
compile: valueFn(function(scope, element, attrs) { compile: valueFn(function(scope, element, attrs) {
log(attrs.log || 'LOG'); log(attrs.log || 'LOG');
@ -16,19 +17,19 @@ describe('$compile', function() {
}); });
$compileProvider.directive('highLog', function(log) { $compileProvider.directive('highLog', function(log) {
return { priority:3, compile: valueFn(function(scope, element, attrs) { return { restrict: 'CAM', priority:3, compile: valueFn(function(scope, element, attrs) {
log(attrs.highLog || 'HIGH'); log(attrs.highLog || 'HIGH');
})}; })};
}); });
$compileProvider.directive('mediumLog', function(log) { $compileProvider.directive('mediumLog', function(log) {
return { priority:2, compile: valueFn(function(scope, element, attrs) { return { restrict: 'CAM', priority:2, compile: valueFn(function(scope, element, attrs) {
log(attrs.mediumLog || 'MEDIUM'); log(attrs.mediumLog || 'MEDIUM');
})}; })};
}); });
$compileProvider.directive('greet', function() { $compileProvider.directive('greet', function() {
return { priority:10, compile: valueFn(function(scope, element, attrs) { return { restrict: 'CAM', priority:10, compile: valueFn(function(scope, element, attrs) {
element.text("Hello " + attrs.greet); element.text("Hello " + attrs.greet);
})}; })};
}); });
@ -64,9 +65,12 @@ describe('$compile', function() {
it('should register a directive', function() { it('should register a directive', function() {
module(function($compileProvider) { module(function($compileProvider) {
$compileProvider.directive('div', function(log) { $compileProvider.directive('div', function(log) {
return function(scope, element) { return {
log('OK'); restrict: 'ECA',
element.text('SUCCESS'); link: function(scope, element) {
log('OK');
element.text('SUCCESS');
}
}; };
}) })
}); });
@ -80,10 +84,16 @@ describe('$compile', function() {
it('should allow registration of multiple directives with same name', function() { it('should allow registration of multiple directives with same name', function() {
module(function($compileProvider) { module(function($compileProvider) {
$compileProvider.directive('div', function(log) { $compileProvider.directive('div', function(log) {
return log.fn('1'); return {
restrict: 'ECA',
link: log.fn('1')
};
}); });
$compileProvider.directive('div', function(log) { $compileProvider.directive('div', function(log) {
return log.fn('2'); return {
restrict: 'ECA',
link: log.fn('2')
};
}); });
}); });
inject(function($compile, $rootScope, log) { inject(function($compile, $rootScope, log) {
@ -142,6 +152,7 @@ describe('$compile', function() {
$compileProvider.directive('log', function($injector, $rootScope) { $compileProvider.directive('log', function($injector, $rootScope) {
injector = $injector; injector = $injector;
return { return {
restrict: 'CA',
compile: function(element, templateAttr) { compile: function(element, templateAttr) {
expect(typeof templateAttr.$normalize).toBe('function'); expect(typeof templateAttr.$normalize).toBe('function');
expect(typeof templateAttr.$set).toBe('function'); expect(typeof templateAttr.$set).toBe('function');
@ -318,6 +329,7 @@ describe('$compile', function() {
beforeEach(module(function($compileProvider) { beforeEach(module(function($compileProvider) {
$compileProvider.directive('replace', valueFn({ $compileProvider.directive('replace', valueFn({
restrict: 'CAM',
replace: true, replace: true,
template: '<div class="log" style="width: 10px" high-log>Hello: <<CONTENT>></div>', template: '<div class="log" style="width: 10px" high-log>Hello: <<CONTENT>></div>',
compile: function(element, attr) { compile: function(element, attr) {
@ -326,6 +338,7 @@ describe('$compile', function() {
} }
})); }));
$compileProvider.directive('append', valueFn({ $compileProvider.directive('append', valueFn({
restrict: 'CAM',
template: '<div class="log" style="width: 10px" high-log>Hello: <<CONTENT>></div>', template: '<div class="log" style="width: 10px" high-log>Hello: <<CONTENT>></div>',
compile: function(element, attr) { compile: function(element, attr) {
attr.$set('compiled', 'COMPILED'); attr.$set('compiled', 'COMPILED');
@ -415,16 +428,18 @@ describe('$compile', function() {
beforeEach(module( beforeEach(module(
function($compileProvider) { function($compileProvider) {
$compileProvider.directive('hello', valueFn({ templateUrl: 'hello.html' })); $compileProvider.directive('hello', valueFn({ restrict: 'CAM', templateUrl: 'hello.html' }));
$compileProvider.directive('cau', valueFn({ templateUrl:'cau.html' })); $compileProvider.directive('cau', valueFn({ restrict: 'CAM', templateUrl:'cau.html' }));
$compileProvider.directive('cError', valueFn({ $compileProvider.directive('cError', valueFn({
restrict: 'CAM',
templateUrl:'error.html', templateUrl:'error.html',
compile: function() { compile: function() {
throw Error('cError'); throw Error('cError');
} }
})); }));
$compileProvider.directive('lError', valueFn({ $compileProvider.directive('lError', valueFn({
restrict: 'CAM',
templateUrl: 'error.html', templateUrl: 'error.html',
compile: function() { compile: function() {
throw Error('lError'); throw Error('lError');
@ -433,15 +448,18 @@ describe('$compile', function() {
$compileProvider.directive('iHello', valueFn({ $compileProvider.directive('iHello', valueFn({
restrict: 'CAM',
replace: true, replace: true,
templateUrl: 'hello.html' templateUrl: 'hello.html'
})); }));
$compileProvider.directive('iCau', valueFn({ $compileProvider.directive('iCau', valueFn({
restrict: 'CAM',
replace: true, replace: true,
templateUrl:'cau.html' templateUrl:'cau.html'
})); }));
$compileProvider.directive('iCError', valueFn({ $compileProvider.directive('iCError', valueFn({
restrict: 'CAM',
replace: true, replace: true,
templateUrl:'error.html', templateUrl:'error.html',
compile: function() { compile: function() {
@ -449,6 +467,7 @@ describe('$compile', function() {
} }
})); }));
$compileProvider.directive('iLError', valueFn({ $compileProvider.directive('iLError', valueFn({
restrict: 'CAM',
replace: true, replace: true,
templateUrl: 'error.html', templateUrl: 'error.html',
compile: function() { compile: function() {
@ -685,9 +704,11 @@ describe('$compile', function() {
it('should prevent multiple templates per element', function() { it('should prevent multiple templates per element', function() {
module(function($compileProvider) { module(function($compileProvider) {
$compileProvider.directive('sync', valueFn({ $compileProvider.directive('sync', valueFn({
restrict: 'C',
template: '<span></span>' template: '<span></span>'
})); }));
$compileProvider.directive('async', valueFn({ $compileProvider.directive('async', valueFn({
restrict: 'C',
templateUrl: 'template.html' templateUrl: 'template.html'
})); }));
}); });
@ -876,6 +897,7 @@ describe('$compile', function() {
$compileProvider.directive('scope' + uppercase(name), function(log) { $compileProvider.directive('scope' + uppercase(name), function(log) {
return { return {
scope: true, scope: true,
restrict: 'CA',
compile: function() { compile: function() {
return function (scope, element) { return function (scope, element) {
log(scope.$id); log(scope.$id);
@ -887,6 +909,7 @@ describe('$compile', function() {
$compileProvider.directive('iscope' + uppercase(name), function(log) { $compileProvider.directive('iscope' + uppercase(name), function(log) {
return { return {
scope: {}, scope: {},
restrict: 'CA',
compile: function() { compile: function() {
return function (scope, element) { return function (scope, element) {
iscope = scope; iscope = scope;
@ -899,6 +922,7 @@ describe('$compile', function() {
$compileProvider.directive('tiscope' + uppercase(name), function(log) { $compileProvider.directive('tiscope' + uppercase(name), function(log) {
return { return {
scope: {}, scope: {},
restrict: 'CA',
templateUrl: 'tiscope.html', templateUrl: 'tiscope.html',
compile: function() { compile: function() {
return function (scope, element) { return function (scope, element) {
@ -911,8 +935,11 @@ describe('$compile', function() {
}); });
}); });
$compileProvider.directive('log', function(log) { $compileProvider.directive('log', function(log) {
return function(scope) { return {
log('log-' + scope.$id + '-' + scope.$parent.$id); restrict: 'CA',
link: function(scope) {
log('log-' + scope.$id + '-' + scope.$parent.$id);
}
}; };
}); });
})); }));
@ -1098,6 +1125,7 @@ describe('$compile', function() {
forEach(['a', 'b', 'c'], function(name) { forEach(['a', 'b', 'c'], function(name) {
$compileProvider.directive(name, function(log) { $compileProvider.directive(name, function(log) {
return { return {
restrict: 'ECA',
compile: function() { compile: function() {
log('t' + uppercase(name)) log('t' + uppercase(name))
return { return {
@ -1174,8 +1202,11 @@ describe('$compile', function() {
it('should read boolean attributes as boolean', function() { it('should read boolean attributes as boolean', function() {
module(function($compileProvider) { module(function($compileProvider) {
$compileProvider.directive({ $compileProvider.directive({
div: valueFn(function(scope, element, attr) { div: valueFn({
element.text(attr.required); restrict: 'ECA',
link:function(scope, element, attr) {
element.text(attr.required);
}
}) })
}); });
}); });
@ -1207,8 +1238,11 @@ describe('$compile', function() {
it('should read boolean attributes as boolean', function() { it('should read boolean attributes as boolean', function() {
module(function($compileProvider) { module(function($compileProvider) {
$compileProvider.directive({ $compileProvider.directive({
div: valueFn(function(scope, element, attr) { div: valueFn({
element.text(attr.required); restrict: 'ECA',
link: function(scope, element, attr) {
element.text(attr.required);
}
}) })
}); });
}); });
@ -1274,8 +1308,11 @@ describe('$compile', function() {
var attr; var attr;
beforeEach(function(){ beforeEach(function(){
module(function($compileProvider) { module(function($compileProvider) {
$compileProvider.directive('div', valueFn(function(scope, element, attr){ $compileProvider.directive('div', valueFn({
scope.attr = attr; restrict: 'ECA',
link: function(scope, element, attr) {
scope.attr = attr;
}
})); }));
}); });
inject(function($compile, $rootScope) { inject(function($compile, $rootScope) {
@ -1629,9 +1666,11 @@ describe('$compile', function() {
module(function($compileProvider) { module(function($compileProvider) {
$compileProvider.directive('first', valueFn({ $compileProvider.directive('first', valueFn({
scope: {}, scope: {},
restrict: 'CA',
transclude: 'content' transclude: 'content'
})); }));
$compileProvider.directive('second', valueFn({ $compileProvider.directive('second', valueFn({
restrict: 'CA',
transclude: 'content' transclude: 'content'
})); }));
}); });

View file

@ -1186,7 +1186,7 @@ describe('widget', function() {
}); });
describe('scriptTemplateLoader', function() { describe('scriptDirective', function() {
it('should populate $templateCache with contents of a ng-template script element', inject( it('should populate $templateCache with contents of a ng-template script element', inject(
function($compile, $templateCache) { function($compile, $templateCache) {
if (msie <=8) return; if (msie <=8) return;