Skip to content

Commit 6a98c52

Browse files
committed
chore(compiler): change default restriction to attribute only for directives
1 parent 6aa3cfc commit 6a98c52

13 files changed

+308
-240
lines changed

docs/content/api/angular.module.ng.$compileProvider.directive.ngdoc

Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ list of some of the possible directive names: `ng:bind`, `ng-bind`, `ng_bind`, `
1717
`data-ng-bind`.
1818

1919
The directives can be placed in element names, attributes, class names, as well as comments. Here
20-
are some equivalent examples of invoking `ngBind`.
20+
are some equivalent examples of invoking `myDir`. (However, most directives are restricted to
21+
attribute only.)
2122

2223
<pre>
23-
<span ng-bind="exp"></span>
24-
<span class="ng-bind: exp;"></span>
25-
<ng-bind></ng-bind>
26-
<!-- directive: ng-bind exp --!>
24+
<span my-dir="exp"></span>
25+
<span class="my-dir: exp;"></span>
26+
<my-dir></my-dir>
27+
<!-- directive: my-dir exp -->
2728
</pre>
2829

2930
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.
3738
}
3839
</script>
3940
<div ng-controller="Ctrl1">
40-
Hello <input ng-model='name'> <hr/>
41+
Hello <input ng-model='name' ng-model-instant> <hr/>
4142
&ltspan ng:bind="name"&gt <span ng:bind="name"></span> <br/>
4243
&ltspan ng_bind="name"&gt <span ng_bind="name"></span> <br/>
4344
&ltspan ng-bind="name"&gt <span ng-bind="name"></span> <br/>
4445
&ltspan data-ng-bind="name"&gt <span data-ng-bind="name"></span> <br/>
4546
&ltspan x-ng-bind="name"&gt <span x-ng-bind="name"></span> <br/>
46-
&ltspan class="ng-bind: name;"&gt <span class="ng-bind: name;"></span> <br/>
4747
</div>
4848
</doc:source>
4949
<doc:scenario>
@@ -239,7 +239,7 @@ The full skeleton of the directive is shown here:
239239
templateUrl: 'directive.html',
240240
replace: false,
241241
transclude: false,
242-
restrict: 'EACM',
242+
restrict: 'A',
243243
scope: false,
244244
local: {},
245245
compile: function compile(tElement, tAttrs, transclude) {
@@ -312,59 +312,66 @@ compiler}. The attributes are:
312312

313313
* `scope` - If set to:
314314

315-
* `true` - then a new scope will be created for this directive. It is an error to have two
316-
directives on the same element both requesting new scope. The new scope rule does not apply
317-
for the root of the template since the root of the template always gets a new scope.
315+
* `true` - then a new scope will be created for this directive. If multiple directives on the
316+
same element request new scope, only one new scope is created. The new scope rule does not
317+
apply for the root of the template since the root of the template always gets a new scope.
318318

319319
* `{}` (object hash) - then a new 'isolate' scope is created. The 'isolate' scope differs from
320320
normal scope that it does not prototypically inherit from the parent scope. This is useful
321-
when creating reusable widgets, which should not accidentally read or modify data in parent
322-
scope. <br/>
323-
The 'isolate' scope takes an object hash which defines a set of local scope properties derived
324-
from the parent scope. These local properties are usefull for aliasing values for
321+
when creating reusable components, which should not accidentally read or modify data in
322+
parent scope. <br/>
323+
The 'isolate' scope takes an object hash which defines a set of local scope properties
324+
derived from the parent scope. These local properties are useful for aliasing values for
325325
templates. Locals definition is a hash of normalized element attribute name to their
326-
coresponding binding strategy. Valid binding strategies are:
326+
corresponding binding strategy. Valid binding strategies are:
327327

328328
* `attribute` - one time read of element attribute value and save it to widget scope. <br/>
329-
Given `<widget my-attr='abc'>` and widget definition of `locals: {myAttr:'attribute'}`, then
330-
widget scope property `myAttr` will be `"abc"`.
329+
Given `<widget my-attr='abc'>` and widget definition of `locals: {myAttr:'attribute'}`,
330+
then widget scope property `myAttr` will be `"abc"`.
331331

332-
* `evaluate` - one time evaluation of expression stored in the attribute. <br/>
333-
Given `<widget my-attr='name'>` and widget definition of `locals: {myAttr:'evaluate'}`, and
332+
* `evaluate` - one time evaluation of expression stored in the attribute. <br/> Given
333+
`<widget my-attr='name'>` and widget definition of `locals: {myAttr:'evaluate'}`, and
334334
parent scope `{name:'angular'}` then widget scope property `myAttr` will be `"angular"`.
335335

336336
* `bind` - Set up one way binding from the element attribute to the widget scope. <br/>
337-
Given `<widget my-attr='{{name}}'>` and widget definition of `locals: {myAttr:'bind'}`, and
338-
parent scope `{name:'angular'}` then widget scope property `myAttr` will be `"angular"`, but
339-
any changes in the parent scope will be reflected in the widget scope.
340-
341-
* `accessor` - Set up getter/setter function for the expression in the widget element attribute
342-
to the widget scope. <br/>
343-
Given `<widget my-attr='name'>` and widget definition of `locals: {myAttr:'prop'}`, and
344-
parent scope `{name:'angular'}` then widget scope property `myAttr` will be a function such
345-
that `myAttr()` will return `"angular"` and `myAttr('new value')` will update the parent
346-
scope `name` property. This is usefull for treating the element as a data-model for
347-
reading/writing.
348-
349-
* `expression` - Treat element attribute as an expression to be exectude in form of an event.
337+
Given `<widget my-attr='{{name}}'>` and widget definition of `locals: {myAttr:'bind'}`,
338+
and parent scope `{name:'angular'}` then widget scope property `myAttr` will be
339+
`"angular"`, but any changes in the parent scope will be reflected in the widget scope.
340+
341+
* `accessor` - Set up getter/setter function for the expression in the widget element
342+
attribute to the widget scope. <br/> Given `<widget my-attr='name'>` and widget definition
343+
of `locals: {myAttr:'prop'}`, and parent scope `{name:'angular'}` then widget scope
344+
property `myAttr` will be a function such that `myAttr()` will return `"angular"` and
345+
`myAttr('new value')` will update the parent scope `name` property. This is useful for
346+
treating the element as a data-model for reading/writing.
347+
348+
* `expression` - Treat element attribute as an expression to be executed in form of an event.
350349
<br/>
351-
Given `<widget my-attr='doSomething()'>` and widget definition of
352-
`locals: {myAttr:'expression'}`, and parent scope `{doSomething:function() {}}` then calling
353-
the widget scope function `myAttr` will execute the expression against the parent scope.
350+
Given `<widget my-attr='doSomething()'>` and widget definition of `locals:
351+
{myAttr:'expression'}`, and parent scope `{doSomething:function() {}}` then calling the
352+
widget scope function `myAttr` will execute the expression against the parent scope.
354353

355354
* `controller` - Controller constructor function. The controller is instantiated before the
356-
pre-linking phase and it is shared with directives, if they request it by name. This allows the
357-
directives to communicate with each other and augment each other behavior. The controller is
358-
injectable with the following locals:
355+
pre-linking phase and it is shared with other directives if they request it by name (see
356+
`require` attribute). This allows the directives to communicate with each other and augment
357+
each other behavior. The controller is injectable with the following locals:
359358

360359
* `$scope` - Current scope associated with the element
361360
* `$element` - Current element
362361
* `$attrs` - Current attributes obeject for the element
363362
* `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
364363
`function(cloneLinkingFn)`.
365364

