angularJS---service

  service

 

    ng的服务是这样定义的:

Angular services are singletons objects or functions that carry out specific tasks common to web apps.

    它是一个单例对象或函数,对外提供特定的功能。

    1.首先是一个单例,即无论这个服务被注入到任何地方,对象始终只有一个实例。

    2.其次这与我们自己定义一个function然后在其他地方调用不同,因为服务被定义在一个模块中,所以其使用范围是可以被我们管理的。ng的避免全局变量污染意识非常强。

    个人感觉service就像模块,把方法封装好,注入调用就行了。

  首先先介绍一下ng内置的服务

    ng内置的服务都是以$开头的。如$scope 

   常用的内置服务:

     A. $scope:  $scope(作用域)是angular应用程序中视图(HTML)和控制器(JavaScript)之间的纽带。也就是说html和javascript可以通过它来传输、共享数据,跟别的服务不同,它只用在controller上。

     B. $http: $http服务是封装了ajax方法的用于请求数据的一个服务

        

  php代码:

<?php
    if($_GET["name"]=="jjk" && $_GET["age"]=='18'){
        $data = "welcome to you";
        echo $_GET["callback"]."({data: '".$data."'})";//使用jsonp请求时 callback是ng内部自动生成的
    }else{
        $data = "sorry!!!";
        echo $_GET["callback"]."({data: '".$data."'})";//返回一个对象
    }
?>

  

  jsonp请求:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body ng-app="app">
    <div ng-controller="myCtrl">
        <input ng-model="name">
        <input ng-model="age">
        <button ng-click="submitJsonp()">Jsonp</button>
        <br>
        <div ng-bind='data'></div>
    </div>
</body>
<script src="./js/angular.js"></script>
<script>
    angular.module('app', [])
        .controller('myCtrl', function ($scope, $http) {
            $scope.name = 'jjk';
            $scope.age = 18;
           
            $scope.submitJsonp = function () {
                $http({
                    url: './php/jsonp.php',
                    method: 'JSONP',
                    params: {
                        name: $scope.name,
                        age: $scope.age
                    }
                }).then(function (data) {
                    console.log(data);
                    $scope.data = data.data.data;
                })
            }
        })

</script>

</html>

  在chrome中显示:

  

 

  php代码:

<?php
    if($_GET["name"]=="jjk"){
        echo "1";
    }else{
        echo "2";
    }
?>

 

 

  get请求:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body ng-app="app">
    <div ng-controller="myCtrl">
        <input ng-model="name">
        <input ng-model="age">
        <button ng-click="submitGet()">Jsonp</button>
        <br>
        <div ng-bind='data'></div>
    </div>
</body>
<script src="./js/angular.js"></script>
<script>
    angular.module('app', [])
        .controller('myCtrl', function ($scope, $http) {
            $scope.name = 'jjk';
            $scope.age = 18;
           
            $scope.submitGet = function () {
                $http({
                    url: './php/get.php',
                    method: 'GET',
                    params: {
                        name: $scope.name,
                        age: $scope.age
                    }
                }).then(function (data) {
                    console.log(data);
                    $scope.data = data.data;
                })
            }
        })

</script>

</html>

 

  在chrome中显示:

 

 

  post请求在angular中就变的有点麻烦了。按照之前jquery的post请求方式

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>jquery的post请求</title>
</head>
<body>
    <button id="btn">post</button>
</body>
<script src="./js/jquery.js"></script>
<script>
    $('#btn').on('click', function(){
        $.ajax({
            url:'./php/post.php',
            method: "POST",
            data:{user:'jjk',password:'123456'},
            datatype:'json',
            success: function(data){
                console.log(data);
            }
        });
    });
</script>
</html>

  php代码:

<?php
    echo $_POST["user"];
    echo $_POST["password"];
?>

  在chrome中显示:

 

看一下他的请求头:

  

 

  重点看红框的内容:application/x-www-form-urkencoded.     从客户端到服务器端数据传输编码标准就是application/x-www-form-urlencoded    这种数据的基本形式是采用"&"分割开的key/value例如:   name1=value1&name2=value2&name3=value3&.....&nameN=valueN  跟get请求的参数是一样的,不同在于,get请求是放在url上,而post请求把数据放在请求头上。 等一下,我们明明传递的是一个对象啊!data:{name:'jjk',password:'123456'}.  其实jquery内部(下面会提到)已经帮我们转换好了数据

   现在来看一下,angular的post请求是如何做的:

  php代码:

