From 116fac05628807acec6baf331a31b58f26f774a7 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 11 Oct 2013 12:59:39 +0100 Subject: docs(guide/controller): improve guidance and examples Remove mention of global controller functions Convert larger examples to runnable demos Remove mention of pre-1.0 controllers, in particular discussion of controller inheritance. TODO: Probably could do with updating to explain the "controller as" syntax at some point. Closes: #4373 --- .../dev_guide.mvc.understanding_controller.ngdoc | 357 ++++++++++++--------- 1 file changed, 202 insertions(+), 155 deletions(-) diff --git a/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc b/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc index 880601fa..847f7f20 100644 --- a/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc +++ b/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc @@ -2,24 +2,31 @@ @name Developer Guide: About MVC in Angular: Understanding the Controller Component @description -In Angular, a controller is a JavaScript function(type/class) that is used to augment instances of -angular {@link scope Scope}, excluding the root scope. +# Understanding Controllers -Use controllers to: +In Angular, a Controller is a JavaScript **constructor function** that is used to augment the +{@link scope Angular Scope}. -- Set up the initial state of a scope object. -- Add behavior to the scope object. +When a Controller is attached to the DOM via the {@link api/ng.directive:ngController ng-controller} +directive, Angular will instantiate a new Controller object, using the specified Controller's +**constructor function**. A new **child scope** will be available as an injectable parameter to the +Controller's constructor function as `$scope`. -# Setting up the initial state of a scope object +Use Controllers to: -Typically, when you create an application you need to set up an initial state for an Angular scope. +- Set up the initial state of the `$scope` object. +- Add behavior to the `$scope` object. -Angular applies (in the sense of JavaScript's `Function#apply`) the controller constructor function -to a new Angular scope object, which sets up an initial scope state. This means that Angular never -creates instances of the controller type (by invoking the `new` operator on the controller -constructor). Constructors are always applied to an existing scope object. +# Setting up the initial state of a `$scope` object -You set up the initial state of a scope by creating model properties. For example: +Typically, when you create an application you need to set up the initial state for the Angular +`$scope`. You set up the initial state of a scope by attaching properties to the `$scope` object. +The properties contain the **view model** (the model that will be presented by the view). All the +`$scope` properties will be available to the template at the point in the DOM where the Controller +is registered. + +The following example shows a very simple constructor function for a Controller, `GreetingCtrl`, +which attaches a `greeting` property containing the string `'Hola!'` to the `$scope`:
     function GreetingCtrl($scope) {
@@ -27,12 +34,18 @@ You set up the initial state of a scope by creating model properties. For exampl
     }
 
 
-The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
+Once the Controller has been attached to the DOM, the `greeting` property can be data-bound to the
+template:
 
-**NOTE**: Many of the examples in the documentation show the creation of functions
-in the global scope. This is only for demonstration purposes - in a real
-application you should use the `.controller` method of your Angular module for
-your application as follows:
+++ +**NOTE**: Although Angular allows you to create Controller functions in the global scope, this is +not recommended. In a real application you should use the `.controller` method of your +{@link module Angular Module} for your application as follows:+ {{ greeting }} ++
     var myApp = angular.module('myApp',[]);
@@ -42,40 +55,62 @@ your application as follows:
     }]);
 
 