365+
* `require` - Require another controller be passed into current directive linking function. The
366+
`require` takes a name of the directive controller to pass in. If no such controller can be
367+
found an error is raised. The name can be prefixed with:
368+
369+
* `?` - Don't raise an error. This makes the require dependency optional.
370+
* `^` - Look for the controller on parent elements as well.
371+
372+
366373
* `inject` (object hash) - Specifies a way to inject bindings into a controller. Injection
367-
definition is a hash of normalized element attribute name to their coresponding binding
374+
definition is a hash of normalized element attribute name to their corresponding binding
368375
strategy. Valid binding strategies are:
369376

370377
* `attribute` - inject attribute value. <br/>
@@ -389,16 +396,8 @@ compiler}. The attributes are:
389396
injecting `myAttr` will inject a function which when called will execute the expression
390397
against the parent scope.
391398

392-
* `require` - Require the another controller be passed into current directive linking function.
393-
The `require` takes a name of the directive controller to pass in. If no such controller
394-
can be found an error is raised. The name can be prefixd with:
395-
396-
* `?` - Don't reaise an error. This makes the require dependency optional.
397-
* `^` - Look for the controller on parent elements as well.
398-
399-
400399
* `restrict` - String of subset of `EACM` which restricts the directive to a specific directive
401-
declaration style.
400+
declaration style. If omitted directives are allowed on attributes only.
402401

