Controller:
Use controllers to:
- Set up the initial state of the
$scope
object. - Add behavior to the
$scope
object.
Do not use controllers to:
- Manipulate DOM — Controllers should contain only business logic. Putting any presentation logic into Controllers significantly affects its testability. Angular has databinding for most cases and directives to encapsulate manual DOM manipulation.
- Format input — Use angular form controls instead.
- Filter output — Use angular filters instead.
- Share code or state across controllers — Use angular services instead.
- Manage the life-cycle of other components (for example, to create service instances).
In general, a Controller shouldn't try to do too much. It should contain only the business logic needed for a single view.
Angular services are:
- Lazily instantiated – Angular only instantiates a service when an application component depends on it.
- Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.
Registering a Service with $provide
You can also register services via the $provide
service inside of a module's config
function:
angular.module('myModule', []).config(['$provide', function($provide) {
$provide.factory('serviceId', function() {
var shinyNewServiceInstance;
// factory function body that constructs shinyNewServiceInstance
return shinyNewServiceInstance;
});
}]);
This technique is often used in unit tests to mock out a service's dependencies.
Scope Events Propagation
Scopes can propagate events in similar fashion to DOM events. The event can be broadcasted to the scope children or emitted to scope parents.
Scope life cycle:Controllers and Scopes
Scopes and controllers interact with each other in the following situations:
-
Controllers use scopes to expose controller methods to templates (see ng-controller).
-
Controllers define methods (behavior) that can mutate the model (properties on the scope).
-
Controllers may register watches on the model. These watches execute immediately after the controller behavior executes.
Integration with the browser event loop
The diagram and the example below describe how Angular interacts with the browser's event loop.
- The browser's event-loop waits for an event to arrive. An event is a user interaction, timer event, or network event (response from a server).
- The event's callback gets executed. This enters the JavaScript context. The callback can modify the DOM structure.
- Once the callback executes, the browser leaves the JavaScript context and re-renders the view based on DOM changes.
Angular modifies the normal JavaScript flow by providing its own event processing loop. This splits the JavaScript into classical and Angular execution context. Only operations which are applied in the Angular execution context will benefit from Angular data-binding, exception handling, property watching, etc... You can also use $apply() to enter the Angular execution context from JavaScript. Keep in mind that in most places (controllers, services) $apply has already been called for you by the directive which is handling the event. An explicit call to $apply is needed only when implementing custom event callbacks, or when working with third-party library callbacks.
- Enter the Angular execution context by calling scope
.
$apply(stimulusFn)
, wherestimulusFn
is the work you wish to do in the Angular execution context. - Angular executes the
stimulusFn()
, which typically modifies application state. - Angular enters the $digest loop. The loop is made up of two smaller loops which process $evalAsync queue and the $watch list. The$digest loop keeps iterating until the model stabilizes, which means that the $evalAsync queue is empty and the $watch list does not detect any changes.
- The $evalAsync queue is used to schedule work which needs to occur outside of current stack frame, but before the browser's view render. This is usually done with
setTimeout(0)
, but thesetTimeout(0)
approach suffers from slowness and may cause view flickering since the browser renders the view after each event. - The $watch list is a set of expressions which may have changed since last iteration. If a change is detected then the
$watch
function is called which typically updates the DOM with the new value. - Once the Angular $digest loop finishes the execution leaves the Angular and JavaScript context. This is followed by the browser re-rendering the DOM to reflect any changes.
Here is the explanation of how the Hello world
example achieves the data-binding effect when the user enters text into the text field.
- During the compilation phase:
- the ng-model and input directive set up a
keydown
listener on the<input>
control. - the interpolation sets up a $watch to be notified of
name
changes.
- the ng-model and input directive set up a
- During the runtime phase:
- Pressing an '
X
' key causes the browser to emit akeydown
event on the input control. - The input directive captures the change to the input's value and calls $apply
("name = 'X';")
to update the application model inside the Angular execution context. - Angular applies the
name = 'X';
to the model. - The $digest loop begins
- The $watch list detects a change on the
name
property and notifies the interpolation, which in turn updates the DOM. - Angular exits the execution context, which in turn exits the
keydown
event and with it the JavaScript execution context. - The browser re-renders the view with update text.
- Pressing an '