English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

AngularJS Book - 1

Chapter 1. Getting Started with AngularJS 

AngularJS is a JavaScript framework that can help you write web applications quickly using less code. AngularJS is not a library, and it is not a collection of tools to help you achieve a specific goal. Rather, at its core, it is an entirely different way of thinking about writing web applications. As such, AngularJS has certain opinions about how you should be using it. Whereas in the vanilla JavaScript world, you are given a blank canvas on which to paint, AngularJS provides structure, organization and patterns. In order to master AngularJS, you must understand how to adhere to its guidelines and why the guidelines exist in the first place.


 Can I use AngularJS within Node.js?

Not presently. AngularJS is designed for client-side applications instead of server-side JavaScript.  I can't think of a reason to even want to do this other than perhaps server-side template rendering. Look for this feature in the future.


 

AngularJS is excellent at helping you write CRUD (Create, Read, Update and Delete) applications. Its tools, abilities and limitations lend itself to this common scenario. For example, the databinding ability of AngularJS is specifically tailored to allow for the extremely easy modification of models and data sets.

What AngularJS is not suited for is applications with a strong graphical component: ones that do many different heavy DOM modifications or games. Instead of just writing your jQuery, you are going to be wrapping it in a lot of code with little benefit.  Look at what's available in the AngularJS API. If you find yourself outside of those bounds most of the time, you may want to find another framework.  Of course AngularJS doesn't provide everything, and you can use 3rd party libraries with it, but if most of your application is 3rd party libraries, you may not want to work with AngularJS.

Do not attempt to write a general-purpose JavaScript library in AngularJS; it's meant for writing applications, not libraries. You can write modules to be reused by other AngularJS applications, but anything intended to be standalone or used with another library is not a good candidate.

Keep in mind that many web crawlers don't grok single-page web applications, and you may need to take extra steps to ensure a good search engine position. And it goes without saying, though I am going to say it: if your users don't have JavaScript turned on for some reason, AngularJS is useless. 


 What if I want to write a CRUD app with a lot of animation?

It's now pretty easy to do this, assuming you're not doing some kind of wacko graphical demo.  AngularJS, with the release of unstable version 1.1.4, now supports native animations.  You can define animations with CSS3, jQuery, or any other animation library.  As this is an unstable version and subject to change (more change than the stable version, anyway) I won't be covering the ins and outs of animation in this book; it is not in the API documentation yet.  However, the blog Year of Moo has a great overview of animations here: http://www.yearofmoo.com/2013/04/animation-in-angularjs.html.


A First Example

Our "Hello World" example will be a single-page weblog application. I realize this wheel has been invented countless times, but it gives us a great opportunity to highlight AngularJS' myriad functionalities. First, we'll dive into AngularJS' basic concepts with some easy examples. Next, we'll cover all of these concepts (and more!) in exhausting detail. Last, we'll look at some recipes for common (and not-so-common) use cases.

Let's create a couple of files on Plunker (http://plnkr.co) for our web-accessible directory. The first thing you will want to do is make a fake backend. What we can do is create a file called data.json and populate it with a JSON object. This object is large and would be rather silly to reprint here, so simply get it from the book's website, listed in the Preface. To create the file, if you are using Plunker, the "NEW..." link is what you're looking for.

Click that "NEW..." link again. We'll need an HTML file, index.html, and this is our "view":

<!DOCTYPE html>
<html ng-app="blog">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>My Important Blog</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min.js"
type="text/javascript" charset="utf-8"></script>
<script src="blog.js" type="text/javascript" charset="utf-8"></script>
</head>
<body ng-controller="MainCtrl">
<h1>My Important Blog</h1>

<div id="posts">
<h2>{{posts[0].title}}</h2>
<p bold-click>{{posts[0].body}}</p>
<p><em>posted on {{posts[0].date|date:'short'}} by {{posts[0].author}}</em></p>
</div>
</body>
</html>

This, as you can see, is visible to your application's user.  AngularJS-specific code is highlighted above in blue. Quickly, let's address the six bits:

  • ng-app="blog": This tells AngularJS we should start an application, and that application is in a module named "blog."
  • ng-controller="MainCtrl": This gives the controller "MainCtrl" control over the entire <body> of the application.
  • {posts[0].title}}: For our purposes, this displays the title of the first blog post.
  • bold-click: This makes the paragraph toggle between bold and normal font weight when clicked;  more on this later.
  • {{posts[0].body}}: Displays the body of the first post.
  • {{posts[0].date|date:'short'}}: Displays the first post's date and formats it nicely.
  • {{posts[0].author}}: Displays the author of the first post.

