AngularJS实战笔记我爱编程Angualr

Angular与服务端交互

2016-08-02  本文已影响718人  阿振_sc

在Angular中,封装了诸如$http$resource等众多服务模块,供开发者与服务端交互时调用,同时,应用内部的缓存机制可加速交互时的数据通信,高效的处理客户端与服务端的数据交互

与服务端交互简介

Angular中的$http服务就是将以往的Ajax请求封装成了一个内部的服务模块来供开发者使用。

传统的Ajax方式与服务端交互

下面是传统的Ajax请求示例代码:

<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <title>传统Ajax与服务端交互</title>
  <style>
    .frame {font-size: 12px; width: 320px; float: left}
    ul {list-style-type: none; padding: 0; margin: 0}
    ul li {background-color: #f3f1f1; padding: 8px; float: left; border-bottom: 1px solid #666}
    ul li span {text-align: left; width: 86px; height: 18px; line-height: 18px; float: left}
    .show {width: 260px; padding: 8px; background-color: #eee;}
    .show .tip {font-size: 9px; color: #666; margin: 8px 3px}
  </style>
</head>
<body>
  <div class="frame">
    <ul id="stuInfo">
      <li>正在加载中......</li>
    </ul>
  </div>

  <script>
    (function () {
      var xhr = null;
      if (window.ActiveXObject) {
        xhr = new ActiveXObject("Microsoft.XMLHTTP")
      } else if (window.XMLHttpRequest) {
        xhr = new XMLHttpRequest();
      }

      xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
          if (xhr.status === 200) {
            var HTML = '';
            var data = eval("(" + xhr.responseText + ")");
            for (var i=0; i<data.length; i++) {
              HTML += '<li><span>' + data[i].Code + '</span>';
              HTML += '<span>' + data[i].Name + '</span>';
              HTML += '<span>' + data[i].Score + '</span></li>';
            }
            document.getElementById('stuInfo').innerHTML = HTML;
          }
        }
      }
      xhr.open('GET', 'http://localhost/data/stu.php', true);
      xhr.send();
    })();
  </script>
</body>
</html>

PHP服务端代码:

<?php
header('Content-type: text/json');
$stulist = array (
  array('Code'=>'10101', 'Name'=>'刘真真', 'Score'=>'530'),
  array('Code'=>'10102', 'Name'=>'张明吉', 'Score'=>'460'),
  array('Code'=>'10103', 'Name'=>'舒虎', 'Score'=>'660'),
  array('Code'=>'10104', 'Name'=>'周晓敏', 'Score'=>'500'),
  array('Code'=>'10105', 'Name'=>'卢明明', 'Score'=>'300'),
  array('Code'=>'10106', 'Name'=>'王小五', 'Score'=>'490')
);

echo json_encode($stulist);
?>

使用$http服务与服务端交互

在Angular中,与后端交互调用$http服务模块,它封装了Javascript中的XMLHttpRequest对象,接收一个对象作为参数,用于收集生成HTTP请求的配置内容,同时返回一个promise对象,该对象可以使用successerror两个回调方法

通用格式如下:

$http.请求类型(url, [data], [config])
     .success (data, status, headers, config) {
       // 成功后的操作
     }     .error (data, status, headers, config) {
       // 错误时的操作
     }

示例:

html:

<div class="frame">
    <div class="tip">POST返回的结果是:{{result}}</div>
    <button ng-click="onclick()">发送</button>
</div>

javascript:

var myapp = angular.module('MyApp', []);
    
    // 将客户端数据以POST方式通过$http服务发送到服务端
    // 需要调用config方法,注入$httpProvider服务
    // 并调用该服务对象重置发送数据时默认函数transformRequest和属性Content-Type的值
    myapp.config(function ($httpProvider) {
        // 通过$httpProvider服务改写transformRequest和headers
      $httpProvider.defaults.transformRequest = function (obj) {
        var arrStr = [];
        for (var p in obj) {
          arrStr.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
        }
        return arrStr.join('&');
      }
      $httpProvider.defaults.headers.post = {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    });

    myapp.controller('MyController', ['$scope', '$http', function ($scope, $http) {
      var postData = {name: 'Kaindy'};
      $scope.onclick = function () {
        $http.post('post.php', postData)
             .success(function (data, status, headers, config) {
               $scope.result = data;
             })
             .error(function (data, status, headers, config) {
               $socpe.result = data;
             })
      }
    }]);

使用$http配置对象方式与服务端交互

上面的方式缺乏灵活性,而且代码量较多,我们可以将$http服务模板当成一个函数来使用,将构造XHR对象的所有配置项作为一个对象,并将对象定义为函数的形参,在调用时修改形参对象中的各属性值即可,调用格式如下:

// $http中的形参是一个配置对象
$http({
  method:   // 请求方法,可以是PSOT、GET、JSONP、DELETE、PUT和HEAD
  url:      // 向服务器请求的地址
  data:     // 对象,作为消息体的一部分发送给服务端,常用于POST和PUT
  params:   // 字符串或对象,如果是对象将被自动按json格式序列化,并追加到URL后
  transformRequest: // 用于将请求头信息和请求体进行序列化,并生成一个数组发送给服务端
  transformResponse: // 用于将响应头信息和响应体进行反序列化,其实质就是解析服务器发送过来的被序列化后的数据
  cache:  // 如果为true,表示将缓存请求结果,反之则不缓存
  timeout: // 表示延迟发送HTTP请求的时间,单位是毫秒
})

示例代码:

html:

<input type="text" ng-model="num">
<button ng-click="onclick()">验证奇偶</button>
<div class="tip">您输入的是: {{result}}</div>

javascript:

var myapp = angular.module('MyApp', []);

    myapp.controller('MyController', ['$scope', '$http', function ($scope, $http) {
      // $scope.num = 0;
      // $scope.result = '偶数';
      $scope.onclick = function () {
        $http({
          method: 'GET',
          url: 'chk.php',
          params: {
            n: $scope.num
          }
        }).success(function (data, status, headers, config) {
          $scope.result = data;
        })
      }
    }]);

php:

<?php
  function checkNum ($num) {
    return ($num % 2) ? true : false;
  }

  if (checkNum($_GET['n']) === true) {
    echo '奇数';
  } else {
    echo '偶数';
  }
?>

需要注意的是,在Angular中,执行$http函数后,它返回的内容其实一个是promise对象,可以直接通过链式的写法调用then方法获取成功和异常后的数据

$http({
    // 配置对象
})
.success(fn1)
.error(fn2)

等价于:

$http({
    // 配置对象
})
.then(fn1, fn2)

上面的fn1和fn2分别表示成功和错误时的返回函数,使用第二种方式获取的是服务端完整的响应对象,而使用successerror方法只是接收解析并处理后的响应对象

Angular中的缓存

缓存的功能就是加快获取内容的速度,减少重复请求。因此,在Angular中,提供了专门的服务 - $cacheFactory来生成缓存对象,同时,$http服务中还可以开启缓存、自定义默认缓存名称

$cacheFactory服务创建缓存对象

使用$cacheFactoy服务创建缓存对象,一般都以key/value形式存储,如下:

$cacheFactoy(key, [options]);

参数key表示缓存对象的名称,可选参数options是一个对象,用于指定缓存的特征。一般情况下,会在这个对象中添加一个capacity属性,它是一个数字,用于说明缓存的最大容量,如该值为3,则只能缓存前3次请求。

创建或获取缓存对象后,就可以使用对象本身的方法进行缓存的操作

(1) info方法

info方法返回缓存对象的一些信息,包括大小、名称

var cache = $cacheFactory('test');
console.log(cache.info());

(2) put方法

put方法向缓存对象中以key/value的形式添加缓存内容,并返回添加后的键值

cache.put('c1', 'hello');
console.log(cache.put('c1', 'hello'));

(3) get方法

get方法获取键名对应的键值内容

console.log(cache.get('c1'));  // hello
console.log(cache.get('c2'));  // undefined

(4) remove方法

remove方法可以移除指定键名的缓存

cache.remove('c1');

(5) removeAlldestory方法

removeAll方法用于移除全部的缓存内容,并重置缓存结构,destory方法则是从$cacheFactory缓存注册表中删除所有的缓存引用条目,并重置缓存对象

示例代码:

<div ng-controller="MyCtrl">
  <input type="text" ng-model="cname" size="6">
  <button ng-click="cset()">设置</button>
  <button ng-click="cshow()">显示</button>
  <button ng-click="cdel()">删除</button>
  <div>缓存值是: {{cvalue}}</div>
</div>
var myapp = angular.module('MyApp', []);

  myapp.service('cache', function ($cacheFactory) {
    return $cacheFactory('test');
  });

  myapp.controller('MyCtrl', ['$scope', 'cache', function ($scope, cache) {
    $scope.cset = function () {
      if (cache.put('mytest', $scope.cname)) {
        alert('set cache success!');
      }
    }
    $scope.cshow = function () {
      var tcache = cache.get('mytest');
      $scope.cvalue = tcache ? tcache : '空值';
    }
    $scope.cdel = function () {
      cache.remove('mytest');
    }
  }])

$http服务中的缓存

在Angular中,当调用$http方法与服务端进行数据交互时,也能使用缓存,方法是在配置对象中添加一个名为'cache'的属性,并将它的属性值设为true,表示开启请求缓存

示例代码:

<div ng-controller="MyCtrl">
  <div>接收的内容是: {{result}}</div>
  <div>缓存的内容是: {{cache}}</div>
</div>
var myapp = angular.module('MyApp', []);

  myapp.controller('MyCtrl', ['$scope', '$http', '$cacheFactory', function ($scope, $http, $cacheFactory) {
    var url = 'cache.php';
    // 在调用$http方法时,Angular内部自动创建$http缓存对象
    var cache = $cacheFactory.get('$http');
    $http({
      method: 'GET',
      url: url,
      cache: true
    })
    .then(function (data, status, headers, config) {
      $scope.result = data.data;
      var arrResp = cache.get(url);
      $scope.cache = arrResp[0] + ' - ' + arrResp[1];
    })
  }])

自定义$http服务中的缓存

在自定义缓存对象过程中,可以采用传递实例缓存的方法,将定义好的缓存对象添加到$http服务中

示例代码:

<div ng-controller="MyCtrl">
  <div>接收内容是: {{result}}</div>
  <button ng-click="refresh()">刷新</button>
</div>
var myapp =angular.module('MyApp', []);

  // 创建名为cache的服务,并返回一个名为mycache的缓存实例
  myapp.service('cache', ['$cacheFactory', function ($cacheFactory) {
    return $cacheFactory('mycache', {capacity: 3});
  }]);

  myapp.controller('MyCtrl', ['$scope', '$http', 'cache', function ($scope, $http, cache) {
    var url = 'cache.php';
    $http({
      method: 'GET',
      url: url,
      cache: cache  // 这里设置为cache,实现缓存实例的传递
    }).success(function (data, status, headers, config) {
      $scope.result = data;
      cache.put('c', data);
    })
    $scope.refresh = function () {
      var _c = cache.get('c');
      $scope.result = (_c) ? _c + '来源缓存' : '刷新失败!';
    }
  }])

$resource服务

$resource服务能支持与RESTful的服务器进行无缝隙的数据交互,在调用$resource服务后,返回的$resource对象包含了多种与服务端进行交互的API,像getsavequery

$resource服务的使用和对象中的方法

$resource服务是一个可选模块,所以它没有被包含在Angular中,所以如果需要使用它,就需要使用<script>元素进行文件导入

<script src="js/angular-resource.min.js"></script>

导入模块后,在应用的模型中通过下面的方式进行注入

angular.module('myapp', ['ngResource'])

注入之后,就可以在控制器或其他自定义的服务中直接调用$resource服务了

var obj = $resource(url, [, paramDefaults] [, actions]);

obj表示请求服务器指定url地址后返回的$resource对象,该对象就包含了与服务器进行数据交互的全部API

参数url表示请求服务器的地址,它允许使用占位符变量,该变量以:为前缀

var obj = $resource('url?action=:act');
obj.$save(act : 'save');
// 在执行save后,实际发送地址就是:url?action=save

参数paramDefaults是一个对象,用于设置请求时的默认参数值

var obj = $resource('url?action=:act', {
  act: 'save',
  a: '1',
  b: '2'
});
// 实际发送地址为:url?action=save&a=1&b=2

另一个可选参数actions也是一个对象,它的功能是扩展默认资源动作,比如可以在该对象中自定义新的方法

var obj = $resource('url?action=:act', {
  // 定义请求默认值
}, {
  a: {
    method: 'get'
  }
});

然后我们就可以直接调用可选参数actions中自定义的方法a,即obj.$a()

调用$resource服务所返回的对象中包含5个与服务端交互的API,2个是GET类型,3个是非GET类型

(1) $resource对象中的GET类型请求

$resource对象中的两个GET类型请求分别是getquery方法

var obj = $resource('url');
// get()方法
obj.get(params, successFn, errorFn);
// query()方法
obj.query(params, successFn, errorFn);

参数params是一个对象,用于添加随请求一起发送的数据,发生请求时该对象中的键值会被自动序列化并添加到url的后面。

successFn和errorFn分别表示成功和失败后的回调函数

它们的区别是,get()方法可以返回单个资源,而query()方法必须返回一个数组或集合类的资源

(2) $resource对象中的非GET类型请求

非GET类型请求有3个,分别是savedeleteremove方法

var obj = $resource(url);

// save()方法
obj.save(params, postData, successFn, errorFn);

// delete()方法
obj.delete(params, postData, successFn, errorFn);

// remove()方法
obj.remove(params, postData, successFn, errorFn);

非GET类型请求与上面的GET请求类型相比,多了一个postData参数,它的功能是添加以非GET方式向服务端发送的数据体

save()方法在服务端保存数据,它将以POST方式向服务端发送请求,postData参数中添加的数据体也将一起被发送

delte()remove()方法都是在删除服务端数据时使用,它们将携带postData参数中添加的数据体,以delete方式向服务端发送请求,它们的区别是,remove方法可以解决IE浏览器中delete是JavaScript保留字而导致的错误

综合示例:

html:

<div ng-controller="MyCtrl">
  <ul>
    <li ng-repeat="item in items">
      <span>{{item.Code}}</span>
      <span>{{item.Name}}</span>
      <span>{{item.Sex}}</span>
    </li>
  </ul>
  <div>
    key值: <input type="text" ng-model="key">
    <button ng-click="save()">保存</button>
    <div>{{result}}</div>
  </div>
</div>

javascript:

var myapp = angular.module('MyApp', ['ngResource']);

  myapp.config(function ($httpProvider) {
    $httpProvider.defaults.transformRequest = function (obj) {
      var arrStr = [];
      for (var p in obj) {
        arrStr.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
      }
      return arrStr.join('&');
    }
    $httpProvider.defaults.headers.post = {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });

  myapp.controller('MyCtrl', ['$scope', '$resource', function ($scope, $resource) {
    var stus = $resource('info.php');
    stus.query({action: 'search'}, function (resp) {
      $scope.items = resp;
    });
    $scope.save = function () {
      var data = {
        key: $scope.key
      }
      stus.save({action: 'save'}, data, function (resp) {
        $scope.result = (resp[0] == '1') ? '保存成功' : '保存失败';
      })
    }
  }]);

PHP:

<?php
header('Content-Type: text/json');

if ($_GET['action'] == 'search') {
  $stulist = array (
    array('Code'=>'1001', 'Name'=>'刘振', 'Sex'=>'男'),
    array('Code'=>'1002', 'Name'=>'李雨', 'Sex'=>'女')
  );
  echo json_encode($stulist);
} elseif ($_GET['action'] == 'save') {
  if ($_POST['key'] == '1010') {
    echo '1';
  } else {
    echo '0';
  }
}
?>

$resource服务中自定义请求方法

$resource服务中自定义请求方法,只需在调用$resource服务的方法中,添加第3个可选项参数actions,在参数对象中,通过key/value的方式自定义$resource对象方法。

示例代码:

html:

<div ng-controller="MyCtrl">
  <div>{{r0}}</div>
  <div>{{r1}}</div>
  <div>{{r2}}</div>
  <button ng-click="click()">开始</button>
</div>

javascript:

'use strict';
  var url = 'self.php?action=:act';
  var myapp = angular.module('MyApp', ['ngResource']);

  // 重置transformRequest和Content-Type,以便以POST方式通过$http发送数据
  myapp.config(function ($httpProvider) {
    $httpProvider.defaults.transformRequest = function (obj) {
      var arrStr = [];
      for (var p in obj) {
        arrStr.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
      }
      return arrStr.join('&');
    }
    $httpProvider.defaults.headers.post = {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });

  // 通过factory工厂函数定义custom服务,返回$resource对象
  // 并在actions参数中自定义方法update
  myapp.factory('custom', ['$resource', function ($resource) {
    return $resource(url,
      {
        act: 'search'
      },
      {
        update: {
          method: 'POST',
          params: {
            update: true
          },
          isArray: false
        }
      });
  }]);

  myapp.controller('MyCtrl', ['$scope', 'custom', function ($scope, custom) {
    $scope.click = function () {
      custom.get({id: '1010'}, function (resp0) {
        $scope.r0 = (resp0[0] == '1') ? '查找成功' : '查找失败';
        resp0.key = '1011';
        resp0.$update({act: 'update'}, function (resp1) {
          $scope.r1 = (resp1[0] == '1') ? '更新成功' : '更新失败';
          resp1.key = '1012';
          resp1.$save({act: 'save'}, function (resp2) {
            $scope.r2 = (resp2[0] == '1') ? '保存成功' : '保存失败';
          })
        });
      });
    }
  }]);

PHP:

<?php
if ($_GET['action'] == 'search') {
  if ($_GET['id'] == '1010') {
    echo '1';
  } else {
    echo '0';
  }
} elseif ($_GET['action'] == 'update') {
  if ($_POST['key'] == '1011' && $_GET['update'] == 'true') {
    echo '1';
  } else {
    echo '0';
  }
} elseif ($_GET['action'] == 'save') {
  if ($_POST['key'] == '1012') {
    echo '1';
  } else {
    echo '0';
  }
}
?>

promise对象

在Ajax中,我们会添加回调函数来处理服务端返回的数据,但这种处理方法失去了控制流、异常处理,并会陷入层层的回调嵌套中。

promise是一种处理异步编程的模式,可以有效的解决回调的繁琐,并以一种同步的方式去处理业务流程

我们用一种拟物化的方式来说明pormise

比如有一名A客户,向B公司提出制作网页的需求,B公司答应3天内完成,这个承诺就是一个promise对象

它的本质就是A客户发起的一个延期业务,可以理解为通过$q对象调用defer方法创建了一个延期对象

在接下来的3天里,A客户与B公司交流开发进度,这可以理解为调用延期对象中的notify方法发送消息的过程

如果3天内,B公司正常将网页交付给A客户了,则可以理解为调用延期对象中resolve方法的过程

如果无法交付,则可以理解为调用延期对象中reject方法的过程

如果B公司将以前做过的一个相同的页面交付给了A客户,A客户也很满意,则可以理解为通过$q对象调用then方法的过程

总结promise中常用的各种方法:

在Angular中创建一个promise对象,必须在模板中先注入$q服务,然后调用defer()方法创建一个延期对象

var myapp = angular.module('MyApp', []);
myapp.controller('MyCtrl', ['$scope', '$q', function($scope, $q) {
  var defer = $q.defer();  // 创建延期对象
}])

defer是一个延期对象,包括3个方法,分别是notify()resolve()reject(),还有一个名为promise的属性

一旦创建了promise对象,就可以通过调用then方法来执行延期对象不同操作后的回调函数,then方法包含与操作相对应的3个回调函数

promise.then(successCallback, errorCallback, notifyCallback);

successCallback表示执行resolve方法时的回调函数
errorCallback表示执行reject方法时的回调函数
notifyCallback表示执行notify方法时的回调函数

示例代码:

html:

<div ng-controller="MyCtrl">
  <div>{{t0}}</div>
  <div>{{t1}}</div>
  <button ng-click="action(true)">解决</button>
  <button ng-click="action(false)">拒绝</button>
</div>
'use strict';
var myapp = angular.module('MyApp', []);

myapp.controller('MyCtrl', ['$scope', '$q', function ($scope, $q) {
  var defer = $q.defer();    // 定义延期对象
  $scope.action = function (type) {
    defer.notify(0);
    type ? defer.resolve(1) : defer.reject(1);
    // 创建promise对象
    var promise = defer.promise;
    // 调用promise的then方法完成回调处理
    promise.then(function (n) {
      n++;
      $scope.t1 = '已处理完成:' + n;
    }, function (n) {
      n++;
      $scope.t1 = '未完成原因:' + n;
    }, function (n) {
      n++;
      $scope.t0 = '正在处理中:' + n;
    });
  }
}]);

promise对象在$http中的应用

$http请求中使用promise对象,可以减少数据加载时的白框现象或等待加载的时间

示例代码:

html:

<div ng-controller="MyCtrl">
  {{result}}
</div>

javascript:

<script>
  'use strict';
  var myapp = angular.module('MyApp', []);

  myapp.factory('async', function ($q, $http) {
    var defer = $q.defer();
    $http.get('async.php')
    .success(function (data) {
      defer.resolve(data);
    })
    .error(function (reason) {
      defer.reject(reason);
    })
    return defer.promise;
  });

  myapp.controller('MyCtrl', ['$scope', 'async', function ($scope, async) {
    var promise = async;
    promise.then(function (resp) {
      $scope.result = '请求成功:' + resp;
    }, function (n) {
      $scope.result = '请求失败:' + resp;
    })
  }])
</script>
上一篇下一篇

猜你喜欢

热点阅读