Tải bản đầy đủ (.pdf) (108 trang)

Angular dot JS Succinctly by Frederik Dietz

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.23 MB, 108 trang )



1



2

By
Frederik Dietz
Foreword by Daniel Jebaraj



3
Copyright © 2013 by Syncfusion Inc.
2501 Aerial Center Parkway
Suite 200
Morrisville, NC 27560
USA
All rights reserved.

mportant licensing information. Please read.
This book is available for free download from www.syncfusion.com upon completion of a registration
form.
If you obtained this book from any other source, please register and download a free copy from
www.syncfusion.com.
This book is licensed for reading only if obtained from www.syncfusion.com.
This book is licensed strictly for personal or educational use.
Redistribution in any form is prohibited.
The authors and copyright holders provide absolutely no warranty for any information provided.


The authors and copyright holders shall not be liable for any claim, damages, or any other liability arising
from, out of, or in connection with the information in this book.
Please do not use this book if the listed terms are unacceptable.
Use shall constitute acceptance of the terms listed.
SYNCFUSION, SUCCINCTLY, DELIVER INNOVATION WITH EASE, ESSENTIAL, and .NET ESSENTIALS are the
registered trademarks of Syncfusion, Inc.




Technical Reviewer: Rui Machado
Copy Editor: Suzanne Kattau
Acquisitions Coordinator: Hillary Bowling, marketing coordinator, Syncfusion, Inc.
Proofreader: Darren West, content producer, Syncfusion, Inc.
I


4
Table of Contents

The Story behind the Succinctly Series of Books 13
About the Author 15
Preface 16
Introduction 16
Code Examples 16
How to Contact Me 16
Acknowledgements 16
Chapter 1 An Introduction to Angular.js 17
Including the Angular.js Library Code in an HTML Page 17
Problem 17

Solution 17
Discussion 17
Binding a Text Input to an Expression 18
Problem 18
Solution 18
Discussion 18
Responding to Click Events using Controllers 19
Problem 19
Solution 19
Discussion 20
Converting Expression Output with Filters 20
Problem 20
Solution 20


5
Discussion 21
Creating Custom HTML Elements with Directives 21
Problem 21
Solution 21
Discussion 22
Chapter 2 Controllers 23
Assigning a Default Value to a Model 23
Problem 23
Solution 23
Discussion 23
Changing a Model Value with a Controller Function 24
Problem 24
Solution 24
Discussion 24

Encapsulating a Model Value with a Controller Function 25
Problem 25
Solution 25
Discussion 25
Responding to Scope Changes 26
Problem 26
Solution 26
Discussion 26
Sharing Models between Nested Controllers 27
Problem 27
Solution 27
Discussion 28
Sharing Code between Controllers using Services 28


6
Problem 28
Solution 29
Discussion 29
Testing Controllers 30
Problem 30
Solution 30
Discussion 31
Chapter 3 Directives 32
Enabling/Disabling DOM Elements Conditionally 32
Problem 32
Solution 32
Discussion 32
Changing the DOM in Response to User Actions 33
Problem 33

Solution 33
Discussion 33
Rendering an HTML Snippet in a Directive 35
Problem 35
Solution 35
Discussion 35
Rendering a Directive's DOM Node Children 36
Problem 36
Solution 36
Discussion 37
Passing Configuration Params Using HTML Attributes 37
Problem 37
Solution 37


7
Discussion 37
Repeatedly Rendering Directive's DOM Node Children 39
Problem 39
Solution 39
Discussion 40
Directive-to-Directive Communication 41
Problem 41
Solution 41
Discussion 43
Testing Directives 43
Problem 43
Solution 45
Discussion 47
Chapter 4 Filters 48

Formatting a String with a Currency Filter 48
Problem 48
Solution 48
Discussion 48
Implementing a Custom Filter to Revert an Input String 49
Problem 49
Solution 49
Discussion 49
Passing Configuration Params to Filters 49
Problem 49
Solution 50
Discussion 50
Filtering a List of DOM Nodes 50


8
Problem 50
Solution 50
Discussion 51
Chaining Filters Together 51
Problem 51
Solution 51
Discussion 52
Testing Filters 52
Problem 52
Solution 52
Discussion 53
Chapter 5 Consuming Externals Services 54
Requesting JSON Data with AJAX 54
Problem 54

Solution 54
Discussion 55
Consuming RESTful APIs 55
Problem 55
Solution 55
Discussion 57
Consuming JSONP APIs 58
Problem 58
Solution 58
Discussion 59
Deferred and Promise 59
Problem 59
Solution 60


9
Discussion 60
Testing Services 62
Problem 62
Solution 62
Discussion 63
Chapter 6 URLs, Routing, and Partials 64
Client-Side Routing with Hashbang URLs 64
Problem 64
Solution 64
Discussion 65
Using Regular URLs with the HTML 5 History API 66
Problem 66
Solution 66
Discussion 68