-Note also that we use the array notation to explicitly specify the dependency
-of the controller on the `$scope` service provided by Angular.
+We have used an **inline injection annotation** to explicitly specify the dependency
+of the Controller on the `$scope` service provided by Angular. See the guide on 
+{@link http://docs.angularjs.org/guide/di Dependency Injection} for more information.
+
 
 # Adding Behavior to a Scope Object
 
-Behavior on an Angular scope object is in the form of scope method properties available to the
-template/view. This behavior interacts with and modifies the application model.
+In order to react to events or execute computation in the view we must provide behavior to the
+scope. We add behavior the scope by attaching methods to the `$scope` object.  These methods are
+then available to be called from the template/view.
+
+The following example uses a Controller to add a method to the scope, which doubles a number:
+
+
+    var myApp = angular.module('myApp',[]);
+
+    myApp.controller('DoubleCtrl', ['$scope', function($scope) {
+        $scope.double = function(value) { return value * 2; };
+    }]);
+
+
+Once the Controller has been attached to the DOM, the `double` method can be invoked in an Angular
+expression in the template:
+
++As discussed in the {@link dev_guide.mvc.understanding_model Model} section of this guide, any -objects (or primitives) assigned to the scope become model properties. Any functions assigned to +objects (or primitives) assigned to the scope become model properties. Any methods assigned to the scope are available in the template/view, and can be invoked via angular expressions and `ng` event handler directives (e.g. {@link api/ng.directive:ngClick ngClick}). # Using Controllers Correctly -In general, a controller shouldn't try to do too much. It should contain only the business logic +In general, a Controller shouldn't try to do too much. It should contain only the business logic needed for a single view. -The most common way to keep controllers slim is by encapsulating work that doesn't belong to -controllers into services and then using these services in controllers via dependency injection. +The most common way to keep Controllers slim is by encapsulating work that doesn't belong to +controllers into services and then using these services in Controllers via dependency injection. This is discussed in the {@link di Dependency Injection} {@link dev_guide.services Services} sections of this guide. -Do not use controllers for: +Do not use Controllers for: - Any kind of DOM manipulation — Controllers should contain only business logic. DOM -manipulation—the presentation logic of an application—is well known for being hard to test. -Putting any presentation logic into controllers significantly affects testability of the business +manipulation (the presentation logic of an application) is well known for being hard to test. +Putting any presentation logic into Controllers significantly affects testability of the business logic. Angular offers {@link dev_guide.templates.databinding databinding} for automatic DOM manipulation. If you have to perform your own manual DOM manipulation, encapsulate the presentation logic in {@link guide/directive directives}. - Input formatting — Use {@link forms angular form controls} instead. - Output filtering — Use {@link dev_guide.templates.filters angular filters} instead. -- Sharing stateless or stateful code across controllers — Use {@link dev_guide.services angular +- Sharing stateless or stateful code across Controllers — Use {@link dev_guide.services angular services} instead. - Managing the life-cycle of other components (for example, to create service instances). @@ -86,196 +121,208 @@ You can associate controllers with scope objects implicitly via the {@link api/n directive} or {@link api/ng.$route $route service}. -## Controller Constructor and Methods Example +## Simple Spicy Controller Example -To illustrate how the controller component works in angular, let's create a little app with the +To illustrate further how Controller components work in Angular, let's create a little app with the following components: - A {@link dev_guide.templates template} with two buttons and a simple message - A model consisting of a string named `spice` -- A controller with two functions that set the value of `spice` +- A Controller with two functions that set the value of `spice` The message in our template contains a binding to the `spice` model, which by default is set to the string "very". Depending on which button is clicked, the `spice` model is set to `chili` or `jalapeño`, and the message is automatically updated by data-binding. - -## A Spicy Controller Example - -+ Two times equals {{ double(num) }} ++
- - - -+The food is {{spice}} spicy!
- - -var myApp = angular.module('SpicyApp', []); - -myApp.controller('SpicyCtrl', ['$scope', function($scope){ - $scope.spicy = 'very'; - - $scope.chiliSpicy = function() { - $scope.spice = 'chili'; - }; - - $scope.jalapenoSpicy = function() { - $scope.spice = 'jalapeño'; - }; -}]); -
The food is {{spice}} spicy!
+- - - - -+The food is {{spice}} spicy!
- - -var myApp = angular.module('SpicyApp', []); - -myApp.controller('SpicyCtrl', ['$scope', function($scope){ - $scope.customSpice = "wasabi"; - $scope.spice = 'very'; - - $scope.spicy = function(spice){ - $scope.spice = spice; - }; -}]); - -
The food is {{spice}} spicy!
+- -- -Notice how we nested three `ngController` directives in our template. This template construct will -result in 4 scopes being created for our view: +## Scope Inheritance Example + +It is common to attach Controllers at different levels of the DOM hierarchy. Since the +{@link api/ng.directive:ngController ng-controller} directive creates a new child scope, we get a +hierarchy of scopes that inherit from each other. The `$scope` that each Controller receives will +have access to properties and methods defined by Controllers higher up the hierarchy. +See {@link https://github.com/angular/angular.js/wiki/Understanding-Scopes Understanding Scopes} for +more information about scope inheritance. + +Good {{timeOfDay}}, {{name}}!
--- - -var myApp = angular.module('MyApp', []) - -.controller('MainCtrl', ['$scope', function($scope){ - $scope.timeOfDay = 'morning'; - $scope.name = 'Nikki'; -}]) -.controller('ChildCtrl', ['$scope', function($scope){ - $scope.name = 'Mattie'; -}]) -.controller('BabyCtrl', ['$scope', function($scope){ - $scope.timeOfDay = 'evening'; - $scope.name = 'Gingerbreak Baby'; -}]); -Good {{timeOfDay}}, {{name}}!
-Good {{timeOfDay}}, {{name}}!
-
Good {{timeOfDay}}, {{name}}!
+ +Good {{timeOfDay}}, {{name}}!
+ +Good {{timeOfDay}}, {{name}}!
+
-function myController($scope) {
-   $scope.spices = [{"name":"pasilla", "spiciness":"mild"},
-                  {"name":"jalapeno", "spiceiness":"hot hot hot!"},
-                  {"name":"habanero", "spiceness":"LAVA HOT!!"}];
+    var myApp = angular.module('myApp',[]);
 
-   $scope.spice = "habanero";
-}
+    myApp.controller('MyController', function($scope) {
+      $scope.spices = [{"name":"pasilla", "spiciness":"mild"},
+                       {"name":"jalapeno", "spiceiness":"hot hot hot!"},
+                       {"name":"habanero", "spiceness":"LAVA HOT!!"}];
+      $scope.spice = "habanero";
+    });
 
 
-Controller Test:
+**Controller Test:**
 
 describe('myController function', function() {
 
   describe('myController', function() {
-    var scope;
+    var $scope;
+
+    beforeEach(module('myApp'));
 
     beforeEach(inject(function($rootScope, $controller) {
-      scope = $rootScope.$new();
-      var ctrl = $controller(myController, {$scope: scope});
+      $scope = $rootScope.$new();
+      $controller('MyController', {$scope: $scope});
     }));
 
     it('should create "spices" model with 3 spices', function() {
-      expect(scope.spices.length).toBe(3);
+      expect($scope.spices.length).toBe(3);
     });
 
     it('should set the default value of spice', function() {
-      expect(scope.spice).toBe('habanero');
+      expect($scope.spice).toBe('habanero');
     });
   });
 });
 
 
 
-If you need to test a nested controller you need to create the same scope hierarchy
-in your test that exists in the DOM.
+If you need to test a nested Controller you need to create the same scope hierarchy
+in your test that exists in the DOM:
 
 
 describe('state', function() {
     var mainScope, childScope, babyScope;
 
+    beforeEach(module('myApp'));
+
     beforeEach(inject(function($rootScope, $controller) {
         mainScope = $rootScope.$new();
-        var mainCtrl = $controller(MainCtrl, {$scope: mainScope});
+        $controller('MainCtrl', {$scope: mainScope});
         childScope = mainScope.$new();
-        var childCtrl = $controller(ChildCtrl, {$scope: childScope});
+        $controller('ChildCtrl', {$scope: childScope});
         babyScope = childScope.$new();
-        var babyCtrl = $controller(BabyCtrl, {$scope: babyScope});
+        $controller('BabyCtrl', {$scope: babyScope});
     }));
 
     it('should have over and selected', function() {
-- 
cgit v1.2.3