aboutsummaryrefslogtreecommitdiffstats
path: root/docs/tutorial.step_07.ngdoc
blob: 3c715a79a5e769f3734f1c1b3ce5aa83178539f3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
@workInProgress
@ngdoc overview
@name Tutorial: Step 7
@description
<table id="tutorial_nav">
<tr>
<td id="previous_step">{@link tutorial.step_00 Previous}</td>
<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-7/app Live Demo
}</td>
<td id="tut_home">{@link tutorial Tutorial Home}</td>
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code
Diff}</td>
<td id="next_step">{@link tutorial.step_00 Next}</td>
</tr>
</table>

Our app is slowly growing and becoming more complex. Up until now, the app provided our users with
just one view (the list of all phones), and all of our template code was located in the
`index.html` file.  The next step in building our app is the addition of a view that will show
detailed information about each of the devices in our list.  

To add the detailed view, we could expand the `index.html` file to contain template code for both
views, but that would get messy very quickly. Instead, we are going to turn the `index.html`
template into what we call a "layout template". This is a template that is common for all views in
our application.   Other "partial templates" are then included into this layout template depending
on the current "route"  the view that is currently displayed to the user.

Similarly as with templates, angular also allows for controllers and scopes managed by these
controllers to be nested. We are going to create a "root" controller called `PhoneCatCtrl`, which
will contain the declaration of routes for the application.

Application routes in angular are declared via the {@link angular.service.$route $route} service.
This services makes it easy to wire together controllers, View templates, and the current URL
location in the browser.  Using this feature we can implement {@link
http://en.wikipedia.org/wiki/Deep_linking deep linking}, which lets us utilize the browser's
History, and Back and Forward browser navigation.

We'll use the $route service to declare that our application consists of two different views: one
view presents the phone listing, and the other view presents the details for a particular phone.
Each view will have the template stored in a separate file in the `app/partials/` directory.
Similarly each view will have a controller associated with it. These will be stored in the
existing `app/js/controllers.js` file.

The `$route` service is usually used in conjunction with the {@link angular.widget.ng:view
ng:view} widget. The role of the `ng:view` widget is to include the view template for the current
route into the layout template, which makes it a perfect fit for our `index.html` template.

For now we are going to get all the routing going, and move the phone listing template into a
separate file. We'll save the implementation of the phone details View for the next step.

__`app/index.html`:__
<pre>
...
<body ng:controller="PhoneCatCtrl">

  <ng:view></ng:view>

  <script src="lib/angular/angular.js" ng:autobind></script>
  <script src="js/controllers.js"></script>
</body>
</html>
</pre>

__`app/partials/phone-list.html`:__
<pre>
<ul class="predicates">
  <li>
    Search: <input type="text" name="query"/>
  </li>
  <li>
    Sort by:
    <select name="orderProp">
      <option value="name">Alphabetical</option>
      <option value="age">Newest</option>
    </select>
  </li>
</ul>

<ul class="phones">
  <li ng:repeat="phone in phones.$filter(query).$orderBy(orderProp)">
    <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
    <a href="#/phones/{{phone.id}}" class="thumb"><img ng:src="{{phone.imageUrl}}"></a>
    <p>{{phone.snippet}}</p>
  </li>
</ul>
</pre>

__`app/partials/phone-list.html`:__
<pre>
TBD: detail view for {{params.phoneId}}
</pre>

__`app/js/controller.js`:__
<pre>
/* App Controllers */

function PhoneCatCtrl($route) {
  var self = this;

  $route.when('/phones',
              {template: 'partials/phone-list.html',   controller: PhoneListCtrl});
  $route.when('/phones/:phoneId',
              {template: 'partials/phone-detail.html', controller: PhoneDetailCtrl});
  $route.otherwise({redirectTo: '/phones'});

  $route.onChange(function(){
    self.params = $route.current.params;
  });

  $route.parent(this);
}

//PhoneCatCtrl.$inject = ['$route'];


function PhoneListCtrl($xhr) {
  var self = this;

  $xhr('GET', 'phones/phones.json', function(code, response) {
    self.phones = response;
  });

  self.orderProp = 'age';
}

//PhoneListCtrl.$inject = ['$xhr'];


function PhoneDetailCtrl() {}
</pre>

## Discussion:

* __The View.__ Our View template in `index.html` has been reduced down to this:
`<ng:view></ng:view>`.  As described above, it is now a "layout template". We added the following
two new View templates:

    * `app/partials/phone-list.html` for the phone list.  The phone-list view was formerly our
    main view. We simply moved the code from `index.html` to here.

    * `app/partials/phone-detail.html` for the phone details (just a placeholder template for now).

* __The Controller(s).__ We now have a new root controller (`PhoneCatCtrl`) and two
sub-controllers (`PhoneListCtrl` and `PhoneDetailCtrl`). These inherit the model properties and
behavior from the root controller. 

    * __`$route.`__ The root controller's job now is to set up the `$route` configuration:

        * When the fragment part of the URL in the browser ends in "/phones", `$route` service
        grabs the `phone-list.html` template, compiles it, and links it with a new scope that is
        controlled by our `PhoneListCtrl` controller. 

        * When the URL ends in "/phones/:phoneId", `$route` compiles and links the
        `phone-detail.html` template as it did with `phone-list.html`. But note the use of the
        `:phoneId` parameter declaration in the `path` argument of `$route.when()`: `$route`
        services provides all the values for variables defined in this way as
        `$route.current.params` map. In our route, `$route.current.params.phoneId` always holds
        the current contents of the `:phoneId` portion of the URL. We will use the `phoneId`
        parameter when we fetch the phone details in Step 8.

        * Any other URL fragment gets redirected to `/phones`.

    * __Controller/Scope inheritance.__ In the function passed into `$route`'s `onChange()`
    method, we copied url parameters extracted from the current route to the `params` property in
    the root scope. This property is inherited by child scopes created for our view controllers
    and accessible by these controllers.

  * __Tests.__ To automatically verify that everything is wired properly, we write end to end
  tests that navigate to various URLs and verify that the correct view was rendered.

<table id="tutorial_nav">
<tr>
<td id="previous_step">{@link tutorial.step_00 Previous}</td>
<td id="step_result">{@link  http://angular.github.com/angular-phonecat/step-7/app Live Demo
}</td>
<td id="tut_home">{@link tutorial Tutorial Home}</td>
<td id="code_diff">{@link https://github.com/angular/angular-phonecat/compare/step-6...step-7 Code
Diff}</td>
<td id="next_step">{@link tutorial.step_00 Next}</td>
</tr>
</table>