Using Route Location to Implement a Navigation Menu 69
Problem 69
Solution 69
Discussion 69
Listening on Route Changes to Implement a Login Mechanism 70
Problem 70
Solution 70
Discussion 71
Chapter 7 Using Forms 72
Implementing a Basic Form 72
Problem 72
Solution 72
Discussion 73


10
Validating a Form Model Client-Side 73
Problem 73
Solution 73
Discussion 74
Displaying Form Validation Errors 75
Problem 75
Solution 75
Discussion 76
Displaying Form Validation Errors with the Twitter Bootstrap Framework 76
Problem 76
Solution 76
Discussion 78
Only Enabling the Submit Button if the Form is Valid 78
Problem 78

Solution 78
Discussion 78
Implementing Custom Validations 78
Problem 78
Solution 79
Discussion 79
Chapter 8 Common User Interface Patterns 80
Filtering and Sorting a List 80
Problem 80
Solution 80
Discussion 81
Pagination through Client-Side Data 81
Problem 81


11
Solution 81
Discussion 83
Pagination through Server-Side Data 84
Problem 84
Solution 84
Discussion 86
Pagination Using Infinite Results 87
Problem 87
Solution 87
Discussion 88
Displaying a Flash Notice/Failure Message 88
Problem 88
Solution 89
Discussion 91

Editing Text InPlace Using HTML 5 ContentEditable 91
Problem 91
Solution 91
Discussion 92
Displaying a Modal Dialog 92
Problem 92
Solution 93
Discussion 94
Displaying a Loading Spinner 94
Problem 94
Solution 94
Discussion 96
Chapter 9 Backend Integration with Ruby on Rails 97


12
Consuming REST APIs 97
Problem 97
Solution 97
Discussion 98
Implementing Client-Side Routing 99
Problem 99
Solution 99
Discussion 100
Validating Forms Server-Side 101
Problem 101
Solution 101
Discussion 103
Chapter 10 Backend Integration with Node Express 104
Consuming REST APIs 104

Problem 104
Solution 104
Discussion 106
Implementing Client-Side Routing 106
Problem 106
Solution 106
Discussion 108


13
The Story behind the Succinctly Series
of Books
Daniel Jebaraj, Vice President
Syncfusion, Inc.
taying on the cutting edge
As many of you may know, Syncfusion is a provider of software components for the
Microsoft platform. This puts us in the exciting but challenging position of always
being on the cutting edge.
Whenever platforms or tools are shipping out of Microsoft, which seems to be about
every other week these days, we have to educate ourselves, quickly.
Information is plentiful but harder to digest
In reality, this translates into a lot of book orders, blog searches, and Twitter scans.
While more information is becoming available on the Internet, and more and more books are
being published, even on topics that are relatively new, one aspect that continues to inhibit us is
the inability to find concise technology overview books.
We are usually faced with two options: read several 500+ page books or scour the web for
relevant blog posts and other articles. Just like everyone else who has a job to do and
customers to serve, we find this quite frustrating.
The Succinctly series
This frustration translated into a deep desire to produce a series of concise technical books that

would be targeted at developers working on the Microsoft platform.
We firmly believe, given the background knowledge such developers have, that most topics can
be translated into books that are between 50 and 100 pages.
This is exactly what we resolved to accomplish with the Succinctly series. Isn’t everything
wonderful born out of a deep desire to change things for the better?
The best authors, the best content
Each author was carefully chosen from a pool of talented experts who shared our vision. The
book you now hold in your hands, and the others available in this series, are a result of the
authors’ tireless work. You will find original content that is guaranteed to get you up and running
in about the time it takes to drink a few cups of coffee.
S


14
Free forever
Syncfusion will be working to produce books on several topics. The books will always be free.
Any updates we publish will also be free.
Free? What is the catch?
There is no catch here. Syncfusion has a vested interest in this effort.
As a component vendor, our unique claim has always been that we offer deeper and broader
frameworks than anyone else on the market. Developer education greatly helps us market and
sell against competing vendors who promise to “enable AJAX support with one click” or “turn the
moon to cheese!”
Let us know what you think
If you have any topics of interest, thoughts, or feedback, please feel free to send them to us at

We sincerely hope you enjoy reading this book and that it helps you better understand the topic
of study. Thank you for reading.











Please follow us on Twitter and “Like” us on Facebook to help us spread the
word about the Succinctly series!



15
About the Author
Frederik Dietz is a passionate software engineer with a focus on web-based and mobile
technologies. He currently works at the startup Protonet, shipping orange boxes made with love
in Hamburg.
Frederik has a blog at fdietz.de. You can reach him on Twitter via @fdietz or by email at