<?php
    // echo $_POST["user"];
    // echo $_POST["password"];
     echo $_POST["input"];
     echo $_POST["skill"];
?>

  post请求:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body ng-app="app">
    <div ng-controller="myCtrl">
        <input ng-model="input">
        <input ng-model="skill">
        <button ng-click="submitPost()">post</button>
        <br>
        <div ng-bind='data'></div>
    </div>
</body>
<script src="./js/angular1.5.9.js"></script>
<!--<script src="./js/ngPost.js"></script>-->
<script>
    angular.module('app',[])
        .controller('myCtrl', function ($scope, $http) {
           
            $scope.submitPost = function () {
                $http({
                    url: './php/post.php',
                    method: 'POST',
                    params: {
                            input: $scope.input,
                            skill: $scope.skill
                    }
                }).then(function (data) {
                    console.log(data);
                    // $scope.data = data.data;
                })
            }
        })

</script>

</html>

  在chrome中显示:

 

 

    咦?我明明是post请求啊!!!! 怎么变成了get请求的格式了?

  其实,这里有个小坑。pramas适用情况:get类型请求(注:jsonp也是get类型的请求)。  所以他不适用post请求。要post请求请使用data

  那就改一下:

再看一下network:

 

 

 

  问题就出来了:angular发送的请求数据是json数据,后台直接使用$_POST是读取不到数据的。$POST方式只能接收Content-Type: application/x-www-form-urlencoded提交的数据。从这里可以看出来,jquery是比ng要有“灵性”啊,哎,搞不懂ag开发人员是什么想法。

  解决的办法也有:1.从后台着手:想办法让后台读取json数据,再返回数据。

   php后台有个方法: file_get_contents("php://input",true);  ‘php://input’是个可以访问请求的原始数据的只读流。只读取Content-Type不为multipart/form-data的数据.(详细:http://blog.csdn.net/qiufengwuqing123/article/details/53707840)  我们正好适合这种情况,来试一下:

  改一下php代码:

<?php
    $res = file_get_contents("php://input",true);
    echo $res;

?>

  前端代码不变。请求一下数据:

  在chrome中显示:

  通过修改php去获取数据,嗯,不太好,我们是前端啊,如果数据不能正常返回给后台,是会被后台人员取笑的,哈哈哈哈。还是看看前端有什么方法。

  2.现在来说一下前端的方法:1.修改请求头。

 

 

控制台显示:

 

 

  虽然改了请求头,但发送的数据有问题,变成了字符串。有2个方案:a.改变data的数据格式为字符串  弊端:只能传一个值到后端;b.使用jq的param方法,将数据序列化再传给后台处理 ,弊端:需要引入jq文件

  先来看一下a方案:只需将 data改成这样:data:‘input=jjk’即可

在chrome中显示:

 

  b方案:

  

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body ng-app="app">
    <div ng-controller="myCtrl">
        <input ng-model="input">
        <input ng-model="skill">
        <button ng-click="submitPost()">post</button>
        <br>
        <div ng-bind='data'></div>
    </div>
</body>
<script src="./js/angular1.5.9.js"></script>
<script src="./js/jquery.js"></script>
<!--<script src="./js/ngPost.js"></script>-->
<script>
    angular.module('app', [])
        .controller('myCtrl', function ($scope, $http) {

            $scope.submitPost = function () {
                $http({
                    url: './php/post.php',
                    method: 'POST',
                    data: {
                        input: $scope.input,
                        skill: $scope.skill
                    },
                    transformRequest: function (data) {
                        return $.param(data);
                    },
                    headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
                }).then(function (data) {
                    console.log(data);
                    // $scope.data = data.data;
                })
            }
        })

</script>

</html>

  在chrome中显示:

    

 

  后台返回的数据:

 

 

 

  其实自己也可以写一个类似的param方法;无非就是对data的json数据处理成类似这样的模式:input=jjk&skill=ps   使用循环和字符串方法:

    