Behind this view sits one or more controllers, filters, and built-in or custom directives. Behind those live services and other components to be used across your application. Making up all of these is the module, which defines an application (or AngularJS library). We'll start by discussing the view.

The View

What's a view?  It's the presentation layer.  It's your markup (typically HTML).  It's what the user sees.  It is declarative--not imperative--it doesn't have control flow, per se.  When you are ready to implement an AngularJS application, I recommend implementing the view first, and working backwards from there.  Write directives that don't work, but use them how you would want--write the directives later.  Call functions in the scope and reference models that don't exist--then write the controllers later.  Because the view is so descriptive, it can essentially provide a blueprint for your actual code. 

You need to set some custom attributes--or more precisely, directives--to get your AngularJS application up and running.

  • ng-app on <html> tag: This is AngularJS' way of automatically bootstrapping the application. This ng-app call in particular initializes the blog module, and makes that the main module for the application.

 Bootstrapping

If you need to execute some code before AngularJS loads, you can skip putting ng-app in your view, and instead use angular.bootstrap() when ready.


  • ng-controller on <body> tag: This directive tells AngularJS to initialize the controller named MainCtrl and make it responsible for handling the scope under the <body> tag, inclusive.
  • Stuff in {{curly braces}}: For now, you want to understand that the stuff in double curly braces corresponds to AngularJS expressions, which in this case correspond to models, and those models are in the scope of our controller, MainCtrl. Curly braces mean, "evaluate this expression and output it as a string". If you are familiar with client-side templating languages and/or MVC frameworks, it's just some data coming out of a controller.Pipe (|) syntax: The pipe syntax in the reference to posts[0].date is a filter. The filter is the date filter, built-in to AngularJS. This filter takes the argument, posts[0].date, and applies a transformation against it. posts[0].date, in this case, is a JavaScript Date object. The Date object is transformed into a string. short is a shortcut for a "short date", which is a preconfigured format in the filter. This filter also allows you to output a Date object in basically any format you choose.

Next, we'll need some place to stuff our JavaScript. What follows includes an example of a model, controller, service and directive. Create blog.js:



The model is typically stored within the controller's scope object, and the controller communicates with the view.  The view contains directives, and the services provide functionality to controllers and directives, as shown here in blog.js:

(function () {
'use strict';
// Define our module. Yes, you need the empty array, or the module() function
// becomes a "getter": angular.module('blog') would RETRIEVE the
// module instead of DECLARE it.
var blog = angular.module('blog', []);
// Define our main controller.
// Our first example of dependency injection; $scope and Posts are
// automagically injected into the function by AngularJS.
blog.controller('MainCtrl', function ($scope, Posts) {

// Retrieve the posts from our "server". If this succeeds, we'll
// put the posts into our $scope and do some post-processing.
Posts.getPosts().success(function (data) {
$scope.posts = data.posts;
// Let's convert our timestamps to JS Date objects
var i = $scope.posts.length;
while (i--) {
$scope.posts[i].date = new Date($scope.posts[i].date * 1000);
} });
});
// Define our first service; Posts
// This service simply uses the built-in $http service to retrieve data from
// a static JSON store.
blog.service('Posts', function ($http) {
this.getPosts = function () {
return $http.get('data.json');
};
});

// Our first directive. Toggles boldface on node's contents when clicked.
// We'll cover exactly what a directive is a little later.
blog.directive('boldClick', function() {
return function(scope, element) {
var bold = false;
element.bind('click', function() {
if (bold) {
element.css('font-weight', 'normal');
} else {
element.css('font-weight', 'bold');
}
bold = !bold;
});
};
});
})();

Let's address some of these things in brief before moving on:

  • First, let's define a module "blog".  This module, when combined with the ng-app="blog" attribute in the markup, functions as our main application.
  • We define a controller called "MainCtrl", which provides business logic to the view under the <body> tag inclusive.  This uses dependency injection (DI) to pass in the $scope and Posts parameters.
  • When MainCtrl is run, it grabs some post data from the Posts service, and does a bit of post-processing on the date objects.
  • We define a service, Posts, also using DI.  This provides one function, getPosts(), which we call from the MainCtrl controller.  It uses the built-in $http service to contact a server and return data.
  • Finally, we define a directive called boldClick, which provides some fancy functionality to the view; when you click on whatever element it's attached to, it will toggle between bold and normal font weight.

