aboutsummaryrefslogtreecommitdiffstats
path: root/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc
diff options
context:
space:
mode:
Diffstat (limited to 'docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc')
-rw-r--r--docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc323
1 files changed, 323 insertions, 0 deletions
diff --git a/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc b/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc
new file mode 100644
index 00000000..18e74edc
--- /dev/null
+++ b/docs/content/guide/dev_guide.mvc.understanding_controller.ngdoc
@@ -0,0 +1,323 @@
+@workInProgress
+@ngdoc overview
+@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 dev_guide.scopes Scope}, excluding the root scope. When you or angular create a new
+child scope object via the {@link api/angular.scope.$new scope.$new} API , there is an
+option to pass in a controller as a method argument. This will tell angular to associate the
+controller with the new scope and to augment its behavior.
+
+
+Use controllers to:
+
+
+- Set up the initial state of a scope object.
+- Add behavior to the scope object.
+
+
+# Setting up the initial state of a scope object
+
+
+Typically, when you create an application you need to set up an initial state for an angular scope.
+
+
+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.
+
+
+You set up the initial state of a scope by creating model properties. For example:
+
+
+function GreetingCtrl() {
+ this.greeting = 'Hola!';
+}
+
+
+The `GreetingCtrl` controller creates a `greeting` model which can be referred to in a template.
+
+
+When a controller function is applied to an angular scope object, the `this` of the controller
+function becomes the scope of the angular scope object, so any assignment to `this` within the
+controller function happens on the angular scope object.
+
+
+# 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.
+
+
+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
+the scope, along with any prototype methods of the controller type, become functions available in
+the template/view, and can be invoked via angular expressions and `ng:` event handlers (e.g. {@link
+api/angular.directive.ng:click ng:click}). These controller methods are always evaluated within the
+context of the angular scope object that the controller function was applied to (which means that
+the `this` keyword of any controller method is always bound to the scope that the controller
+augments). This is how the second task of adding behavior to the scope is accomplished.
+
+
+
+
+# Using Controllers Correctly
+
+
+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.
+This is discussed in the {@link dev_guide.di Dependency Injection} {@link dev_guide.services
+Services} sections of this guide.
+
+
+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
+logic. Angular offers {@link dev_guide.templates.databinding} for automatic DOM manipulation. If
+you have to perform your own manual DOM manipulation, encapsulate the presentation logic in {@link
+dev_guide.compiler.widgets widgets} and {@link dev_guide.compiler.directives directives}.
+- Input formatting — Use {@link dev_guide.templates.formatters angular formatters} instead.
+- Output filtering — Use {@link dev_guide.templates.filters angular filters} instead.
+- Run stateless or stateful code shared across controllers — Use {@link dev_guide.services angular
+services} instead.
+- Instantiate or manage the life-cycle of other components (for example, to create service
+instances).
+
+
+
+
+# Associating Controllers with Angular Scope Objects
+
+
+You can associate controllers with scope objects explicitly via the {@link api/angular.scope.$new
+scope.$new} api or implicitly via the {@link api/angular.directive.@ng:controller ng:controller
+directive} or {@link api/angular.service.$route $route service}.
+
+
+
+
+## Controller Constructor and Methods Example
+
+
+To illustrate how the controller component works 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`
+
+
+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
+
+
+<pre>
+<body ng:controller="SpicyCtrl">
+ <button ng:click="chiliSpicy()">Chili</button>
+ <button ng:click="jalapenoSpicy()">Jalapeño</button>
+ <p>The food is {{spice}} spicy!</p>
+</body>
+
+
+function SpicyCtrl() {
+ this.spice = 'very';
+ this.chiliSpicy = function() {
+ this.spice = 'chili';
+ }
+}
+
+
+SpicyCtrl.prototype.jalapenoSpicy = function() {
+ this.spice = 'jalapeño';
+}
+</pre>
+
+
+Things to notice in the example above:
+
+
+- The `ng:controller` directive is used to (implicitly) create a scope for our template, and the
+scope is augmented (managed) by the `SpicyCtrl` controller.
+- `SpicyCtrl` is just a plain JavaScript function. As an (optional) naming convention the name
+starts with capital letter and ends with "Ctrl" or "Controller".
+- The JavaScript keyword `this` in the `SpicyCtrl` function is bound to the scope that the
+controller augments.
+- Assigning a property to `this` creates or updates the model.
+- Controller methods can be created through direct assignment to scope (the `chiliSpicy` method) or
+as prototype methods of the controller constructor function (the `jalapenoSpicy` method)
+- Both controller methods are available in the template (for the `body` element and and its
+children).
+
+
+Controller methods can also take arguments, as demonstrated in the following variation of the
+previous example.
+
+
+## Controller Method Arguments Example
+
+
+<pre>
+<body ng:controller="SpicyCtrl">
+ <input name="customSpice" value="wasabi">
+ <button ng:click="spicy('chili')">Chili</button>
+ <button ng:click="spicy(customSpice)">Custom spice</button>
+ <p>The food is {{spice}} spicy!</p>
+</body>
+
+
+function SpicyCtrl() {
+ this.spice = 'very';
+ this.spicy = function(spice) {
+ this.spice = spice;
+ }
+}
+</pre>
+
+
+Notice that the `SpicyCtrl` controller now defines just one method called `spicy`, which takes one
+argument called `spice`. The template then refers to this controller method and passes in a string
+constant `'chili'` in the binding for the first button and a model property `spice` (bound to an
+input box) in the second button.
+
+
+
+
+## Controller Inheritance Example
+
+
+Controller inheritance in angular is based on {@link api/angular.scope Scope} inheritance. Let's
+have a look at an example:
+
+
+<pre>
+<body ng:controller="MainCtrl">
+ <p>Good {{timeOfDay}}, {{name}}!</p>
+ <div ng:controller="ChildCtrl">
+ <p>Good {{timeOfDay}}, {{name}}!</p>
+ <p ng:controller="BabyCtrl">Good {{timeOfDay}}, {{name}}!</p>
+</body>
+
+
+function MainCtrl() {
+ this.timeOfDay = 'morning';
+ this.name = 'Nikki';
+}
+
+
+function ChildCtrl() {
+ this.name = 'Mattie';
+}
+
+
+function BabyCtrl() {
+ this.timeOfDay = 'evening';
+ this.name = 'Gingerbreak Baby';
+}
+</pre>
+
+
+Notice how we nested three `ng:controller` directives in our template. This template construct will
+result in 4 scopes being created for our view:
+
+
+- The root scope
+- The `MainCtrl` scope, which contains `timeOfDay` and `name` models
+- The `ChildCtrl` scope, which shadows the `name` model from the previous scope and inherits the
+`timeOfDay` model
+- The `BabyCtrl` scope, which shadows both the `timeOfDay` model defined in `MainCtrl` and `name`
+model defined in the ChildCtrl
+
+
+Inheritance works between controllers in the same way as it does with models. So in our previous
+examples, all of the models could be replaced with controller methods that return string values.
+
+
+Note: Standard prototypical inheritance between two controllers doesn't work as one might expect,
+because as we mentioned earlier, controllers are not instantiated directly by angular, but rather
+are applied to the scope object.
+
+
+
+
+## Testing Controllers
+
+
+The way to test a controller depends upon how complicated the controller is.
+
+
+- If your controller doesn't use DI or scope methods — create the controller with the `new`
+operator and test away. For example:
+
+
+Controller Function:
+<pre>
+function myController() {
+ this.spices = [{"name":"pasilla", "spiciness":"mild"},
+ {"name":"jalapeno", "spiceiness":"hot hot hot!"},
+ {"name":"habanero", "spiceness":"LAVA HOT!!"}];
+
+
+ this.spice = "habanero";
+}
+</pre>
+
+
+Controller Test:
+<pre>
+describe('myController function', function() {
+
+
+ describe('myController', function(){
+ var ctrl;
+
+
+ beforeEach(function() {
+ ctrl = new myController();
+ });
+
+
+ it('should create "spices" model with 3 spices', function() {
+ expect(ctrl.spices.length).toBe(3);
+ });
+
+
+ it('should set the default value of spice', function() {
+ expect(ctrl.spice).toBe('habanero');
+ });
+ });
+});
+</pre>
+
+
+- If your controller does use DI or scope methods — create a root scope, then create the controller
+in the root scope with `scope.$new(MyController)`. Test the controller using `$eval`, if necessary.
+- If you need to test a nested controller that depends on its parent's state — create a root scope,
+create a parent scope, create a child scope, and test the controller using $eval if necessary.
+
+
+
+
+## Related Topics
+
+
+* {@link dev_guide.mvc About MVC in Angular}
+* {@link dev_guide.mvc.understanding_model Understanding the Model Component}
+* {@link dev_guide.mvc.understanding_view Understanding the View Component}