Skip to content

Commit 349000c

Browse files
committed
feature(asDirective): create as directive
1 parent 7324804 commit 349000c

File tree

4 files changed

+180
-0
lines changed

4 files changed

+180
-0
lines changed

angularFiles.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ var angularFiles = {
5353

5454
'src/ng/directive/directives.js',
5555
'src/ng/directive/a.js',
56+
'src/ng/directive/as.js',
5657
'src/ng/directive/attrs.js',
5758
'src/ng/directive/form.js',
5859
'src/ng/directive/input.js',

src/AngularPublic.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
$CompileProvider,
77
88
htmlAnchorDirective,
9+
componentAsDirective,
910
inputDirective,
1011
inputDirective,
1112
formDirective,
@@ -166,6 +167,7 @@ function publishExternalAPI(angular) {
166167
$provide.provider('$compile', $CompileProvider).
167168
directive({
168169
a: htmlAnchorDirective,
170+
as: componentAsDirective,
169171
input: inputDirective,
170172
textarea: inputDirective,
171173
form: formDirective,

src/ng/directive/as.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use strict';
2+
3+
/**
4+
* @ngdoc directive
5+
* @name as
6+
* @restrict A
7+
*
8+
* @description
9+
* Publishes a component controller or a DOM element into the current scope.
10+
*
11+
* @element ANY
12+
* @param {expression} as {@link guide/expression Expression} to assign the controller.
13+
*
14+
* This directive allows to access to the controller of components in your
15+
* current template or from current controller.
16+
*
17+
* @example
18+
* ### Access to component controller
19+
* This example demonstrates basic publishing of the component controller into the scope.
20+
* <example name="asDirectiveComponentExample" module="asComponentExample">
21+
* <file name="index.html">
22+
* <script>
23+
* angular.module('asComponentExample', [])
24+
* .component('toggle', {
25+
* controller: function() {
26+
* var opened = false;
27+
* this.isOpen = function() { return opened; };
28+
* this.toggle = function() { opened = !opened; };
29+
* }
30+
* });
31+
* </script>
32+
* <toggle as="myToggle"></toggle>
33+
* <button ng-click="myToggle.toggle()">Toggle</button>
34+
* <div ng-show="myToggle.isOpen()">You are using a children component to show it.</div>
35+
* </file>
36+
* <file name="protractor.js" type="protractor">
37+
* it('should publish the toggle into the scope', function() {
38+
* var toggle = element(by.buttonText('Toggle'));
39+
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(false);
40+
* toggle.click();
41+
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(true);
42+
* });
43+
* </file>
44+
* </example>
45+
*
46+
* This directive also allows to bind elements to the scope.
47+
*
48+
* ```js
49+
* var myMod = angular.module(...);
50+
* myMod.component('myPlayer', {
51+
* template: '' +
52+
* '<audio as="$ctrl.audio" src="foo.ogg"></audio>' +
53+
* '<button ng-click="$ctrl.play()">Play</button>' +
54+
* '',
55+
* controller: function() {
56+
* this.play = function() {
57+
* this.audio.play();
58+
* };
59+
* }
60+
* });
61+
* ```
62+
*
63+
* Note that angular does not allows to access DOM elements directly
64+
* from templates, you need a controller to manipulate them.
65+
*
66+
*/
67+
var componentAsDirective = ['$parse', function($parse) {
68+
return {
69+
restrict: 'A',
70+
compile: function(tElement, tAttrs) {
71+
// gets the theoretical controller name, converts <some-thing> into "someThing")
72+
var controllerName = tElement[0].tagName.toLowerCase()
73+
.replace(/[^\w]\w/g, function(a) { return a.slice(1).toUpperCase(); });
74+
75+
// get the setter for the as attribute
76+
var setter = $parse(tAttrs.as).assign;
77+
78+
return function(scope, iElement, iAttrs) {
79+
// gets the controller of the current element (see jqLiteController for details)
80+
var controller = iElement.data('$' + controllerName + 'Controller');
81+
82+
if (controller) {
83+
setter(scope, controller);
84+
} else {
85+
setter(scope, iElement[0]);
86+
}
87+
};
88+
}
89+
};
90+
}];

test/ng/directive/asSpec.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict';
2+
3+
describe('a', function() {
4+
5+
it('should bind in the current scope the controller of a component', function() {
6+
var expected = {some: 'value'};
7+
8+
angular.module('my', []).component('myComponent', {
9+
template: 'foo',
10+
controller: function() {
11+
this.property = expected;
12+
}
13+
});
14+
module('my');
15+
16+
inject(function($compile, $rootScope) {
17+
var scope = $rootScope.$new();
18+
var $ctrl = {undamaged: true};
19+
scope.$ctrl = $ctrl;
20+
dealoc($compile('<my-component as="$ctrl.myComponent"></my-component>')(scope));
21+
22+
expect(scope.$ctrl).toBe($ctrl);
23+
expect(scope.$ctrl.undamaged).toBe(true);
24+
expect($ctrl.myComponent).not.toBeUndefined();
25+
expect($ctrl.myComponent && $ctrl.myComponent.property).toBe(expected);
26+
scope.$destroy();
27+
});
28+
});
29+
30+
it('should be parametrized with any variable', function() {
31+
var myComponentController;
32+
33+
angular.module('my', []).component('myComponent', {
34+
template: 'foo',
35+
controller: function() {
36+
myComponentController = this;
37+
}
38+
});
39+
module('my');
40+
41+
inject(function($compile, $rootScope) {
42+
var scope = $rootScope.$new();
43+
dealoc($compile('<my-component as="bar.myComponent"></my-component>')(scope));
44+
45+
expect(scope.bar).not.toBeUndefined();
46+
expect(scope.bar && scope.bar.myComponent).toBe(myComponentController);
47+
scope.$destroy();
48+
});
49+
});
50+
51+
it('should be compatible with entity directives with controller', function() {
52+
var myDirectiveController;
53+
54+
angular.module('my', []).directive('myDirective', function() {
55+
return {
56+
restrict: 'E',
57+
controller: function() {
58+
myDirectiveController = this;
59+
}
60+
};
61+
});
62+
module('my');
63+
64+
inject(function($compile, $rootScope) {
65+
var scope = $rootScope.$new();
66+
dealoc($compile('<my-directive as="bar.myDirective"></my-directive>')(scope));
67+
68+
expect(scope.bar).not.toBeUndefined();
69+
expect(scope.bar && scope.bar.myDirective).toBe(myDirectiveController);
70+
scope.$destroy();
71+
});
72+
});
73+
74+
it('should bind non HTMLElements elements into the current scope if no component', inject(function($compile, $rootScope) {
75+
var scope = $rootScope.$new();
76+
var $ctrl = {};
77+
scope.$ctrl = $ctrl;
78+
var element = $compile('<div as="$ctrl.myDiv">SUCCESS</div>')(scope);
79+
80+
expect(scope.$ctrl).toBe($ctrl);
81+
expect($ctrl.myDiv).not.toBeUndefined();
82+
expect($ctrl.myDiv && $ctrl.myDiv.innerText).toBe('SUCCESS');
83+
dealoc(element);
84+
scope.$destroy();
85+
}));
86+
87+
});

0 commit comments

Comments
 (0)