403402
* `E` - Element name: `<my-directive></my-directive>`
404403
* `A` - Attribute: `<div my-directive="exp"></div>`
@@ -534,8 +533,8 @@ function linkingFn(scope, elm, attrs, ctrl) {
534533

535534
# Understanding Transclusion and Scopes
536535

537-
It is often desirable to have reusable components, which we will refer to as widgets. Below is a
538-
pseudo code showing how a simplified dialog widget may work.
536+
It is often desirable to have reusable components. Below is a pseudo code showing how a simplified
537+
dialog component may work.
539538

540539
<pre>
541540
<div>
@@ -570,7 +569,9 @@ This will not render properly, unless we do some scope magic.
570569
The first issue we have to solve is that the dialog box template expect `title` to be defined, but
571570
the place of instantiation would like to bind to `username`. Furthermore the buttons expect `onOk`
572571
as well as `onCancel` functions to be present in the scope. This limits the usefulness of the
573-
widget. To solve the mapping issue we use the `locals` to create local variables which the template expects as follows
572+
widget. To solve the mapping issue we use the `locals` to create local variables which the
573+
template expects as follows
574+
574575
<pre>
575576
locals: {
576577
title: 'bind', // set up title to accept data-binding
@@ -606,16 +607,15 @@ Therefore the final directive definition looks something like this:
606607

607608
<pre>
608609
transclude: true,
609-
scope: 'isolate',
610-
locals: {
610+
scope: {
611611
title: 'bind', // set up title to accept data-binding
612612
onOk: 'exp', // create a delegate onOk function
613613
onCancel: 'exp', // create a delegate onCancel function
614614
show: 'prop' // create a getter/setter function for visibility.
615615
}
616616
</pre>
617617

618-
# Creating Widgets
618+
# Creating Components
619619

620620
It is often desirable to replace a single directive with a more complex DOM structure. This
621621
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.
635635
angular.module('zippyModule', [])
636636
.directive('zippy', function(){
637637
return {
638+
restrict: 'C',
638639
// This HTML will replace the zippy directive.
639640
replace: true,
640641
transclude: true,

docs/src/ngdoc.js

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -365,15 +365,50 @@ Doc.prototype = {
365365
html_usage_directive: function(dom){
366366
var self = this;
367367
dom.h('Usage', function() {
368-
dom.tag('pre', {'class':"brush: js; html-script: true;"}, function() {
369-
dom.text('<' + self.element + ' ');
370-
dom.text(self.shortName);
371-
if (self.param.length) {
372-
dom.text('="' + self.param[0].name + '"');
373-
}
374-
dom.text('>\n ...\n');
375-
dom.text('</' + self.element + '>');
376-
});
368+
var restrict = self.restrict || 'AC';
369+
if (restrict.match(/E/)) {
370+
dom.text('as element');
371+
dom.code(function() {
372+
dom.text('<');
373+
dom.text(self.shortName);
374+
(self.param||[]).forEach(function(param){
375+
dom.text('\n ');
376+
dom.text(param.optional ? ' [' : ' ');
377+
dom.text(param.name);
378+
dom.text(BOOLEAN_ATTR[param.name] ? '' : '="..."');
379+
dom.text(param.optional ? ']' : '');
380+
});
381+
dom.text('></');
382+
dom.text(self.shortName);
383+
dom.text('>');
384+
});
385+
}
386+
if (restrict.match(/A/)) {
387+
var element = self.element || 'ANY'
388+
dom.text('as attribute');
389+
dom.code(function() {
390+
dom.text('<' + element + ' ');
391+
dom.text(self.shortName);
392+
if (self.param.length) {
393+
dom.text('="' + self.param[0].name + '"');
394+
}
395+
dom.text('>\n ...\n');
396+
dom.text('</' + element + '>');
397+
});
398+
}
399+
if (restrict.match(/C/)) {
400+
dom.text('as class');
401+
var element = self.element || 'ANY'
402+
dom.code(function() {
403+
dom.text('<' + element + ' class="');
404+
dom.text(self.shortName);
405+
if (self.param.length) {
406+
dom.text(': ' + self.param[0].name + ';');
407+
}
408+
dom.text('">\n ...\n');
409+
dom.text('</' + element + '>');
410+
});
411+
}
377412
self.html_usage_directiveInfo(dom);
378413
self.html_usage_parameters(dom);
379414
});
@@ -427,46 +462,6 @@ Doc.prototype = {
427462
});
428463
},
429464

430-
html_usage_widget: function(dom){
431-
var self = this;
432-
dom.h('Usage', function() {
433-
dom.h('In HTML Template Binding', function() {
434-
dom.code(function() {
435-
if (self.shortName.match(/^@/)) {
436-
dom.text('<');
437-
dom.text(self.element);
438-
dom.text(' ');
439-
dom.text(self.shortName.substring(1));
440-
if (self.param.length) {
441-
dom.text('="');
442-
dom.text(self.param[0].name);
443-
dom.text('"');
444-
}
445-
dom.text('>\n ...\n</');
446-
dom.text(self.element);
447-
dom.text('>');
448-
} else {
449-
dom.text('<');
450-
dom.text(self.shortName);
451-
(self.param||[]).forEach(function(param){
452-
dom.text('\n ');
453-
dom.text(param.optional ? ' [' : ' ');
454-
dom.text(param.name);
455-
dom.text(BOOLEAN_ATTR[param.name] ? '' : '="..."');
456-
dom.text(param.optional ? ']' : '');
457-
});
458-
dom.text('></');
459-
dom.text(self.shortName);
460-
dom.text('>');
461-
}
462-
});
463-
});
464-
465-
self.html_usage_directiveInfo(dom);
466-
self.html_usage_parameters(dom);
467-
});
468-
},
469-
470465
html_usage_directiveInfo: function(dom) {
471466
var self = this;
472467
var list = [];

docs/src/templates/doc_widgets.js

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
2525
$compileProvider.directive('docExample', ['$injector', '$log', '$browser', '$location',
2626
function($injector, $log, $browser, $location) {
2727
return {
28+
restrict: 'E',
2829
terminal: true,
2930
compile: function(element, attrs) {
3031
var module = attrs.module;
@@ -238,6 +239,7 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
238239
'</div>';
239240

240241
return {
242+
restrict: 'EA',
241243
compile: function(element, attrs) {
242244
var tabs = angular.element(HTML_TPL.replace('{show}', attrs.show || 'false')),
243245
nav = tabs.find('ul'),
@@ -268,35 +270,38 @@ angular.module('ngdocs.directives', [], function($compileProvider) {
268270

269271

270272
$compileProvider.directive('docTutorialNav', function() {
271-
return function(scope, element, attrs) {
272-
var prevStep, codeDiff, nextStep,
273-
content, step = attrs.docTutorialNav;
274-
275-
step = parseInt(step, 10);
276-
277-
if (step === 0) {
278-
prevStep = '';
279-
nextStep = 'step_01';
280-
codeDiff = 'step-0~7...step-0';
281-
} else if (step === 11){
282-
prevStep = 'step_10';
283-
nextStep = 'the_end';
284-
codeDiff = 'step-10...step-11';
285-
} else {
286-
prevStep = 'step_' + pad(step - 1);
287-
nextStep = 'step_' + pad(step + 1);
288-
codeDiff = 'step-' + step + '...step-' + step;
289-
}
273+
return {
274+
restrict: 'EA',
275+
link:function(scope, element, attrs) {
276+
var prevStep, codeDiff, nextStep,
277+
content, step = attrs.docTutorialNav;
278+
279+
step = parseInt(step, 10);
280+
281+
if (step === 0) {
282+
prevStep = '';
283+
nextStep = 'step_01';
284+
codeDiff = 'step-0~7...step-0';
285+
} else if (step === 11){
286+
prevStep = 'step_10';
287+
nextStep = 'the_end';
288+
codeDiff = 'step-10...step-11';
289+
} else {
290+
prevStep = 'step_' + pad(step - 1);
291+
nextStep = 'step_' + pad(step + 1);
292+
codeDiff = 'step-' + step + '...step-' + step;
293+
}
290294

291-
content = angular.element(
292-
'<li><a href="#!/tutorial/' + prevStep + '">Previous</a></li>' +
293-
'<li><a href="http://angular.github.com/angular-phonecat/step-' + step + '/app">Live Demo</a></li>' +
294-
'<li><a href="https://github.com/angular/angular-phonecat/compare/' + codeDiff + '">Code Diff</a></li>' +
295-
'<li><a href="#!/tutorial/' + nextStep + '">Next</a></li>'
296-
);
295+
content = angular.element(
296+
'<li><a href="#!/tutorial/' + prevStep + '">Previous</a></li>' +
297+
'<li><a href="http://angular.github.com/angular-phonecat/step-' + step + '/app">Live Demo</a></li>' +
298+
'<li><a href="https://github.com/angular/angular-phonecat/compare/' + codeDiff + '">Code Diff</a></li>' +
299+
'<li><a href="#!/tutorial/' + nextStep + '">Next</a></li>'
300+
);
297301

298-
element.attr('id', 'tutorial-nav');
299-
element.append(content);
302+
element.attr('id', 'tutorial-nav');
303+
element.append(content);
304+
}
300305
};
301306

302307
function pad(step) {

0 commit comments

Comments
 (0)