angularjs 教程8

In this step, you will learn how to create a layout template and how to build an app that has multiple views by adding routing.

  1. Reset the workspace to step 7.

    git checkout -f step-7
  2. Refresh your browser or check the app out on Angular's server.

  1. Reset the workspace to step 7.

    git checkout -f step-7
  2. Refresh your browser or check the app out on Angular's server.

Note that when you now navigate to app/index.html, you are redirected toapp/index.html#/phones and the same phone list appears in the browser. When you click on a phone link the stub of a phone detail page is displayed.

The most important changes are listed below. You can see the full diff on GitHub.

Multiple Views, Routing and Layout Template

Our app is slowly growing and becoming more complex. Before step 7, the app provided our users with a single view (the list of all phones), and all of the template code was located in theindex.html file. The next step in building the app is to add 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 theindex.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.

Application routes in Angular 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 implementdeep linking, which lets us utilize the browser's history (back and forward navigation) and bookmarks.

A Note About DI, Injector and Providers

As you noticed,dependency injection (DI) is at the core of AngularJS, so it's important for you to understand a thing or two about how it works.

When the application bootstraps, Angular creates an injector that will be used for all DI stuff in this app. The injector itself doesn't know anything about what$http or $route services do, in fact it doesn't 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 config functions. Thus you could not inject $routeProvider into PhoneListCtrl.

Angular 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, Angular modules don't 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.

The App Module

app/js/app.js:

 
 
  1. var phonecatApp = angular.module('phonecatApp', [
  2. 'ngRoute',
  3. 'phonecatControllers'
  4. ]);
  5.  
  6. phonecatApp.config(['$routeProvider',
  7. function($routeProvider) {
  8. $routeProvider.
  9. when('/phones', {
  10. templateUrl: 'partials/phone-list.html',
  11. controller: 'PhoneListCtrl'
  12. }).
  13. when('/phones/:phoneId', {
  14. templateUrl: 'partials/phone-detail.html',
  15. controller: 'PhoneDetailCtrl'
  16. }).
  17. otherwise({
  18. redirectTo: '/phones'
  19. });
  20. }]);

In order to configure our application with routes, we need to create a module for our application. We call this modulephonecatApp. Notice the second argument passed to angular.module:['ngRoute', 'phonecatControllers']. This array lists the modules thatphonecatApp depends on.

Above, we added angular-route.js to index.html. That's not all we need to do to be able to use it, however. We also have to addngRoute as a dependency of our app. To improve the organization of the app, we're going to move the controllers into their own file (as shown below), and call the modulephonecatControllers. By listing these two modules as dependencies ofphonecatApp, we can use the directives and services they provide.

Thus using the config API we request the $routeProvider to be injected into our config function and use the$routeProvider.when API to define our routes.

Our application routes are defined as follows:

  • The phone list view will be shown when the URL hash fragment is /phones. To construct this view, Angular will use thephone-list.html template and the PhoneListCtrl controller.

  • The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where:phoneId is a variable part of the URL. To construct the phone details view, angular will use thephone-detail.html template and the PhoneDetailCtrl controller.

We reused the PhoneListCtrl controller that we constructed in previous steps and we added a new, emptyPhoneDetailCtrl controller to the app/js/controllers.js file for the phone details view.

$route.otherwise({redirectTo: '/phones'}) triggers a redirection to/phones when the browser address doesn't match either of our routes.

Note the use of the :phoneId parameter in the second route declaration. The$route service uses the route declaration — '/phones/:phoneId' — as a template that is matched against the current URL. All variables defined with the: notation are extracted into the $routeParams object.

In order for our application to bootstrap with our newly created module we'll also need to specify the module name as the value of thengApp directive:

app/index.html:

 
 
  1. <!doctype html>
  2. <html lang="en" ng-app="phonecatApp">
  3. ...

Controllers

app/js/controllers.js:

 
 
  1. var phonecatControllers = 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.  
  9. $scope.orderProp = 'age';
  10. }]);
  11.  
  12. phonecatControllers.controller('PhoneDetailCtrl', ['$scope', '$routeParams',
  13. function($scope, $routeParams) {
  14. $scope.phoneId = $routeParams.phoneId;
  15. }]);