16
Preface
Introduction
Angular.js is an open-source JavaScript framework developed by Google. It gives JavaScript
developers a highly-structured approach to developing rich, browser-based applications which
leads to very high productivity.
If you are using Angular.js or considering it, this cookbook provides easy-to-follow recipes for

issues you are likely to face. Each recipe solves a specific problem and provides a solution and
in-depth discussion of it.
Code Examples
All code examples in this book can be found on GitHub.
How to Contact Me
If you have questions or comments, please get in touch with:
Frederik Dietz ()
Acknowledgements
Special thanks go to my English editor and friend Robert William Smales!
Thanks go to John Lindquist for his excellent screencast project egghead.io, Lukas Ruebbelke
for his awesome videos, and Matias Niemela for his great blog. And, of course, the whole
development team behind Angular.js!


17
Chapter 1 An Introduction to Angular.js
Including the Angular.js Library Code in an HTML
Page
Problem
You wish to use Angular.js on a web page.
Solution
In order to get your first Angular.js app up and running, you need to include the Angular
JavaScript file via script tag and make use of the ng-app directive:
<html>
<head>
<script src="
angularjs/1.0.4/angular.js">
</script>
</head>
<body ng-app>

<p>This is your first angular expression: {{ 1 + 2 }}</p>
</body>
</html>
You can check out a complete example on GitHub.
Discussion
Adding the ng-app directive tells Angular to kick in its magic. The expression {{ 1 + 2 }} is
evaluated by Angular and the result 3 is rendered. Note that removing ng-app will result in the
browser rendering the expression as is instead of evaluating it. Play around with the expression!
You can, for instance, concatenate Strings and invert or combine Boolean values.
For reasons of brevity, we will skip the boilerplate code in the following recipes.


18
Binding a Text Input to an Expression
Problem
You want user input to be used in another part of your HTML page.
Solution
Use Angular's ng-model directive to bind the text input to the expression:
Enter your name: <input type="text" ng-model="name"></input>
<p>Hello {{name}}!</p>
You can find the complete example on GitHub.
Discussion
Assigning "name" to the ng-model attribute and using the name variable in an expression will
keep both in sync automatically. Typing in the text input will automatically reflect these changes
in the paragraph element below.
Consider how you would implement this traditionally using jQuery:
<html>
<head>
<script src="
</head>

<body>
Enter your name: <input type="text"></input>
<p id="name"></p>

<script>
$(document).ready(function() {
$("input").keypress(function() {
$("#name").text($(this).val());
});
});
</script>

</body>
</html>



19
On document ready, we bind to the keypress event in the text input and replace the text in the
paragraph in the callback function. Using jQuery, you need to deal with document ready
callbacks, element selection, event binding, and the context of this. Quite a lot of concepts to
swallow and lines of code to maintain!
Responding to Click Events using Controllers
Problem
You wish to hide an HTML element on button click.
Solution
Use the ng-hide directive in conjunction with a controller to change the visibility status on
button click:
<html>
<head>

<script src="js/angular.js"></script>
<script src="js/app.js"></script>
<link rel="stylesheet" href="css/bootstrap.css">
</head>
<body ng-app>
<div ng-controller="MyCtrl">
<button ng-click="toggle()">Toggle</button>
<p ng-show="visible">Hello World!</p>
</div>
</body>
</html>
And the controller in js/app.js:
function MyCtrl($scope) {
$scope.visible = true;

$scope.toggle = function() {
$scope.visible = !$scope.visible;
};
}
You can find the complete example on GitHub.
When toggling the button, the "Hello World" paragraph will change its visibility.


20
Discussion
Using the ng-controller directive, we bind the div element, including its children, to the
context of the MyCtrl controller. The ng-click directive will call the toggle() function of our
controller on button click. Note that the ng-show directive is bound to the visible scope
variable and will toggle the paragraph's visibility accordingly.
The controller implementation defaults the visible attribute to true and toggles its Boolean

state in the toggle function. Both the visible variable and the toggle function are defined on
the $scope service, which is passed to all controller functions automatically via dependency
injection.
The next chapter will go into all the details of controllers in Angular. For now, let us quickly
discuss the Model-View-ViewModel (MVVM) pattern as used by Angular. In the MVVM pattern,
the model is plain JavaScript, the view is the HTML template, and the ViewModel is the glue
between the template and the model. The ViewModel makes Angular's two-way binding
possible where changes in the model or the template are in sync automatically.
In our example, the visible attribute is the model, but it could of course be much more
complex when, for example, retrieving data from a backend service. The controller is used to
define the scope which represents the ViewModel. It interacts with the HTML template by
binding the scope variable visible and the function toggle() to it.
Converting Expression Output with Filters
Problem
When presenting data to the user, you might need to convert the data to a more user-friendly
format. In our case, we want to uppercase the name value from the previous recipe in the
expression.
Solution
Use the uppercase Angular filter:
Enter your name: <input type="text" ng-model="name"></input>
<p>Hello {{name | uppercase }}!</p>
You can find the complete example on GitHub.


