Upload
ashay-naik
View
228
Download
0
Embed Size (px)
Citation preview
8/13/2019 AJS-02
1/12
Conceptual Overview
1. 2. 3. 4. 5. 6. Invoice:7. Qty: 8. Costs: 9. Total:{{qty * cost | currency}}10. 11. 12.
This is an HTML file with some new markup. In
AJS, a file like this is called a template. When
AJS starts your application, it parses and
processes the new markup from the template
using a compiler. The loaded, transformed
and rendered DOM is called view. The first
kind of new markup are called directives.
They apply special behavior to attributes or
elements in the HTML. The ng-appattribute is
linked to a directive that automatically initializes our application. In case of the inputelement,
AJS is able to automatically validate that the entered text is non empty by evaluating the
requiredattribute. The ng-modeldirective stores/updates the value of the input field into/from
a variable and shows the validation state of the input field by adding css classes. In AJS,directives are the only place where an application touches the DOM. To access the DOM
directly, you need to write a custom directive.
The second kind of new markup are the double curly braces {{ expression | filter }}. When
the compiler encounters this markup, it will replace it with the evaluated value of the markup.
An expressionin a template is a JavaScript-like code snippet that allows to read and write
variables. AJS provides a scopefor the variables accessible to expressions. The values that are
stored in variables on the scope are referred to as the model. A filterformats the value of the
expression for display to the user. AJS provides live bindings: Whenever the input values
change, the value of the expressions are automatically recalculated and the DOM is updatedwith their values. The concept behind this is two-way data binding.
1. 2. 3. 4. 5. angular.module('invoice1', [])6. .controller('InvoiceController', function() {7. this.qty= 1;
8/13/2019 AJS-02
2/12
8. this.cost= 2;9. this.inCurr= 'EUR';10. this.currencies= ['USD', 'EUR', 'CNY'];11. this.exRate= { USD: 1, EUR: 0.74, CNY: 6.09 };12. this.total= functiontotal(outCurr) {13. return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);14. };15. this.convertCurrency= functionconvertCurrency(amount, inCurr, outCurr) {16. return amount * this.exRate[outCurr] * 1 / this.exRate[inCurr];17. };18. this.pay= functionpay() { window.alert("Thanks!"); };19. });20. 21. 22. 23. 24. Invoice:25. Quantity: 26. 27. Costs: 28. 29. {{c}}30. 31. 32. 33. Total:34. 35. {{invoice.total(c) | currency:c}}36. 37. Pay38. 39. 40. 41.
A constructor function creates a
controllerwhose purpose is to expose
variables and functionality to expressions
and directives. The ng-controllerdirective
tells AJS that the InvoiceController is
responsible for the element with the
directive and all of the elements children.
The syntax InvoiceController as invoice
tells AJS to instantiate the controller and
save it in the variable invoicein the
current scope. All expressions in the page to read and write variables within that controllerinstance are prefixed with invoice. The possible currencies are defined in the controller and
added to the template using ng-repeat. The result of the totalfunction in the controller is
bound to the DOM using {{ invoice.total() }}. This binding is live and the DOM will be
automatically updated whenever the result of the function changes. The directive ngClickused
with the button will evaluate the corresponding expression whenever the button is clicked. We
are also creating a moduleat which we register the controller.
8/13/2019 AJS-02
3/12
All the logic in the example is contained in the InvoiceController . It is a good practice to move
view independent logic from the controller into a serviceso it can reused by other parts of the
application as well. Later on, the service can be changed to load the exchange rate from the
web without changing the controller.
1. angular.module('finance2', [])2. .factory('currencyConverter', function() {3. var currencies= ['USD', 'EUR', 'CNY'],4. exRates= { USD: 1, EUR: 0.74, CNY: 6.09 };5. return { currencies: currencies, convert: convert };6. function convert(amount, inCurr, outCurr) {7. return amount * exRates[outCurr] * 1 / exRates[inCurr];8. }9. });10.11.angular.module('invoice2', ['finance2'])12. .controller('InvoiceController', ['currencyConverter', function(currencyConverter) {13. this.qty= 1;14.
this.cost= 2;15. this.inCurr= 'EUR';
16. this.currencies= currencyConverter.currencies;17.18. this.total= functiontotal(outCurr) {19. return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);20. };21. this.pay= functionpay() {22. window.alert("Thanks!");23. };24. }]);
The convertCurrency function and the definition
of the existing currencies are moved to a
different module. The controller gets hold of the
now separated function through dependency
injection. Everything within AJS (directives,
filters, controllers, service, ) is created and
wired using dependency injection. Within AJS,
the DI container is called injector. To use DI,
there needs to be a place where all the things
that should work together are registered. In AJS,
this is the purpose of modules. When AJS starts,
it will use the configuration of the module withthe name defined by the ng-appdirective,
including the configuration of all modules that
this module depends on.
In this example, the template contains the directive which tells AJS
to use the invoice2module as the main module for the application. [11] shows that this
depends on the finance2module. By this, AJS uses the InvoiceController and the
8/13/2019 AJS-02
4/12
currencyConverterservice. After AJS knows of all the parts of the application, it has to create
them. Controllers are created using a factory function. There are multiple ways to define the
factory for services. Here, we are using a function that returns the currencyConverter function
as the factory for the service.
How does the InvoiceController get a reference to the currencyConverter function? Witharguments defined on the constructor function, the injector creates objects in the right order
and passes the previously created objects into the factories of the objects that depend on
them. By the argument currencyConverter of the InvoiceController, AJS knows about the
dependency between the controller and the service and calls the controller with the service
instance as argument.
We now pass an array to the module.controller function instead of a plain function. The array
first contains the name of the service dependencies that the controller needs. The last entry in
the array is the controller constructor function. AJS uses this array syntax to define the
dependencies so that the DI also works after minifying the code, which will most probablyrename the argument name of the controller constructor function to something shorter like a.
In the following, we fetch the exchange rates from the Yahoo Finance API.
1. angular.module('finance3', [])2. .factory('currencyConverter', ['$http', function($http) {3. var YAHOO_FINANCE_URL_PATTERN=4. 'http://query.yahooapis.com/v1/public/yql?q=select * from '+5. 'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+6. 'env=store://datatables.org/alltableswithkeys& callback=JSON_CALLBACK',7. currencies= ['USD', 'EUR', 'CNY'],8. exRates= {};9. refresh();10. return { currencies: currencies, convert: convert, refresh: refresh };11.12. function convert(amount, inCurr, outCurr) {13. return amount * exRates[outCurr] * 1 / exRates[inCurr];14. }15.16. function refresh() {17. var url= YAHOO_FINANCE_URL_PATTERN.18. replace('PAIRS', 'USD' + currencies.join('","USD'));19. return $http.jsonp(url).success(function(data) {20. var newUsdToForeignRates= {};21. angular.forEach(data.query.results.rate, function(rate) {22. var currency= rate.id.substring(3,6);23. newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);24. });25. usdToForeignRates= newUsdToForeignRates;26. });27. }28. }]);
Our currencyConverter service of the finance module now uses the $httpservice, a built-in
service provided by AJS for accessing the backend. It is a wrapper around XMLHttpRequest and
JSONPtransports.
8/13/2019 AJS-02
5/12
Tutorial
Bootstrapping AJS apps
1. 2. ...3. 4. ...5.
Nothing here {{'yet' + '!'}}
The ng-appattribute represents the ngAppAJS directive. It is used to flag the html element that
AJS should consider to be the root element of the application. The entire html page or only a
portion of it can be treated as an AJS application. [3] downloads the angular.jsscript and
registers a callback that will be executed by the browser when the containing HTML page is fully
downloaded. When the callback is executed, AJS will bootstrap the application with the root of
the application DOM being the element on which the ngAppdirective was defined. [5] includes a
double-curly binding with an expression. The binding tells AJS to evaluate the expression andinsert the result into the DOM in place of the binding. AJS expression is a JavaScript-like code
snippet that is evaluated by AJS in the context of the current model scope, rather than within
the scope of the global context (window). The binding is static and the model is empty.
Generally AJS apps are bootstrapped using ngApp
directive but you can also use imperative or manual
way. During app bootstrap: [1] The injectoris created
[2] The injectorcreates the root scope that will
become the context for the model of the app [3] AJS
compiles the DOM starting at the ngAppelement,processing any directives and bindings found along the
way. Once the app is bootstrapped, AJS waits for
browser events that might change the model. If the
model changes, the affected bindings will be updated.
Static Template
The above is a purely static HTML page. The HTML code will be turned into a template that AJS
will use to dynamically display the same result with any set of data.
1. 2. 3. Nexus S4.
Fast just got faster with Nexus S.
5. 6. 7. Motorola XOOMwith Wi-Fi8.The Next, Next Generation tablet.
8/13/2019 AJS-02
6/12
9. 10.
Angular Templates
For AJS apps, the use if MVC design pattern is encouraged. In AJS, the view is a projection of the
model through the HTML template. This means that whenever the model changes, AJSrefreshes the appropriate binding points, which updates the view.
1. 2. 3. ...4. 5. 6. 7. 8. 9. 10. {{phone.name}}11.
{{phone.snippet}}
12. 13. 14.15.The ng-controllerdirective attaches a
PhoneListCtrlcontroller to the DOM at this
point. The expressions in the bindings refer to
the application model set up in the controller.
The data model (a simple array of phones in
object literal notation) is now instantiatedwithin the PhoneListCtrlcontroller. It is
simply a constructor function that takes a
$scopeparameter.
1. varphonecatApp = angular.module('phonecatApp', []);2. phonecatApp.controller('PhoneListCtrl', function($scope) {3. $scope.phones = [4. {'name': 'Nexus S', 'snippet': 'Fast just got faster with Nexus S.'},5. {'name': 'Motorola XOOM with Wi-Fi', 'snippet': 'The NNG tablet.'},6. {'name': 'MOTOROLA XOOM', 'snippet': 'The NNG tablet.'}7. ];8.
});
The PhoneListCtrlcontroller is registered in the AJS module phonecatApp, specified in the ng-
appdirective as the module to load when bootstrapping the AJS. By providing context for the
data model, the controller allows us to establish data-binding between the model and the view.
The PhoneListCtrlcontroller attaches the phone data to the $scopethat was injected into the
controller function. This scope is a prototypical descendant of the root scope that was created
8/13/2019 AJS-02
7/12
when the application was defined. This controller scope is available to all bindings located
within the tag. A scope can be seen as the glue which
allows the template, model and controller to work together.
Filtering Repeaters
1. 2. 3. 4. 5. Search: 6. 7. 8. 9. 10. 11. {{phone.name}}12.
{{phone.snippet}}
13. 14. 15. 16. 17.We added a standard HTML
tag and used AJS
filterfunction to process the
input for the ngRepeat
directive. The data that a user
types into the input box
(named query) is immediatelyavailable as a filter input in the
list repeater (phone in phones
| filter:query). The filter
function uses the query value
to create a new array that
contains only those records
that match query. ngRepeat
automatically updates the view in response to the changing number of phones returned by the
filterfilter.
Two-way Data Binding
1. Search: 2. Sort by:3. 4. Alphabetical5. Newest
8/13/2019 AJS-02
8/12
6. 7. 8. 9. {{phone.name}}10.
{{phone.snippet}}
11. 12.We added a html
element named orderProp
so that users can pick from
the two provided sorting
options. We then chained
the filterfilter with
orderByfilter to further
process the input into the
repeater. orderByis a filter
that takes an input array,
copies it and reorders the
copy which is then returned.
AJS creates two-way binding
between the selectelement
and the orderPropmodel. orderPropis then used as the input for the orderByfilter.
1. varphonecatApp = angular.module('phonecatApp', []);2. phonecatApp.controller('PhoneListCtrl', function($scope) {3. $scope.phones = [4. {'name': 'Nexus S', 'snippet': 'Fast Nexus S.', 'age': 1},5. {'name': 'Wi-Fi', 'snippet': 'The NNG tablet.', 'age': 2},6. {'name': 'MOTOROLA, 'snippet': 'The NNG tablet.', 'age': 3}7. ];8. $scope.orderProp = 'age';9. });
We modified the phonesmodel and added an ageproperty to each phone record. We added a
line to the controller that sets the default value of orderPropto ageelse the orderByfilter
would remain uninitialized until a user picked an option from the drop down menu. There is a
two-way data-binding. When the app is loaded, Newest is selected in the drop down menu
because orderPropis set to agein the controller. The binding works in the direction from our
model to the UI. If you select Alphabetically the model will be updated and the phones will bereordered. The binding works in the direction from the UI to the model.
XHRs & Dependency Injection
The app/phones/phones.json file is a dataset containing a list of phones in JSON format.
1. [ { "age": 13, "id": "moto", "name": "Motorola", "snippet": "Hello"... }, ...]
8/13/2019 AJS-02
9/12
AJS $httpis a built-in service injected by AJS DI subsystem where needed.
1. varphonecatApp = angular.module('phonecatApp', []);2. phonecatApp.controller('PhoneListCtrl', function($scope, $http) {3. $http.get('phones/phones.json').success(function(data) {4. $scope.phones = data;5.
});6. $scope.orderProp = 'age';
7. });$httpmakes an HTTP GET
request to the web server,
asking for phone/phones.json
(the url is relative to the
index.htmlfile) and returns a
promise objectwith a
success method. We call this
method to handle theasynchronous response and
assign the phone data to the
scope controlled by this
controller, as a model called
phones. To use a service in AJS, declare the name of the required dependencies as arguments to
the controllers constructor function. The dependency injector takes care of creating any
transitive dependencies the service may have. AJS built-in services, scope methods and some
APIs have a $ prefix to namespace them. To prevent collisions, avoid naming your services and
models starting with $. When JavaScript is minified, the function arguments of the controller is
minified and DI would not be able to identify services correctly. To overcome the issue, use
$injectproperty on the controller function or the inline bracket notation which wraps the
function to be injected into an array of strings (representing the dependency names) followed
by the function to be injected. In the second method, the constructor function is provided inline
as an anonymous function when registering the controller.
1. functionPhoneListCtrl($scope, $http) {...}2. PhoneListCtrl.$inject = ['$scope', '$http'];3. phonecatApp.controller('PhoneListCtrl', PhoneListCtrl);4.5. functionPhoneListCtrl($scope, $http) {...}6. phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', PhoneListCtrl]);7.8. phonecatApp.controller('PhoneListCtrl', ['$scope', '$http', function($scope, $http) {..
.}]);
Templating Links & Images
The app/phones/phones.json file is a dataset containing a list of phones in JSON format.
8/13/2019 AJS-02
10/12
1. [ { ... "id": "moto", "imageUrl": "img/phone/a.jpg", "name": "Motorola", ... }, ...]Template:
1. ...2. 3. 4. 5. {{phone.name}}6.
{{phone.snippet}}
7. 8. 9. ...We add phone images to each record using an image tag with the ngSrcdirective which
prevents the browser from treating the AJS {{ expression }}markup literally and initiating a
request to invalid urlhttp://.../app/{{phone.imageUrl}} which it would have done if the
regular srcattribute had been used.
Routing and Multiple Views
A layout template is a template that is common for all views in our application. Other partial
templates are included into this layout template depending on the current routethe view
that is currently displayed to the user. Application routes in AJS are declared via the
$routeProvider, which is the provider of the $route service. This service makes it easy to wire
together controllers, view templates, and the current URL location in the browser. Using this
feature, we can implement deep linking, which lets us utilize the browsers history (back and
forward navigation) and bookmarks.
When the application bootstraps, AJS creates an injector that will be used for all DI stuff in thisapp. The injector itself doesnt know anything about what $httpor $routeservices do, in fact it
doesnt even know about the existence of these services unless it is configured with proper
module definitions. The sole responsibilities of the injector are to load specified module
definition(s), register all service providers defined in these modules, and when asked, inject a
specified function with dependencies (services) that it lazily instantiates via their providers.
Providers are objects that provide (create) instances of services and expose configuration APIs
that can be used to control the creation and runtime behavior of a service. In case of the $route
service, the $routeProvider exposes APIs that allow you to define routes for your application.
NOTE: Providers can only be injected into configfunctions. Thus, you could not inject
$routeProviderinto PhoneListCtrl.
AJS modules solve the problem of removing global state from the application and provide a way
of configuring the injector. As opposed to AMD or require.js modules, AJS modules dont try to
solve the problem of script load ordering or lazy script fetching. These goals are totally
independent and both module systems can live side by side and fulfil their goals.
http://.../app/%7b%7bphone.imageUrl%7d%7dhttp://.../app/%7b%7bphone.imageUrl%7d%7dhttp://.../app/%7b%7bphone.imageUrl%7d%7dhttp://.../app/%7b%7bphone.imageUrl%7d%7d8/13/2019 AJS-02
11/12
app/js/app.js
1. varphonecatApp = angular.module('phonecatApp', [2. 'ngRoute', 'phonecatControllers']);3.4. phonecatApp.config(['$routeProvider', function($routeProvider) {5. $routeProvider.6. when('/phones', {7. templateUrl: 'partials/phone-list.html' ,8. controller: 'PhoneListCtrl'9. }).10. when('/phones/:phoneId', {11. templateUrl: 'partials/phone-detail.html',12. controller: 'PhoneDetailCtrl'13. }).14. otherwise({15. redirectTo: '/phones'16. });17. }]);
In order to configure the app with routes, create a module phonecatApp. The second argument
passed to angular.module lists the modules that phonecatAppdepends on. By listing ngRoute
and phonecatControllers modules as dependencies, we can use the directives and services
they provide. Using configAPI we request the $routeProvider to be injected into our config
function and use the $routeProvider.when API to define our routes. /phones/:phoneId is an
example of URL template matching. The variable is extracted into the $routeParamsobject.
app/js/controllers.js
1. varphonecatControllers = angular.module('phonecatControllers', []);2.3. phonecatControllers.controller( 'PhoneListCtrl', ['$scope', '$http',4. function($scope, $http) {5. $http.get('phones/phones.json').success(function(data) {6. $scope.phones = data;7. });8. $scope.orderProp = 'age';9. }]);10.11.phonecatControllers.controller( 'PhoneDetailCtrl', ['$scope', '$routeParams',12. function($scope, $routeParams) {13. $scope.phoneId = $routeParams.phoneId;14. }]);
The $route service is usually used in conjunction with the ngViewdirective whose role is to
include the view template for the current route into the layout template. Starting with AJS 1.2,ngRoute is in its own module and must be loaded by loading the angular-route.js file distributed
with AJS using the tag.
8/13/2019 AJS-02
12/12