The Module

Our module is called, surprisingly, blog. Declaring a module is very simple as you will see, but note that you must specify an array as the second parameter to the module() call. The contents of this array will be a list of modules to include. In our case, we are not including any other modules, so this is an empty array. Once we have declared a module and assigned it to a variable, we can use that variable to define other components within the module.

You must understand the difference between the module declaration:

angular.module('foo', []);

And the module reference:

angular.module('foo');

Both return a module object, but the first defines a module and the other retrieves a module. If you start out your code by using the second, your application won't do anything except spew some errors at you.

If you want to include other modules (which run all module code and make any injectable components in the included module available to your module), specify them by name--as strings--within the array parameter of the module definition. You cannot include modules on-the-fly.

So, if you want to use the ngResource module supplied by the AngularJS folks, you must link to the .js file in a <script> tag, then load it like so, including a module:

angular.module('myModule', ['ngResource']);

Now you can use the $resource service, which is not available by default. Some you may want include are ngMock for testing, ngCookies for cookie manipulation, and ngSanitize for string/HTML sanitization and post-processing, among others. None of these are included by default so you must refer to their corresponding .js files in some manner.

There are some perhaps less-known, but very useful, module functions that we haven't covered: 

  • value(): Allows you to set some "global" (injectable) values within an object; good for "defaults".
  • constant(): Much like value, except the object you supply is expected to not change, and is available to the config function.
  • config(): Inject providers in here to configure them, or work with constants, or otherwise do things during the initialization phase. Configuring default headers for the $http service or setting up routes is usually done here.
  • run(): Run an arbitrary function after initialization is complete; if you need to configure a service/factory/etc. that is not a provider, or set something using $rootScope, this is a good place to do it.
Here is an example of value() and run():
angular.module('myModule')
.value('foo', {bar: 'baz'})
.run(function(foo) { .. });

And here is an example of constant() and config():

angular.module('myModule')
.constant('foo', {bar: 'baz'})
.config(function(foo) { .. });
 
Note that you can inject a constant into a run() function, but you can't inject a value into a config() function.
 
How do we know when to create a module? In practice, I've found that generally I want one module per (HTML) page. If you want to automatically bootstrap your AngularJS application, which you generally do, you will use the ngApp directive. This directive can only be used once per HTML page. Now, that doesn't mean that you shouldn't bundle common functionality between the pages in another module--in fact, you should. It's simply that each page will require a module to be the module for that page, and it must require all other modules it needs.  Given that you may mostly be writing single-page apps in AngularJS, you will typically have one module for your "app" and others for reusable components.

Another situation in which you should create a module is if you have a collection of like components that are reusable across disparate single-or-multi-page applications. For example, you may need the $http service to behave in a particular way across all of your system's applications, then it's a great idea to create a module that all of those applications can include.

I've found a neat way to use modules is to say: overwrite a built-in directive or two within one, then include that module only when necessary. Also, supplying test fixture functionality to QUnit (or any other non-Jasmine framework) is another good use case, as ngMock only supplies some fixtures if you are using Jasmine.

The Controller

MainCtrl is our controller. As discussed in The View chapter, it has control of the entire page, since it's attached to the <body> tag. OK, that's great, but what is $scope, what is Posts, and more importantly, where do they come from? The short answer is that $scope is a Scope object and Posts is a service, and they arrive in your controller via dependency injection. The long answer is that this is dependency injection (DI) at work. Dependency injection is a core concept of AngularJS and aids unit testing and code organization. Components arriving in a function via DI are either included components of AngularJS, or custom components you write yourself.

So what are we doing in here? When the controller is initialized, we execute the getPosts() function in the Posts service, wait for its success, put its response into our $scope object, and perform some post-processing on said response. The getPosts() function returns a promise. If you are familiar with jQuery.Deferred() or Kristopher Kowal's q, then you understand promises. We'll go into promises in detail later. For now, know that getPosts() returns a promise, and at some point it will be resolved, and when it is, the success callback is executed:

 

We can see that in this controller we're assigning a value to $scope.posts. When we do this, the view is alerted and the expressions involving posts are (re-)evaluated. This is an example of data binding. A portion of a view is data-bound to a model, and when either one changes, they are both synchronized.