Again, note that we created a new module called phonecatControllers. For small AngularJS applications, it's common to create just one module for all of your controllers if there are just a few. For larger apps, you will probably want to create separate modules for each major feature of your app.

Because our example app is relatively small, we'll add all of our controllers to this module.

Template

The $route service is usually used in conjunction with the ngView directive. The role of the ngView directive is to include the view template for the current route into the layout template. This makes it a perfect fit for ourindex.html template.

Note: Starting with AngularJS version 1.2, ngRoute is in its own module and must be loaded by loading the angular-route.js file distributed with Angular. The easist way to load the file is to add a <script> tag to your index.html file as shown below.

app/index.html:

 
 
  1. <!doctype html>
  2. <html lang="en" ng-app="phonecatApp">
  3. <head>
  4. ...
  5. <script src="lib/angular/angular.js"></script>
  6. <script src="lib/angular/angular-route.js"></script>
  7. <script src="js/app.js"></script>
  8. <script src="js/controllers.js"></script>
  9. </head>
  10. <body>
  11.  
  12. <div ng-view></div>
  13.  
  14. </body>
  15. </html>

Note that we removed most of the code in the index.html template and replaced it with a single line containing a div with theng-view attribute. The code that we removed was placed into the phone-list.html template:

app/partials/phone-list.html:

 
 
  1. <div class="container-fluid">
  2. <div class="row-fluid">
  3. <div class="span2">
  4. <!--Sidebar content-->
  5.  
  6. Search: <input ng-model="query">
  7. Sort by:
  8. <select ng-model="orderProp">
  9. <option value="name">Alphabetical</option>
  10. <option value="age">Newest</option>
  11. </select>
  12.  
  13. </div>
  14. <div class="span10">
  15. <!--Body content-->
  16.  
  17. <ul class="phones">
  18. <li ng-repeat="phone in phones | filter:query | orderBy:orderProp" class="thumbnail">
  19. <a href="#/phones/{{phone.id}}" class="thumb"><img ng-src="{{phone.imageUrl}}"></a>
  20. <a href="#/phones/{{phone.id}}">{{phone.name}}</a>
  21. <p>{{phone.snippet}}</p>
  22. </li>
  23. </ul>
  24.  
  25. </div>
  26. </div>
  27. </div>
TODO!

We also added a placeholder template for the phone details view:

app/partials/phone-detail.html:

 
 
  1. TBD: detail view for {{phoneId}}

Note how we are using phoneId model defined in the PhoneDetailCtrl controller.

Test

To automatically verify that everything is wired properly, we wrote end-to-end tests that navigate to various URLs and verify that the correct view was rendered.

 
 
  1. ...
  2. it('should redirect index.html to index.html#/phones', function() {
  3. browser().navigateTo('../../app/index.html');
  4. expect(browser().location().url()).toBe('/phones');
  5. });
  6. ...
  7.  
  8. describe('Phone detail view', function() {
  9.  
  10. beforeEach(function() {
  11. browser().navigateTo('../../app/index.html#/phones/nexus-s');
  12. });
  13.  
  14.  
  15. it('should display placeholder page with phoneId', function() {
  16. expect(binding('phoneId')).toBe('nexus-s');
  17. });
  18. });

You can now rerun ./scripts/e2e-test.sh or refresh the browser tab with the end-to-end test runner to see the tests run, or you can see them running onAngular's server.

Experiments

  • 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 theorderProp 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.
* In PhoneCatCtrl, create a new model called " hero" with this.hero = 'Zoro'. In PhoneListCtrl let's shadow it with this.hero = 'Batman', and in PhoneDetailCtrl we'll use this.hero = "Captain Proton". Then add the <p>hero = {{hero}}</p> to all three of our templates ( index.html, phone-list.html, and phone-detail.html). Open the app and you'll see scope inheritance and model property shadowing do some wonders.

Summary

With the routing set up and the phone list view implemented, we're ready to go tostep 8 to implement the phone details view.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值