AngularJS 路由:ng-route 与 ui-router

AngularJS HTML JavaScript MVC 模板 路由

AngularJSng-route模块为控制器和视图提供了[Deep-Linking]URL。 通俗来讲,ng-route模块中的$routeService监测$location.url()的变化,并将它映射到预先定义的控制器。也就是在客户端进行URL的路由。 下面首先给出$route的使用示例,然后引入一个更加强大的客户端路由框架ui-router

Angular 路由

在APP中定义多个页面的控制器,并给出对应的模板。然后$routeProvider进行配置,即可将URL映射到这些控制器和视图。 首先定义一个基本的Angular APP,并引入ngRoute

Angular$routeService在ngRoute模块里。需要引入它对应的javascript文件,并在我们的APP里ngRoute添加为模块依赖(如何添加模块依赖?)。

var app = angular.module('ngRouteExample', ['ngRoute'])
    .controller('MainController', function($scope) {
    })
    .config(function($routeProvider, $locationProvider) {
      $routeProvider
          .when('/users', {
              templateUrl: 'user-list.html',
              controller: 'UserListCtrl'
          })
          .when('/users/:username', {
              templateUrl: 'user.html',
              controller: 'UserCtrl'
          });

        // configure html5
        $locationProvider.html5Mode(true);
    });

上述代码中,$routeProvider定义了两个URL的映射:/users使用user-list.html作为模板,UserListCtrl作为控制器; /users/:username则会匹配类似/users/alice之类的URL,稍后你会看到如何获得:username匹配到的值。先看首页的模板:

HTML5Mode: 服务器端路由和客户端路由的URL以#分隔。例如/foo/bar#/users/alice,Angular通过操作锚点来进行路由。 然而html5Mode(true)将会去除#,URL变成/foo/bar/users/alice(这需要浏览器支持HTML5的,因为此时Angular通过pushState来进行路由)。 此时服务器对所有的客户端路由的URL都需要返回首页(/foo/bar)视图,再交给Angular路由到/foo/bar/users/alice对应的视图。

<div ng-controller="MainController">
  Choose:
  <a href="users">user list</a> |
  <a href="users/alice">user: alice</a>

  <div ng-view></div>
</div>

注意到模板文件中有一个div[ng-view],子页面将会载入到这里。

AngularJS 模块化与依赖注入

AngularJS HTML JavaScript MVC 模块化 依赖注入 工厂方法 构造函数

AngularJS使用模块化的组织方式,和依赖注入的设计。这使得模块之间耦合度较低,模块更容易复用。同时支持声明式的编程风格。 在你创建Angular Module 或者 Service 之前,首先需要了解一下 Angular Module 和 Service 的工作方式。

模块概念

在Angular中,一个Module通常对应一个js文件,其中可以包括Controller、Service、Filter、Directive等。 下面我们声明一个模块:helloApp,并在其中声明一个Controller:worldCtrl,一个Directive:customer,和一个Filter:count

接着在模板中,使用上面声明的helloApp模块作为Angular APP:

  • Module依赖:在声明helloApp模块时,需要给出依赖模块的列表。同时这些模块对应的JS需要在HTML中加以引入。在helloApp中可以直接使用依赖模块中声明的Service、Directive、Filter。
  • Service依赖:在声明Controller、Service、Directive、Filter的工厂方法中,把依赖的Service直接放到参数列表,Angular Injector会为你生成这些Service的实例。

AngularJS 数据绑定与 $digest 循环

AngularJS DOM HTML HTTP JavaScript MVC WebSocket 事件 回调函数 数据绑定

数据绑定可以说是AngularJS最大的特色。在Angular中,视图和模型的数据不仅是双向绑定的,并而且是实时的。 使用Angular可以做到良好的甚至是神奇的用户体验,例如用户在输入表单的过程中实时地提示输入有误或者输入正确。

双向绑定

下图是模板引擎中常见的单向数据绑定:

通常在服务器端,将数据模型和模板结合,生成视图。当视图中的数据发生改变时,数据模型不会自动更新;模型发生改变时,视图也不会自动刷新。 因此开发者不得不写大量的代码来同步视图和模型。例如:

  • 视图->模型:绑定DOM事件来监听视图的改变,进而通过javascript函数来同步数据模型,更改javascript对象,或者发送HTTP请求到后台。
  • 模型->视图:模型改变时通过jQuery操作来更新DOM。如果数据模型在后台,可能还需要websocket之类的推送机制。

