How to use this?

We have one virtual machine (Docker container to be precise) ready for you at each step of the official Angular tutorial.
Click this link to launch the docker container. Refresh the newly launched page if necessary, sometimes the virtual machine takes a little bit longer to start.


Because Angular’s double brackets conflict with Hexo, I am using [[[]]] instead of double brackets in this tutorial.

Step 0 - Bootstrapping

git checkout -f step-0

Read step 0 on Angular’s official site.

Try adding a new expression to the app/index.html that will do some math:

<p>1 + 2 = [[[ 1 + 2 ]]]</p>

Step 1 - Static Template

git checkout -f step-1

Read step 1 on Angular’s official site.

Try adding more static HTML to app/index.html. For example:

<p>Total number of phones: 2</p>

Step 2 - Angular Templates

git checkout -f step-2

Read step 2 on Angular’s official site.

Add another binding to app/index.html. For example:

<p>Total number of phones: [[[phones.length]]]</p>

Create a new model property in the controller and bind to it from the template. For example:

$ = "World";

Then add a new binding to app/index.html:

<p>Hello, [[[name]]]!</p>

Refresh your browser and verify that it says “Hello, World!”.

Create a repeater that constructs a simple table:

  <tr><th>row number</th></tr>
  <tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>[[[i]]]</td></tr>

Now, make the list 1-based by incrementing i by one in the binding:

  <tr><th>row number</th></tr>
  <tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>[[[i+1]]]</td></tr>

Make the unit test fail by changing the toBe(3) statement to toBe(4).

Step 3 - Filtering Repeaters

git checkout -f step-3

Read step 3 on Angular’s official site.

Display the current value of the query model by adding a [[[query]]] binding into the app/index.html template, and see how it changes when you type in the input box.

Let’s see how we can get the current value of the query model to appear in the HTML page title.

You might think you could just add the [[[query]]] to the title tag element as follows:

<title>Google Phone Gallery: [[[query]]]</title>

However, when you reload the page, you won’t see the expected result. This is because the “query” model lives in the scope, defined by the ng-controller=”PhoneListCtrl” directive, on the body element:

<body ng-controller="PhoneListCtrl">

If you want to bind to the query model from the title element, you must move the ngController declaration to the HTML element because it is the common parent of both the body and title elements:

<html ng-app="phonecatApp" ng-controller="PhoneListCtrl">

Be sure to remove the ng-controller declaration from the body element.

While using double curlies works fine within the title element, you might have noticed that for a split second they are actually displayed to the user while the page is loading. A better solution would be to use the ngBind or ngBindTemplate directives, which are invisible to the user while the page is loading:

<title ng-bind-template="Google Phone Gallery: [[[query]]]">Google Phone Gallery</title>

Add the following end-to-end test into the describe block within test/e2e/scenarios.js:

it('should display the current filter value within an element with id "status"',
function() {
expect(element('#status').text()).toMatch(/Current filter: \s*$/);


expect(element('#status').text()).toMatch(/Current filter: nexus\s*$/);

//alternative version of the last assertion that tests just the value of the binding

Refresh the browser tab with the end-to-end test runner (located at test/e2e/runner.html) to see the test fail. To make the test pass, edit the index.html template to add a div or p element with id “status” and content with the query binding, prefixed by “Current filter:”. For instance:

<div id="status">Current filter: [[[query]]]</div>

Add a pause() statement inside of an end-to-end test and rerun it. You’ll see the runner pause; this gives you the opportunity to explore the state of your application while it is displayed in the browser. The app is live! You can change the search query to prove it. Notice how useful this is for troubleshooting end-to-end tests.

Step 4 - Two-way Data Binding

git checkout -f step-4

Read step 4 on Angular’s official site.

In the PhoneListCtrl controller, remove the statement that sets the orderProp value and you’ll see that Angular will temporarily add a new “unknown” option to the drop-down list and the ordering will default to unordered/natural order.

Add an [[[orderProp]]] binding into the index.html template to display its current value as text.

Reverse the sort order by adding a - symbol before the sorting value:

Step 5 - XHRs & Dependency Injection

git checkout -f step-5

Read step 5 on Angular’s official site.

At the bottom of index.html, add a [[[phones | json]]] binding to see the list of phones displayed in json format.

In the PhoneListCtrl controller, pre-process the http response by limiting the number of phones to the first 5 in the list. Use the following code in the $http callback:

$scope.phones = data.splice(0, 5);

git checkout -f step-6

Read step 6 on Angular’s official site.

Replace the ng-src directive with a plain old src attribute. Using tools such as Firebug, or Chrome’s Web Inspector, or inspecting the webserver access logs, confirm that the app is indeed making an extraneous request to /app/%7B%7Bphone.imageUrl%7D%7D (or /app/[[[phone.imageUrl]]]).

The issue here is that the browser will fire a request for that invalid image address as soon as it hits the img tag, which is before Angular has a chance to evaluate the expression and inject the valid address.

Step 7 - Routing & Multiple Views

git checkout -f step-7

Read step 7 on Angular’s official site.

Try to add an [[[orderProp]]] binding to index.html, and you’ll see that nothing happens even when you are in the phone list view. This is because the orderProp model is visible only in the scope managed by PhoneListCtrl, which is associated with the div <ng-view> element. If you add the same binding into the phone-list.html template, the binding will work as expected.

Step 8 - More Templating

git checkout -f step-8

Read step 8 on Angular’s official site.

Using the Angular’s end-to-end test runner API, write a test that verifies that we display 4 thumbnail images on the Nexus S details page.

Step 9 - Filters

git checkout -f step-9

Read step 9 on Angular’s official site.

Let’s experiment with some of the built-in Angular filters and add the following bindings to index.html:

[[[ “lower cap string” | uppercase ]]]
[[[ {foo: “bar”, baz: 23} | json ]]]
[[[ 1304375948024 | date ]]]
[[[ 1304375948024 | date:”MM/dd/yyyy @ h:mma” ]]]
We can also create a model with an input element, and combine it with a filtered binding. Add the following to index.html:

<input ng-model="userInput"> Uppercased: [[[ userInput | uppercase ]]]

Step 10 - Event Handlers

git checkout -f step-10

Read step 10 on Angular’s official site.

Let’s add a new controller method to PhoneDetailCtrl:

$scope.hello = function(name) {
    alert('Hello ' + (name || 'world') + '!');

and add:

<button ng-click="hello('Elmo')">Hello</button>

to the phone-detail.html template.

Step 11 - REST and Custom Services

Read step 11 on Angular’s official site.
No experiments available.

Step 12 - Applying Animations

Read step 12 on Angular’s official site.
No experiments available.


Where does the material come from?

The official tutorial can be found at AngularJS’s official site.

Why are you doing this?

To help more people play with Angular. Some students are having a hard time with node.js or git, hence unable to play with the actual code of the official Angular tutorial.