In this step, you will learn how to create a layout template and build an application with multiple views through the routing function.
Please reset the working directory:
git checkout -f step-7
Notice that now when you go to app/index.html, you will be redirected to app/index.html#/phones and the same phone list is displayed in the browser. When you click on a mobile link, a mobile phone details list is also displayed.
The most important differences between Step 6 and Step 7 are listed below. You can see the complete difference in GitHub.
Multi-view, routing and layout templates
Our applications are slowly developing and becoming more complex. Before step 7, the application only provides our users with a simple interface (a list of all phones), and all the template codes are located in the index.html file. The next step is to add a page that displays the details of each phone in our list.
To add the details view, we can expand the index.html to include the template code for both views at the same time, but this will quickly cause us great trouble. Instead, we need to convert the index.html template into a "layout template". This is a common template for us to apply all views. Other "local layout templates" are then filled in according to the current "routing", thus forming a complete view to display to the user.
The route applied in AngularJS is declared through $routeProvider, which is the provider of the $route service. This service allows easy integration of controllers, view templates and the URL of the current browser. By applying this feature, we can implement deep links, which allows us to use browser history (backback or forward navigation) and bookmarks.
About Dependency Injection (DI), Injectors and Service Providers
As you learned from the previous one, dependency injection is a core feature of AngularJS, so you have to know a little bit about how this guy works.
When the application boots, AngularJS will create an injector, which will be needed by all the services that are injected after our application. This injector itself does not know what $http and $route do. In fact, unless it is configured during the module definition, it does not know the existence of these services at all. The only responsibility of the injector is to load the specified service modules, register all defined service providers in these modules, and inject dependencies (services) into a specified function when needed. These dependencies are instantiated by their provider "lazy" (loaded if needed).
A provider is an object that provides (creates) a service instance and provides an API interface externally. It can be used to control the creation and runtime behavior of a service. For the $route service, $routeProvider provides an API interface to the outside world, allowing you to define routing rules for your application through the API interface.
The AngularJS module solves the two problems of removing global state from the application and providing methods to configure the injector. Unlike AMD or require.js (two libraries that are not AngularJS), the AngularJS module does not try to solve the problem of script loading order and lazy script loading. These goals have nothing to do with the problems AngularJS needs to solve, so these modules can coexist to achieve their respective goals.
App module
app/js/app.js
angular.module('phonecat', []). config(['$routeProvider', function($routeProvider) { $routeProvider. when('/phones', {templateUrl: 'partials/phone-list.html', controller: PhoneListCtrl}). when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl}). otherwise({redirectTo: '/phones'});}]);In order to configure routing for our application, we need to create a module for the application. We call this module phonecat, and by using configAPI, we request that $routeProvider be injected into our configuration function and use $routeProvider.whenAPI to define our routing rules.
Note that during the injector configuration phase, the provider can also be injected at the same time, but once the injector is created and the service instance is started, they will no longer be retrieved by the outside world.
Our routing rules are defined as follows
When the URL map segment is /phones, the mobile phone list view will be displayed. To construct this view, AngularJS uses the phone-list.html template and the PhoneListCtrl controller.
When the URL map segment is /phone/:phoneId, the phone details view is displayed. Here: phoneId is the variable part of the URL. In order to construct a detailed view of the phone, AngularJS uses the phone-detail.html template and the PhoneDetailCtrl controller.
We reuse the PhoneListCtrl controller we created before, and at the same time we add a new PhoneDetailCtrl controller to the detailed view of the phone and store it in the app/js/controllers.js file.
The $route.otherwise({redirectTo: '/phones'}) statement causes the redirect to /phones to be triggered when the browser address cannot match any of our routing rules.
Note that in the second route declaration: the use of the phoneId parameter. The $route service uses the routing declaration /phones/:phoneId as a template that matches the current URL. All variables declared with the : symbol (here the variable is phones) will be extracted and stored in the $routeParams object.
In order for our application to guide our newly created module, we also need to specify the name of the module on the value of the ngApp directive:
app/index.html
<!doctype html><html lang="en" ng-app="phonecat">...
Controller
app/js/controllers.js
...function PhoneDetailCtrl($scope, $routeParams) { $scope.phoneId = $routeParams.phoneId;}//PhoneDetailCtrl.$inject = ['$scope', '$routeParams'];template
The $route service is usually used with the ngView directive. The role of the ngView directive is to load the corresponding view template into the layout template for the current route.
app/index.html
<html lang="en" ng-app="phonecat"><head>... <script src="lib/angular/angular.js"></script> <script src="js/app.js"></script> <script src="js/controllers.js"></script></head><body> <div ng-view></div></body></html>
Note that we removed most of the code in the index.html template, and we only placed a <div> container, which has the ng-view attribute. The code we deleted is now placed in the phone-list.html template:
app/partials/phone-list.html
<div> <div> <div> <!--Sidebar content--> Search: <input ng-model="query"> Sort by: <select ng-model="orderProp"> <option value="name">Alphabetical</option> <option value="age">Newest</option> </select> </div> <div> <!--Body content--> <ul> <li ng-repeat="phone in phones | filter:query | orderBy:orderProp"> <a href="#/phones/{{phone.id}}"><img ng-src="{{phone.imageUrl}}"></a> <a href="#/phones/{{phone.id}}">{{phone.name}}</a> <p>{{phone.snippet}}</p> </li> </ul> </div> </div></div>At the same time, we add a placeholder template to the mobile phone details view.
app/partials/phone-detail.html
TBD: detail view for {{phoneId}}
Notice that our layout template has not added the PhoneListCtrl or PhoneDetailCtrl controller properties!
test
To automatically verify that everything is well integrated, we need to write some end-to-end tests, navigate to different URLs and verify that the correct view is rendered.
... it('should redirect index.html to index.html#/phones', function() { browser().navigateTo('../../app/index.html'); expect(browser().location().url()).toBe('/phones'); });... describe('Phone detail view', function() { beforeEach(function() { browser().navigateTo('../../app/index.html#/phones/nexus-s'); }); it('should display placeholder page with phoneId', function() { expect(binding('phoneId')).toBe('nexus-s'); }); });You can now refresh your browser and run the end-to-end test again, or you can run it on AngularJS server.
practise
Try adding an {{orderProp}} binding to index.html and nothing changes when you are on the list view of your phone. This is because the orderProp model is only visible under the scope of PhoneListCtrl management, which is related to the <div ng-view> element. If you add the same binding to the phone-list.html template, the binding will be rendered as you imagined.
Summarize
After setting up the route and implementing the mobile phone list view, we can already proceed to step 8 to implement the mobile phone details view.
The above is to sort out the AngularJS routing and multi-view information, and we will continue to add relevant knowledge in the future. Thank you for your support for this site!