function transformRequest(data){
    var str = '';
    for(var i in data){
        str += i + '=' + data[i] + '&';
    }
    return str.substring(0,str.length-1);
}

  跟param方法没什么区别,这样就可以不用引入jquery(建议将此方法写到commonjs上,有备无患)。

  再来介绍第2种方法:直接修改angular,不惯他这“臭毛病”。

    修改Angular的$httpProvider的默认处理

angular.module('MyModule', [], function($httpProvider) {
  // Use x-www-form-urlencoded Content-Type
  $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
 
  /**
   * The workhorse; converts an object to x-www-form-urlencoded serialization.
   * @param {Object} obj
   * @return {String}
   */
  var param = function(obj) {
    var query = '', name, value, fullSubName, subName, subValue, innerObj, i;
       
    for(name in obj) {
      value = obj[name];
         
      if(value instanceof Array) {
        for(i=0; i<value.length; ++i) {
          subValue = value[i];
          fullSubName = name + '[' + i + ']';
          innerObj = {};
          innerObj[fullSubName] = subValue;
          query += param(innerObj) + '&';
        }
      }
      else if(value instanceof Object) {
        for(subName in value) {
          subValue = value[subName];
          fullSubName = name + '[' + subName + ']';
          innerObj = {};
          innerObj[fullSubName] = subValue;
          query += param(innerObj) + '&';
        }
      }
      else if(value !== undefined && value !== null)
        query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
    }
       
    return query.length ? query.substr(0, query.length - 1) : query;
  };
 
  // Override $http service's default transformRequest
  $httpProvider.defaults.transformRequest = [function(data) {
    return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
  }];
});

 

  将它打包在ngPost.js文件里,使用是引进他,并注入到主模块即可。

  

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body ng-app="app">
    <div ng-controller="myCtrl">
        <input ng-model="input">
        <input ng-model="skill">
        <button ng-click="submitPost()">post</button>
        <br>
        <div ng-bind='data'></div>
    </div>
</body>
<script src="./js/angular1.5.9.js"></script>
<!--<script src="./js/jquery.js"></script>-->
<script src="./js/ngPost.js"></script>
<script>
    angular.module('app', ['MyModule'])
        .controller('myCtrl', function ($scope, $http) {

            $scope.submitPost = function () {
                $http({
                    url: './php/post.php',
                    method: 'POST',
                    data: {
                        input: $scope.input,
                        skill: $scope.skill
                    },
                    // transformRequest: function (data) {
                    //     return $.param(data);
                    // },
                    // headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
                }).then(function (data) {
                    console.log(data);
                    // $scope.data = data.data;
                })
            }
        })

</script>