21
Discussion
Angular uses the | (pipe) character to combine filters with variables in expressions. When
evaluating the expression, the name variable is passed to the uppercase filter. This is similar to
working with the Unix bash pipe symbol where an input can be transformed by another program.
Also, try the lowercase filter!

Creating Custom HTML Elements with Directives
Problem
You wish to render an HTML snippet but hide it conditionally.
Solution
Create a custom directive which renders your Hello World snippet:
<body ng-app="MyApp">
<label for="checkbox">
<input id="checkbox" type="checkbox" ng-model="visible">Toggle me
</label>
<div show="visible">
<p>Hello World</p>
</div>
</body>
The show attribute is our directive, with the following implementation:
var app = angular.module("MyApp", []);

app.directive("show", function() {
return {
link: function(scope, element, attributes) {
scope.$watch(attributes.show, function(value){
element.css('display', value ? '' : 'none');
});
}
};
});
The browser will only show the "Hello World" paragraph if the checkbox is checked and will hide
it otherwise.
You can find the complete example on GitHub.



22
Discussion
Let us ignore the app module creation for now and look into it in more depth in a later chapter.
In our example, it is just the means to create a directive. Note that the show String is the name
of the directive which corresponds to the show HTML attribute used in the template.
The directive implementation returns a link function that defines the directive's behavior. It gets
passed the scope, the HTML element, and the HTML attributes. Since we passed the
visible variable as an attribute to our show directive, we can access it directly via
attributes.show. But since we want to respond to visible variable changes automatically,
we have to use the $watch service, which provides us with a function callback whenever the
value changes. In this callback, we change the CSS display property to either blank or none to
hide the HTML element.
This was just a small glimpse into what can be achieved with directives; there is a whole chapter
later that is dedicated to them, which goes into much more detail.


23
Chapter 2 Controllers
Controllers in Angular provide the business logic to handle view behavior; for example,
responding to a user clicking a button or entering some text in a form. Additionally, controllers
prepare the model for the view template.
As a general rule, a controller should not reference or manipulate the Document Object Model
(DOM) directly. This has the benefit of simplifying unit testing controllers.
Assigning a Default Value to a Model
Problem
You wish to assign a default value to the scope in the controller's context.
Solution
Use the ng-controller directive in your template:
<div ng-controller="MyCtrl">
<p>{{value}}</p>

</div>
Next, define the scope variable in your controller function:
var MyCtrl = function($scope) {
$scope.value = "some value";
};
You can find the complete example on GitHub.
Discussion
Depending on where you use the ng-controller directive, you define its assigned scope. The
scope is hierarchical and follows the DOM node hierarchy. In our example, the value expression
is correctly evaluated to some value, since value is set in the MyCtrl controller. Note that this
would not work if the value expression were moved outside the controller’s scope:


24
<p>{{value}}</p>

<div ng-controller="MyCtrl">
</div>
In this case {{value}} will simply not be rendered at all due to the fact that expression
evaluation in Angular.js is forgiving for undefined and null values.
Changing a Model Value with a Controller Function
Problem
You wish to increment a model value by 1 using a controller function.
Solution
Implement an increment function that changes the scope:
function MyCtrl($scope) {
$scope.value = 1;

$scope.incrementValue = function(increment) {
$scope.value += increment;

};
}
This function can be directly called in an expression; in our example we use ng-init:
<div ng-controller="MyCtrl">
<p ng-init="incrementValue(1)">{{value}}</p>
</div>
You can find the complete example on GitHub.
Discussion
The ng-init directive is executed on page load and calls the function incrementValue defined
in MyCtrl. Functions are defined on the scope very similarly to values but must be called with
the familiar parenthesis syntax.


25
Of course, it would have been possible to increment the value right inside of the expression with
value = value +1 but imagine the function being much more complex! Moving this function
into a controller separates our business logic from our declarative view template, and we can
easily write unit tests for it.
Encapsulating a Model Value with a Controller
Function
Problem
You wish to retrieve a model via a function (instead of directly accessing the scope from the
template) that encapsulates the model value.
Solution
Define a getter function that returns the model value:
function MyCtrl($scope) {
$scope.value = 1;

$scope.getIncrementedValue = function() {
return $scope.value + 1;

};
}
Then, in the template, we use an expression to call it:
<div ng-controller="MyCtrl">
<p>{{getIncrementedValue()}}</p>
</div>
You can find the complete example on GitHub.
Discussion
MyCtrl defines the getIncrementedValue function, which uses the current value and returns it
incremented by 1. One could argue that, depending on the use case, it would make more sense
to use a filter. But there are use cases specific to the controller’s behavior where a generic filter
is not required.

×