而Angular提供了双向的数据绑定,我们可以在Angular Controller的$scope中声明数据模型,在模板中进行绑定。 Angular会自动添加DOM事件,并在$scope发生改变时自动进行DOM操作。下面是Angular双向绑定的MVT关系示意图:

图片来源: https://docs.angularjs.org/guide/databinding

AngularJS Resource:与 RESTful API 交互

AngularJS HTML HTTP JavaScript 数组

REST(表征性状态传输,Representational State Transfer)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。RESTful风格的设计不仅具有更好的可读性(Human Readable),而且易于做缓存以及服务器扩展(scalability)。REST风格体现在URL设计上:

  • 每个URL对应一个资源
  • 对资源的不同操作对应于HTTP的不同方法
  • 资源表现形式(representation)通过AcceptContent-Type指定

AngularJS提供了$resourceService来更方便地与RESTful服务器API进行交互,可以方便地定义一个REST资源,而不必手动所有的声明CRUD方法。

参考文档: https://docs.angularjs.org/api/ngResource/service/$resource

Resource Factory

$resourceService定义在ngResourceModule中,需要在你的HTML中引入这个Module对应的JS,同时在你的APP中添加这样一个依赖:

var app = angular.module('helloApp, ['ngResource']);

然后为资源建立一个Factory:

app.factory('Notes', ['$resource', function($resource) {
    return $resource('/notes/:id');
}]);

当然,你也可以不把$esource的实例放到Factory里,直接在控制器中存起来:var Notes = $resource('/notes/:id)

AngularJS HTTP Service

AJAX AngularJS HTTP JavaScript Promise 链式调用

$httpAngularJS提供的一个核心Service,通过浏览器的XMLHttpRequest或JSONP与服务器进行交互。 这是一个非常常用的Service,类似于jQuery提供的AJAX。与jQuery类似,$http也采用了deferred/promise模式,不同的是Angular的deferred/promise模式是由Angular的$qService来提供的。

在Angular应用中尽量使用$http而不是jQuery函数来进行AJAX请求,因为$http会在响应到达时触发Angular更新视图($digest循环)。 与此同时,$http还可以通过$httpBackend来Mock和单元测试。

$http文档参见: https://docs.angularjs.org/api/ng/service/$http

基本使用

如果你熟悉了jQuery的AJAX操作,那么使用$http只是重新记住几个方法名而已。把done, fail, always换成success, error, finally

$http.get('/someUrl')
    .success(function(data, status, headers, config){
    // GET成功时被回调
    })
    .error(function(data, status, headers, config){
    // GET失败时被回调
    });

$http方法返回的是一个由$qService提供的Promise对象,事实上Promise对象有三个通用方法:then, catch, finally。 上述的successerror$http提供的两个额外的方法。Promise的三个方法参数如下:

then(successCallback, errorCallback, notifyCallback);
catch(errorCallback);
finally(callback, notifyCallback);

Promise方法是可以链式调用的。

配置$httpProvider

$http Service定义在ng Module里,由$httpProvider提供。于是我们可以通过设置$httpProvider来配置$http的行为。比如,给HTTP请求插入一个拦截器:

someModule.config(['$httpProvider', function($httpProvider){
    $httpProvider.interceptors.push(function($q, dependency1, dependency2){
        return {
            request: function(config){
                // 这里可以调整HTTP请求的配置
                return config;
            },
            response: function(response){
                // 这里能拿到响应对象,当然也可以更改它
                return response;
            }
        }
    });
}]);

还可以通过设置$httpProviderdefaults属性来进行请求/响应的转换(transformRequest, transformResponse)、设置请求的HTTP头字段(headers)。

更多信息,请参考$httpProvider文档: https://docs.angularjs.org/api/ng/provider/$httpProvider

AngularJS 表单(ng-form)验证

AngularJS CSS DOM HTML JavaScript MVC 模板 表单 模块化 数据绑定 正则表达式

在HTML中,用户通过input, select, textarea等元素进行输入,我们通常用表单来包装和管理这些控件。客户端表单验证非常重要,可以及时地为用户提供表单验证信息。但客户端表单验证只是为了增强用户体验,服务器端验证仍然是必要的。

AngularJS最大的特点便是数据绑定。利用Angular在客户端脚本中构建MVC框架,Model和View之间可以实现双向绑定。因此AngularJS的表单验证可以做到实时的用户反馈

事实上,正是因为实时的用户反馈这个神奇的特性,我们团队在 http://tianmaying.com 中也继续引入了AngularJS,尽管此时我们对单页应用已经不感兴趣。

一个简单的表单

Angular是模块化的,每个APP都是一个Angular Module。我们知道Module下可以包含这样四种内容:

  • 控制器(controllers),用来完成页面逻辑,不包含DOM操作、资源获取。
  • 服务(services),用来提供资源访问和获取,控制资源的访问,维护数据一致性。
  • 过滤器(filters),用来格式化数据显示,很多第三方插件以提供filter为主,例如angular-moment
  • 语义标签(directives),增强的HTML标签,DOM操作都应当抽象为directive

Angular表单其实是Angular提供的Directive,它有一个别名叫ng-form。是这个Directive实例化了一个FormController来负责表单内的页面逻辑(主要是表单验证)。

<div ng-app>
  <ng-form name=someForm>
    <input name="username" type="text" ng-model="user.username" pattern="^\w{6,18}$">
    <div class="alert alert-danger" ng-show="someForm.username.$error.pattern">
      用户名必须为6-18个字母、数字或下划线
    </div>
  </ng-form>
</div>

ng-model可以把input的值双向地绑定到当前上下文的user.username变量。我们设置了用户名的pattern为6到18位。我们输入用户名时,.alert错误提示便会实时地显示或者隐藏。

这里我们指定了formname属性,form Directive 实例化的FormController就会以someForm命名,并插入到当前$scope。所以在模板中才能够访问userForm变量。另外,Angular的Pattern使用Javascript正则表达式语法,这里\w相当于[a-zA-Z_]

AngularJS 初始化过程

AngularJS DOM HTML JavaScript MVC 模板 模块化 依赖注入 异步

AngularJS属于典型的单页APP框架,现由Google维护,用在了Google的多款产品中。 如果你的项目引入了AngularJS,同时还有不少的jQuery代码,你可能会碰到两者初始化顺序的问题。 本文就来探讨AngularJS APP以及Controller的初始化过程和时机。

Angular APP

一个Angular APP其实就是一个Angular Module,通常可以包含若干Controller、Service以及Directive。甚至不自己定义APP也可以启动一个Angular应用,例如:

<div ng-app>
  <ng-form>...</ng-form>
</div>

在你引入AngularJS之后,div[ng-app]便会在页面载入时启动,其子元素范围内构成一个$scope,可以使用angular标签(又称语义标签,其实就是directive)。通常我们会显示地定义一个Angular APP:

var app = angular.module('helloApp', []); // 第二个参数定义了Module依赖
// 添加controller
app.controller('worldCtrl', ['$scope', function($scope){
    //...
}]);

然后在页面中引用它:

<div ng-app="helloApp">
  <ng-form ng-controller='worldCtrl'>...</ng-form>
</div>

div[ng-app]的子元素中可以使用该app下的所有控制器,控制器可以嵌套,子控制器的$scope直接共享父控制器的$scope中的变量

Angular APP 的启动

同一个页面中可以包含多于一个的APP,但不能嵌套。同一页面中有多于一个APP时AngularJS不会自动帮你启动APP了,你需要手动启动这些APP。例如:

var element = $('#some-div')[0];
angular.bootstrap(element, ['helloApp']);

AngularJS通过依赖注入的方式来实现模块化与封装。在启动APP之前,往往需要注入一些APP所在环境的信息:

这是常见的需求。因为在AngularJS中,尽量不要去操作DOM(除非你在写directive),否则可测试性会严重下降。参见 http://docs.angularjs.cn/guide/controller

var app = angular.module('helloApp'); // 获得之前声明的那个叫helloApp的模块
app.constant('sessionInfo', {
    'currentUser': $('input#current-user').val(),
    'uploadDir':   $('input#upload-dir').val()
});

导航: 上一页 下一页

🔝