</html>

  在chrome中显示:

  

 

  返回的数据:

 

  

        好了!!! $http服务总算总结完了!!!!!!

      C. $timeout  $interval : 封装自原生js的2个定时器。使用方式没区别

     D.   $location: 封装自原生js的location。$location服务解析在浏览器地址栏中的URL(基于window.location)并且让URL在你的应用中可用。改变在地址栏中的URL会作用到$location服务,同样的,改变$location服务也会改变浏览器的地址栏。(可以使用$location进行重定向等操作)

      调用$location服务:

        暴露浏览器地址栏中的URL,让你可以:

          1.监察URL。

          2. 改变URL。

      与浏览器同步URL,当:

        1.改变地址栏。

        2.单击『前进』『后退』或一个历史记录中的链接。

        3.打开一个链接。

        4.将URL对象表示为一个方法集。 (protocol, host, port, path, search, hash)

    内置方法:

    • absUrl( ):只读;根据在RFC 3986中指定的规则,返回url,带有所有的片段。
    • hash( ):读、写;当带有参数时,返回哈希碎片;当在带有参数的情况下,改变哈希碎片时,返回$location。
    • host( ):只读;返回url中的主机路径。
    • path( ):读、写;当没有任何参数时,返回当前url的路径;当带有参数时,改变路径,并返回$location。(返回的路径永远会带有/)
    • port( ):只读;返回当前路径的端口号。
    • protocol( ):只读;返回当前url的协议。
    • replace( ):如果被调用,就会用改变后的URL直接替换浏览器中的历史记录,而不是在历史记录中新建一条信息,这样可以阻止『后退』。
    • search( ):读、写;当不带参数调用的时候,以对象形式返回当前url的搜索部分。
    • url( ):读、写;当不带参数时,返回url;当带有参数时,返回$location。

    内置事件:

    • $locationChangeStart:在URL改变前发生。这种改变可以通过调用事件的preventDefault方法为阻止。成功时触发$locationChangeSuccess事件。
    • $locationChangeSuccess:当URL改变后发生。

    使用:

    何时使用$location

    任何你想要改变当前URL的时候,都可以使用$location。

    $location不会做

    当浏览器的URL改变时,不会重新加载整个页面。如果想要重新加载整个页面,需要使用$window.location.href。

 

    E.  $document   $document服务就和angular.element(document)是一样的,是一个整体的dom结构树,包含jqlite的所有方法 另外,      $document[0]和原生JS的document等效   

      AngularJs是不直接操作DOM的,但是在平时的开发当中,我们有的时候还是需要操作一些DOM的,如果使用原生的JS的话操作过于麻烦

  所以$document服务可以代替原生document去使用内置的jqlite方法。

 

    F. $window  跟原生window没什么两样。

 

    G.$sce   $sce服务是“Strict Contextual Escaping”的缩写。翻译成中文就是“严格的上下文模式”也可以理解为安全绑定.由于ng是使用指令绑定数据,所以后台返回的数据经常会带有标签。但有些标签会可能会对页面造成不可预期的破坏(脚本攻击之类的),所以$sce服务就是为了过滤‘威胁’标签的,再通过ng-bind-html来插入标签。 $sce服务有个方法trustAsHtml()---用于信任后台返回的标签。

    实例:

  

app.filter("html",function($sce){
            return function(input){
                return $sce.trustAsHtml(input)
            }
        })

 

    H. $index  $index只在ng-repeat中起作用,它的作用是获取当前标签的索引值。除了$index外还有$first,$middle,$last,$even,$odd。

 

  常用的内置服务大概就是这些了。

 

    自定义服务:ng支持开发者自己定义服务 。开发者使用provider/factory/service/constant/value 等函数创建一个自定义服务。开发者自定义的服务同样可以注入。以下类型的函数可以接受注入: controller/directive/filter/service/factory等(ng中的‘依赖注入’是通过provider和injector这2个机制联合实现的)

    虽然,使用provider/factory/service/constant/value 等函数均可创建一个自定义服务,但它们之间的区别还是要了解一下:provider是基础,其余函数都是调用provider函数实现,而且从左到右,灵活性越来越差(可以翻源码看看) 但建议使用service(他本身就是服务的意思嘛)就可以了。话虽如此,还是要好好了解这些函数:

  先说这些函数之前,我们先看看module里有什么

  

<script src="./AngularJs/js/angular.js"></script>
<script>
    var app = angular.module('app',[]);
    console.log(app);
</script>

  在chrome中显示:

  大多数函数我们都是知道的。

    现在先来了解一下:provider    本身就有:‘提供者’的意思。

     provider自定义服务的方式:

<body ng-app="app">
    <div ng-controller="myCtrl">
        <p>{{name}}</p>
        <button ng-click='say()'>点击</button>
    </div>
</body>
<script src="./js/angular.js"></script>
<script>
    angular.module('app',[])
    .controller('myCtrl',['$scope','myService',function($scope,myService){
        console.log(myService);
        $scope.name = myService.name;
        $scope.say = myService.say;
    }])
    .provider('myService',function(){
        return{
            $get:function(){
                return {
                    name: 'jjk',
                    say: function(){
                        console.log(this.name)
                    }
                }
            }
        }
    })
</script>

    返回一个对象,对象有$get方法 又返回一个对象,这个对象才是真正的定制服务的内容,其他都是固定写法。其中的$get方法是用来返回provider实例的(说了跟没说一样)

    

  

  来看一下源码:

function provider(name, provider_) {
    assertNotHasOwnProperty(name, 'service');
    if (isFunction(provider_) || isArray(provider_)) {
      provider_ = providerInjector.instantiate(provider_);
    }
    if (!provider_.$get) {
      throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name);
    }
    return (providerCache[name + providerSuffix] = provider_);
  }

  第一个参数是自定义服务的名称,第二个参数可以是一个function、array、object    从

 if (!provider_.$get) {
      throw $injectorMinErr('pget', 'Provider \'{0}\' must define $get factory method.', name);
    }

可以看出,每个prodider都有个$get方法,不然会报错的。

  

    

    factory: 从名字上了解,‘工厂’的意思嘛。

  先看一下他的源码:

    

function factory(name, factoryFn, enforce) {
    return provider(name, {
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
    });
  }

  从源码可以看出 返回一个provider,所以说factory本质上就是由provider实现的。换句话说:factory只是生成provider的快捷方式而已。那factory又是如何自定义服务的呢?

  

<script src="./js/angular.js"></script>
<script>
    angular.module('app',[])
    .controller('myCtrl',['$scope','myService',function($scope,myService){
        console.log(myService);
        $scope.name = myService.name;
        $scope.say = myService.say;
    }])
    .factory('myService', function(){
        return{
            name:'jjk',
            say: function(){
                console.log(this.name);
            }
        }
    })
</script>

  直接返回一个对象,而这个对象就是我们自定服务的内容。

 

  接下来就是 service了    ‘服务’ 

  看一下源码:

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
  }

    这个就有点意思了。service先调用了factoty,factoty又调用了provider。 那个factoty又有什么不同呢?

<script src="./js/angular.js"></script>
<script>
    angular.module('app',[])
    .controller('myCtrl',['$scope','myService',function($scope,myService){
        console.log(myService);
        $scope.name = myService.name;
        $scope.say = myService.say;
    }])
    .service('myService', function(){
       
            this.name='jjk';
            this.say=function(){
                console.log(this.name);
            };
        
    })

  service有点构造函数的意思。注入时,ng自动会帮它‘new’一个实例出来。其实service也是可以写成factoty的形式

<script src="./js/angular.js"></script>
<script>
    angular.module('app',[])
    .controller('myCtrl',['$scope','myService',function($scope,myService){
        console.log(myService);
        $scope.name = myService.name;
        $scope.say = myService.say;
    }])
    .service('myService', function(){
       
          return {
              name:'jjk',
              say: function(){
                  console.log(this.name);
              }
          }
        
    })
</script>

  没什么影响的。这也是为什么推荐使用service创建自定义服务!!

 

  再来看value   ‘值’的意思嘛   value(name,value)  name为注册的常量的名字,value为注册的常量的值或对象。

  它的源码:

    

function value(name, val) { return factory(name, valueFn(val), false); }

  很简单的一句代码,还是用了factoty(这大概就是factoty命名的由来吧)。

<script src="js/angular.js"></script>
    <script>
        var app = angular.module("demo", []);
        console.log(app)
        app.controller("indexCtrl", function($scope,author,six) {
            $scope.name = "value";
            console.log(author)
            console.log(six)
        })
        app.value("author","laoxie");
        app.value("six",6)
    </script>

  简单来说,value方法是用来设置保存一些数据的 它是可以被更改的,常用在baseUrl,文件头上。

 

  最后一个 constant   译为:不变的,永恒的  跟value类似,但又不同。一旦设置了,就不能更改了。

  源代码:

function constant(name, value) {
    assertNotHasOwnProperty(name, 'constant');
    providerCache[name] = value;
    instanceCache[name] = value;
  }

  从源代码可以看出来:constant跟 provider没什么关系 但他自定义服务跟value一样。

<script src="js/angular.js"></script>
    <script>
        var app = angular.module("demo", []);
        console.log(app)
        app.controller("indexCtrl", function($scope,author) {
            $scope.name = "value";
            console.log(author);
        })
        app.constant("author",123);
    </script>

  

    服务这一块总结的差不多了。但我有个疑问:ng内部到底是如何实现注入服务的??

    有机会一定要通读一下源码

                                          2017/4/16 20:23

 

 
posted @ 2017-05-08 10:31  游鱼与渔  阅读(312)  评论(0编辑  收藏  举报