Web Front-end Notes - AngularJs
Bootstrap
Automatic Initialization
- Angularjs looks for
ngApp
directive atdocument.readyState
is set to'complete'
:- Load the module associated with the directive
- Create the application injector
- Compile the DOM treating the
ngApp
directive as the root of the compilation
Manual Initialization
angular.bootstrap(document, [moduleArray])
is used to bootstrap an application. Before calling this method, the module should be well defined and configured using angular.module()
and ngModule.config()
.
Dependency Injection
Factory Methods
The way you define a directive
, service
, or filter
is with a factory
function. The factory methods are registered with modules. The recommended way of declaring factories is:
1 | angular |
Services
,directives
,filters
, andanimations
are defined by an injectable factory method or constructor function, and can be injected withservices
,values
, andconstants
as dependencies..factory
is very much simular to.service
, they both result in singleton objects.service
is just a constructor function that will be called withnew
whilefactory
returns an object you can run some code before
Module methods
We can specify functions to run at configuration and run time for a module by calling the config
and run
methods. These functions are injectable with dependencies just like the factory functions:
1 | angular |
- The
config
method accepts a function, which can be injected withproviders
andconstants
as dependencies. Note that you CANNOT injectservices
orvalues
into configuration. - The
run
method accepts a function, which can be injected withservices
,values
and,constants
as dependencies. Note that you CANNOT injectproviders
intorun
blocks.
Controllers
Controllers are classes
or constructor functions
that are responsible for providing the application behavior that supports the declarative markup in the template. The recommended way of declaring Controllers is using the array notation:
1 | someModule.controller('MyController', ['$scope', 'dep1', 'dep2', function($scope, dep1, dep2) { |
There can be many instances of the same type of controller
in an application. Moreover, additional dependencies are made available to Controllers:
$scope
: Controllers are associated with an element in the DOM and so are provided with access to thescope
. Other components(like services) only have access to the$rootScope
service.resolves
: If a controller is instantiated as part of a route, then any values that are resolved as part of the route are made available for injection into the controller.
Providers
The injector creates two types of objects, services and specialized objects.
Services are objects whose API is defined by the developer writing the service. Specialized objects are one of controllers
, directives
, filters
or animations
. The injector needs to know how to create these objects. You tell it by registering a “recipe” for creating your object with the injector. There are five recipe types
Provider
: The most verbose, but also the most comprehensive one. The remaining four recipe types are just syntactic sugar on top of a provider recipe.Value
:Factory
:Service
:Constant
The Provider recipe is syntactically defined as a custom type that implements a $get
method. This method is a factory function just like the one we use in the Factory recipe:
1 | .provider('unicornLauncher', function UnicornLauncherProvider() { |
Life Cycle Phases
- Configuration Phase: During application bootstrap, before AngularJS create all services, it configures and instantiates all providers. We call this
the configuration phase
of the application life-cycle. During this phase, services aren’t accessible because they haven’t been created yet. - Run phase: Once the configuration phase is over, interaction with providers is disallowed and the process of creating services starts. We call this part of the application life-cycle the run phase.
Value vs. Constant
Value
recipe is still considered as a service, it will not be available in configuration phases. Simple values, like URL prefixes, don’t have dependencies or configuration, it’s often handy to make them available in both the configuration and run phases. This is what the Constant
recipe is for.
1 | constant("planetName", "Greasy Giant"); |
Dependency Annotation
3 ways of annotating code with service name information:
Inline Array Annotation
1 | someModule.controller("MyController", [ |
$inject Property Annotation
1 | var MyController = function ($scope, greeter) { |
Values in the $inject array must match the ordering of the parameters in
MyController
Implicit Annotation
1 | someModule.controller("MyController", function ($scope, greeter) { |
Given a function, the injector can infer the names of the services. However this method will not work with JavaScript minifiers/obfuscators because of how they rename parameters.
Directives
Nowadays, angular directives are mainly restricted to attributes. For elements, we have components.
To create a new directive, we call module.directive('{directiveName}, factoryFunction(){}')
, factory function returns an javascript object with options:
restrict
: one or more among EACM(Element(E), Attribute(A), Class names(C) and Comments(M)).scope
:require
: specify an array of controllers from required directives, like[^^myRequiredDirective]
^^
: prefix means searching for directives on its parents^
: prefix means searching for directives on its own element or its parents- no
^
prefix means to look on its own element only
priority
: When the DOM is ready, angular walks the DOM to identify all registered directives and compile the directives one by one based onpriority
if these directives are on the same element.terminal
: If set totrue
, the other directives(including child ones) will be skipped after this directive is compiled. See here for more details.link
can take an object withpre
andpost
properties that each of them receives afunction(scope, element, attrs, controller)
.preLink
functions get executed from parent to child of the DOM tree, andpostLink
works the other way around. However, it is not safe to do DOM transformation since the compiler linking function will fail to locate the correct elements for linking.link
: actually equal topostLink
, takes afunction link(scope, element, attrs, controller, transcludeFn){...}
function as a DOM listeners in order to munipulate the DOM:- scope: an angular scope object.
- element: the element where the directive is attached.
- attrs: a hash object with key-value pairs of normalized attribute names and their values
- controller: is the directive’s required controller instance(s) or its own controller (if any). The exact value depends on the directive’s require property.
- transcludeFn: is a transclude linking function pre-bound to the correct transclusion scope.
The basic difference between controller
and link
is that controller
can expose an API, and link
functions can interact with controllers
using require
. Use controller
when you want to expose an API to other directives. Otherwise use link
.
Directive Notes:
ng-attr-{attribute}
will decide to render that attribute on element or not, while{attribute}
will not do this.::
:one-time
binding
Misc notes
- sometimes, the
$scope.$watch()
function only works for nested object properties, which means$scope.$watch('datetime', function())
does not fire. Instead, it works when$scope.$watch('myModel.datetime', function())
. This is still under investigation. $scope.$watch('controller.scheduled', function())
can apply watch function to a controller’s property.