aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ng/controller.js34
-rw-r--r--src/ng/directive/ngController.js73
-rw-r--r--src/ng/directive/ngView.js51
-rw-r--r--src/ng/route.js2
-rw-r--r--test/ng/controllerSpec.js11
-rw-r--r--test/ng/directive/ngControllerSpec.js23
-rw-r--r--test/ng/directive/ngViewSpec.js21
7 files changed, 181 insertions, 34 deletions
diff --git a/src/ng/controller.js b/src/ng/controller.js
index 0474e766..cfd730e4 100644
--- a/src/ng/controller.js
+++ b/src/ng/controller.js
@@ -11,7 +11,8 @@
* {@link ng.$controllerProvider#register register} method.
*/
function $ControllerProvider() {
- var controllers = {};
+ var controllers = {},
+ CNTRL_REG = /^(\w+)(\s+as\s+(\w+))?$/;
/**
@@ -56,17 +57,32 @@ function $ControllerProvider() {
* a service, so that one can override this service with {@link https://gist.github.com/1649788
* BC version}.
*/
- return function(constructor, locals) {
- if(isString(constructor)) {
- var name = constructor;
- constructor = controllers.hasOwnProperty(name)
- ? controllers[name]
- : getter(locals.$scope, name, true) || getter($window, name, true);
+ return function(expression, locals) {
+ var instance, match, constructor, identifier;
- assertArgFn(constructor, name, true);
+ if(isString(expression)) {
+ match = expression.match(CNTRL_REG),
+ constructor = match[1],
+ identifier = match[3];
+ expression = controllers.hasOwnProperty(constructor)
+ ? controllers[constructor]
+ : getter(locals.$scope, constructor, true) || getter($window, constructor, true);
+
+ assertArgFn(expression, constructor, true);
+ }
+
+ instance = $injector.instantiate(expression, locals);
+
+ if (identifier) {
+ if (typeof locals.$scope !== 'object') {
+ throw new Error('Can not export controller as "' + identifier + '". ' +
+ 'No scope object provided!');
+ }
+
+ locals.$scope[identifier] = instance;
}
- return $injector.instantiate(constructor, locals);
+ return instance;
};
}];
}
diff --git a/src/ng/directive/ngController.js b/src/ng/directive/ngController.js
index 438a0d87..be2f149f 100644
--- a/src/ng/directive/ngController.js
+++ b/src/ng/directive/ngController.js
@@ -21,7 +21,8 @@
* @scope
* @param {expression} ngController Name of a globally accessible constructor function or an
* {@link guide/expression expression} that on the current scope evaluates to a
- * constructor function.
+ * constructor function. The controller instance can further be published into the scope
+ * by adding `as localName` the controller name attribute.
*
* @example
* Here is a simple form for editing user contact information. Adding, removing, clearing, and
@@ -29,10 +30,77 @@
* easily be called from the angular markup. Notice that the scope becomes the `this` for the
* controller's instance. This allows for easy access to the view data from the controller. Also
* notice that any changes to the data are automatically reflected in the View without the need
- * for a manual update.
+ * for a manual update. The example is included in two different declaration styles based on
+ * your style preferences.
<doc:example>
<doc:source>
<script>
+ function SettingsController() {
+ this.name = "John Smith";
+ this.contacts = [
+ {type: 'phone', value: '408 555 1212'},
+ {type: 'email', value: 'john.smith@example.org'} ];
+ };
+
+ SettingsController.prototype.greet = function() {
+ alert(this.name);
+ };
+
+ SettingsController.prototype.addContact = function() {
+ this.contacts.push({type: 'email', value: 'yourname@example.org'});
+ };
+
+ SettingsController.prototype.removeContact = function(contactToRemove) {
+ var index = this.contacts.indexOf(contactToRemove);
+ this.contacts.splice(index, 1);
+ };
+
+ SettingsController.prototype.clearContact = function(contact) {
+ contact.type = 'phone';
+ contact.value = '';
+ };
+ </script>
+ <div ng-controller="SettingsController as settings">
+ Name: <input type="text" ng-model="settings.name"/>
+ [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
+ Contact:
+ <ul>
+ <li ng-repeat="contact in settings.contacts">
+ <select ng-model="contact.type">
+ <option>phone</option>
+ <option>email</option>
+ </select>
+ <input type="text" ng-model="contact.value"/>
+ [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
+ | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
+ </li>
+ <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
+ </ul>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ it('should check controller', function() {
+ expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
+ expect(element('.doc-example-live li:nth-child(1) input').val())
+ .toBe('408 555 1212');
+ expect(element('.doc-example-live li:nth-child(2) input').val())
+ .toBe('john.smith@example.org');
+
+ element('.doc-example-live li:first a:contains("clear")').click();
+ expect(element('.doc-example-live li:first input').val()).toBe('');
+
+ element('.doc-example-live li:last a:contains("add")').click();
+ expect(element('.doc-example-live li:nth-child(3) input').val())
+ .toBe('yourname@example.org');
+ });
+ </doc:scenario>
+ </doc:example>
+
+
+
+ <doc:example>
+ <doc:source>
+ <script>
function SettingsController($scope) {
$scope.name = "John Smith";
$scope.contacts = [
@@ -93,6 +161,7 @@
});
</doc:scenario>
</doc:example>
+
*/
var ngControllerDirective = [function() {
return {
diff --git a/src/ng/directive/ngView.js b/src/ng/directive/ngView.js
index 5b6d938b..8d7c87c5 100644
--- a/src/ng/directive/ngView.js
+++ b/src/ng/directive/ngView.js
@@ -23,7 +23,7 @@
* @example
<example module="ngView" animations="true">
<file name="index.html">
- <div ng-controller="MainCntl">
+ <div ng-controller="MainCntl as main">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
@@ -37,26 +37,26 @@
ng-animate="{enter: 'example-enter', leave: 'example-leave'}"></div>
<hr />
- <pre>$location.path() = {{$location.path()}}</pre>
- <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
- <pre>$route.current.params = {{$route.current.params}}</pre>
- <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
- <pre>$routeParams = {{$routeParams}}</pre>
+ <pre>$location.path() = {{main.$location.path()}}</pre>
+ <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
+ <pre>$route.current.params = {{main.$route.current.params}}</pre>
+ <pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
+ <pre>$routeParams = {{main.$routeParams}}</pre>
</div>
</file>
<file name="book.html">
<div>
- controller: {{name}}<br />
- Book Id: {{params.bookId}}<br />
+ controller: {{book.name}}<br />
+ Book Id: {{book.params.bookId}}<br />
</div>
</file>
<file name="chapter.html">
<div>
- controller: {{name}}<br />
- Book Id: {{params.bookId}}<br />
- Chapter Id: {{params.chapterId}}
+ controller: {{chapter.name}}<br />
+ Book Id: {{chapter.params.bookId}}<br />
+ Chapter Id: {{chapter.params.chapterId}}
</div>
</file>
@@ -104,31 +104,33 @@
angular.module('ngView', [], function($routeProvider, $locationProvider) {
$routeProvider.when('/Book/:bookId', {
templateUrl: 'book.html',
- controller: BookCntl
+ controller: BookCntl,
+ controllerAlias: 'book'
});
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
- controller: ChapterCntl
+ controller: ChapterCntl,
+ controllerAlias: 'chapter'
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
});
- function MainCntl($scope, $route, $routeParams, $location) {
- $scope.$route = $route;
- $scope.$location = $location;
- $scope.$routeParams = $routeParams;
+ function MainCntl($route, $routeParams, $location) {
+ this.$route = $route;
+ this.$location = $location;
+ this.$routeParams = $routeParams;
}
- function BookCntl($scope, $routeParams) {
- $scope.name = "BookCntl";
- $scope.params = $routeParams;
+ function BookCntl($routeParams) {
+ this.name = "BookCntl";
+ this.params = $routeParams;
}
- function ChapterCntl($scope, $routeParams) {
- $scope.name = "ChapterCntl";
- $scope.params = $routeParams;
+ function ChapterCntl($routeParams) {
+ this.name = "ChapterCntl";
+ this.params = $routeParams;
}
</file>
@@ -202,6 +204,9 @@ var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$c
if (current.controller) {
locals.$scope = lastScope;
controller = $controller(current.controller, locals);
+ if (current.controllerAlias) {
+ lastScope[current.controllerAlias] = controller;
+ }
element.children().data('$ngControllerController', controller);
}
diff --git a/src/ng/route.js b/src/ng/route.js
index b52f4351..7651a806 100644
--- a/src/ng/route.js
+++ b/src/ng/route.js
@@ -44,6 +44,8 @@ function $RouteProvider(){
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly
* created scope or the name of a {@link angular.Module#controller registered controller}
* if passed as a string.
+ * - `controllerAlias` – `{sttring=}` – A controller alias name. If present the controller will be
+ * published to scope under the `controllerAlias` name.
* - `template` – `{string=|function()=}` – html template as a string or function that returns
* an html template as a string which should be used by {@link ng.directive:ngView ngView} or
* {@link ng.directive:ngInclude ngInclude} directives.
diff --git a/test/ng/controllerSpec.js b/test/ng/controllerSpec.js
index 2abcace9..e34463b0 100644
--- a/test/ng/controllerSpec.js
+++ b/test/ng/controllerSpec.js
@@ -88,4 +88,15 @@ describe('$controller', function() {
expect(ctrl.$scope).toBe(scope);
});
+
+
+ it('should publish controller instance into scope', function() {
+ var scope = {};
+
+ $controllerProvider.register('FooCtrl', function() { this.mark = 'foo'; });
+
+ var foo = $controller('FooCtrl as foo', {$scope: scope});
+ expect(scope.foo).toBe(foo);
+ expect(scope.foo.mark).toBe('foo');
+ });
});
diff --git a/test/ng/directive/ngControllerSpec.js b/test/ng/directive/ngControllerSpec.js
index ab85c569..402ddf09 100644
--- a/test/ng/directive/ngControllerSpec.js
+++ b/test/ng/directive/ngControllerSpec.js
@@ -3,6 +3,11 @@
describe('ngController', function() {
var element;
+ beforeEach(module(function($controllerProvider) {
+ $controllerProvider.register('PublicModule', function() {
+ this.mark = 'works';
+ });
+ }));
beforeEach(inject(function($window) {
$window.Greeter = function($scope) {
// private stuff (not exported to scope)
@@ -27,6 +32,10 @@ describe('ngController', function() {
$window.Child = function($scope) {
$scope.name = 'Adam';
};
+
+ $window.Public = function() {
+ this.mark = 'works';
+ }
}));
afterEach(function() {
@@ -41,6 +50,20 @@ describe('ngController', function() {
}));
+ it('should publish controller into scope', inject(function($compile, $rootScope) {
+ element = $compile('<div ng-controller="Public as p">{{p.mark}}</div>')($rootScope);
+ $rootScope.$digest();
+ expect(element.text()).toBe('works');
+ }));
+
+
+ it('should publish controller into scope from module', inject(function($compile, $rootScope) {
+ element = $compile('<div ng-controller="PublicModule as p">{{p.mark}}</div>')($rootScope);
+ $rootScope.$digest();
+ expect(element.text()).toBe('works');
+ }));
+
+
it('should allow nested controllers', inject(function($compile, $rootScope) {
element = $compile('<div ng-controller="Greeter"><div ng-controller="Child">{{greet(name)}}</div></div>')($rootScope);
$rootScope.$digest();
diff --git a/test/ng/directive/ngViewSpec.js b/test/ng/directive/ngViewSpec.js
index e9d53110..3150c040 100644
--- a/test/ng/directive/ngViewSpec.js
+++ b/test/ng/directive/ngViewSpec.js
@@ -55,6 +55,27 @@ describe('ngView', function() {
});
+ it('should instantiate controller with an alias', function() {
+ var log = [], controllerScope,
+ Ctrl = function($scope) {
+ this.name = 'alias';
+ controllerScope = $scope;
+ };
+
+ module(function($compileProvider, $routeProvider) {
+ $routeProvider.when('/some', {templateUrl: '/tpl.html', controller: Ctrl, controllerAlias: 'ctrl'});
+ });
+
+ inject(function($route, $rootScope, $templateCache, $location) {
+ $templateCache.put('/tpl.html', [200, '<div></div>', {}]);
+ $location.path('/some');
+ $rootScope.$digest();
+
+ expect(controllerScope.ctrl.name).toBe('alias');
+ });
+ });
+
+
it('should support string controller declaration', function() {
var MyCtrl = jasmine.createSpy('MyCtrl');