Note: Initially when AngularJS loads, the value of $scope.posts (and thus posts in the view) is undefined. It is only set after our call to getPosts() succeeds! Remember this when you are trying to console.log($posts[0].title) and you get a TypeError.

You may have put together the connection that anything in your controller assigned to $scope.foo is available in the view as simply foo. This holds true for any member of the $scope, including objects, arrays, primitives and functions. You will never have to refer to $scope in the view, because it is implied, and will correspond to the scope of the nearest controller (as defined by the ng-controller directive) or directive if that directive affects the scope.

The Service

Posts is a service. It's a simple service and provides one function, getPosts(). You see that the built-in $http service is available here, and we use it to request data from our fake server, data.json. The $http service is a wrapper around basic Ajax functionality, and can be used to make any type of HTTP request. Consider it analogous to jQuery.ajax(). We're firing off a GET request and returning from getPosts() the object generated by this request. As discussed above, this object is a promise. The promise returned by requests made by $http are unique in that you may use success() and/or error() when resolving them, as we do in the controller.

It's important to note that a service is a singleton. That means only one instance of the service will be available across your entire application. And I do mean instance; AngularJS instantiates the service when it is first requested, and we use this to put members into it. A factory is also a singleton, but it is not instantiated (no new keyword involved). More about the difference between factories and services later.

The Directive

What is a directive? It's an HTML attribute, tag, or comment that can execute any behavior (including modifying the DOM) based on its attributes and often times, scope. There are lots of directives included within AngularJS to do many common tasks, and they all begin with ng ("ng" sounds kind of like "Ang" in "AngularJS" so that's why they chose the prefix). For example, the ngShow directive will allow you to show or hide (using the CSS display property) a block of HTML, based on the truthiness of a model or executed function.

The scope, markup, and services all go into a directive, and out comes markup with JS functionality (events).

 


 ng-show vs. ngShow

ngShow is the proper name for the directive; these should always be camelCased, and you are encouraged to name your own directives this way. ng-show is one possibility of how you would reference it in a view. Also supported are ng:show, ng_show, x-ng-show and data-ng-show. Whew!


Why create a custom directive? There are many reasons. Say you had a custom widget--some HTML fragment--that should accept some parameters and render itself based upon those parameters. For example, a directive that renders a <ul> based on a comma-separated list of values. Or you could apply some sort of fancy animation effect to an HTML node. Or you could take a jQuery plugin (like Twitter Bootstrap's tooltip()) and put your tooltip contents directly into the HTML instead of having to write another line of JavaScript. The possibilities, while perhaps not endless, are many.

Logic? In _my_ markup? It's more common than you think.

The separation of logic from the view is a common, and valid, concern. With AngularJS, you are going to be putting logic into your markup, perhaps much more than you would with any other JavaScript framework. Some people (maybe you!) find this incredibly icky. And that's OK. However, AngularJS is designed with this paradigm in mind. The rationale is fairly simple though: if you use a templating language (server-side, client-side, it doesn't matter), you are going to have logic in your view. Designers are going to see it if they are working with your markup. AngularJS does not pretend that this isn't happening--instead, it embraces logic in the view. It's just that the logic takes place in a directive, which is written as part of the markup itself. It is truly a powerful way of developing for the web, and in the end will save you many lines of code. So if you are unsure, take the plunge and decide for yourself:



Directives are what truly sets AngularJS apart from other frameworks. It allows you an unparalleled degree of flexibility and power when writing your markup. But to be the boss, there is a cost. And that cost is a steep learning curve.

I'm not going to pretend it is straightforward to write a directive. Even the simplest directives may seem strange or need many configuration options. We'll get our hands downright filthy with directives later, but for now, let's look at this relatively simple one.

When you have a HTML fragment like <span bold-click>bar</span>, this directive will be processed. When someone clicks on "bar", it will become boldface; when they click again it will become normal weight.  

Again, we'll go into specifics later about the syntax and certain techniques that we can use when writing directives. For now, just know that it all starts with markup, and that you can make that markup do just about anything.

Conclusion

From here we'll discover the components and concepts that make up an entire AngularJS application, in more detail. AngularJS has a learning curve, as it's a new way of thinking about building web applications, and it is important to understand the core tools it provides and how to use them.

In the next chapter we'll be introduced to the six core components, or building blocks, that make up AngularJS applications.

 

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.