zoukankan      html  css  js  c++  java
  • Angular入门教程三

    4.6指令(directive)

      通过使用模板,我们可以把model和controller中的数据组装起来呈现给浏览器,还可以通过数据绑定,实时更新视图,让我们的页面变成动态的。

     模板中可以使用的东西包括以下四种:

    1.指令(directive):ng提供的或者自定义的标签和属性,用来增强HTML表现力;

    2.标记(markup):即双大括号{{}},可将数据单向绑定到HTML中;

    3.过滤器(filter):用来格式化输出数据;

    4.表单控制:用来增强表单的验证功能。

    其中,指令无疑是使用量最大的,ng内置了很多指令用来控制模板,如ng-repeat,ng-class,也有很多指令来帮你完成业务逻辑,如ng-controller,ng-model。

    指令的几种使用方式如下:

    l 作为标签:<my-dir></my-dir>

    l 作为属性:<span my-dir="exp"></span>

    l 作为注释:<!-- directive: my-dir exp -->

    l 作为类名:<span class="my-dir: exp;"></span>

    其实常用的就是作为标签和属性。

    4.6.1样式相关的指令

      既然模板就是普通的HTML,那我首要关心的就是样式的控制,元素的定位、字体、背景色等等如何可以灵活控制。下面来看看常用的样式控制指令。

    1. ng-class

       ng-class用来给元素绑定类名,其表达式的返回值可以是以下三种:

    l 类名字符串,可以用空格分割多个类名,如’redtext boldtext’;

    l 类名数组,数组中的每一项都会层叠起来生效;

    l 一个名值对应的map,其键值为类名,值为boolean类型,当值为true时,该类会被加在元素上。

      下面来看一个使用map的例子:

    ng-class测试

    红色 加粗 删除线 

    map:{redtext:{{red}}, boldtext:{{bold}}, striketext:{{strike}}}

      如果你想拼接一个类名出来,可以使用插值表达式,如:

      <div class=”{{style}}text”>字体样式测试</div>

      然后在controller中指定style的值:

      $scope.style = ‘red’;

      注意我用了class而不是ng-class,这是不可以对换的,官方的文档也未做说明,姑且认为这是ng的语法规则吧。

      与ng-class相近的,ng还提供了ng-class-odd、ng-class-even两个指令,用来配合ng-repeat分别在奇数列和偶数列使用对应的类。这个用来在表格中实现隔行换色再方便不过了。

    2. ng-style

      ng-style用来绑定元素的css样式,其表达式的返回值为一个js对象,键为css样式名,值为该样式对应的合法取值。用法比较简单:

    <div ng-style="{color:'red'}">ng-style测试</div>

    <div ng-style="style">ng-style测试</div>

    $scope.style = {color:'red'};  

    3. ng-show,ng-hide

       对于比较常用的元素显隐控制,ng也做了封装,ng-show和ng-hide的值为boolean类型的表达式,当值为true时,对应的show或hide生效。框架会用display:block和display:none来控制元素的显隐。

    4.6.2表单控件功能相关指令

      对于常用的表单控件功能,ng也做了封装,方便灵活控制。

      ng-checked控制radio和checkbox的选中状态

      ng-selected控制下拉框的选中状态

      ng-disabled控制失效状态

      ng-multiple控制多选

      ng-readonly控制只读状态

      以上指令的取值均为boolean类型,当值为true时相关状态生效,道理比较简单就不多做解释。注意: 上面的这些只是单向绑定,即只是从数据到模板,不能反作用于数据。要双向绑定,还是要使用 ng-model 。

    4.6.3事件绑定相关指令

    事件绑定是javascrpt中比较重要的一部分内容,ng对此也做了详细的封装,正如我们之前使用过的ng-click一样,事件的指令如下:

    ng-click

      ng-change

      ng-dblclick

      ng-mousedown

      ng-mouseenter

      ng-mouseleave

      ng-mousemove

      ng-mouseover

      ng-mouseup

      ng-submit

      事件绑定指令的取值为函数,并且需要加上括号,例如:

    <select ng-change=”change($event)”></select>  

    然后在controller中定义如下:

    $scope.change = function($event){

             alert($event.target);

             //……………………

    }  

    在模板中可以用变量$event将事件对象传递到controller中。

    对于ng的这种设计,一些人有所质疑,视图与事件绑定混在一起到底好不好?我们不是要讲究视图与逻辑分离吗?如此一来,把事件的绑定又变回了内联的,岂不是历史的倒退。我也一样对此表示不解,因为不写onclick已经很多年。。。但既然已经存在了,我们不妨往合理的方向上想一想,或许ng的设计者压根就不想让模板成为单纯的视图层,本来就是想增强HTML,让它有一点业务能力。这么想的话似乎也能想通,好吧,先欺骗一下自己吧~

    4.6.4特殊的ng-srcng-href

    在说明这两个指令的特殊之前,需要先了解一下ng的启动及执行过程,如下图:

     

    1) 浏览器加载静态HTML文件并解析为DOM;

      2) 浏览器加载angular.js文件;

      3) angular监听DOMContentLoaded 事件,监听到时开始启动;

      4) angular寻找ng-app指令,确定作用范围;

      5) 找到app中定义的Module使用$injector服务进行依赖注入;

      6) 根据$injector服务创建$compile服务用于编译;

      7) $compile服务编译DOM中的指令、过滤器等;

      8) 使用ng-init指令,将作用域中的变量进行替换;

      9) 最后生成了我们在最终视图。

      可以看到,ng框架是在DOMcontent加载完毕后才开始发挥作用。假如我们模板中有一张图片如下:

      <img src="http://m.cnblogs.com/142260/”{{imgUrl}}” />

      那么在页面开始加载到ng编译完成之前,页面上会一直显示一张错误的图片,因为路径{{imgUrl}}还未被替换。

      为了避免这种情况,我们使用ng-src指令,这样在路径被正确得到之前就不会显示找不到图片。同理,<a>标签的href属性也需要换成ng-href,这样页面上就不会先出现一个地址错误的链接。

    顺着这个思路再多想一点,我们在模板中使用{{}}显示数据时,在ng编译完成之前页面上岂不是会显示出大括号及里面的表达式?确实是这样。为了避免这个,ng中有一个与{{}}等同的指令:ng-bind,同样用于单向绑定,在页面刚加载的时候就不会显示出对用户无用的数据了。尽管这样你可能不但没舒心反而更纠结了,{{}}那么好用易理解,还不能用了不成?好消息是我们依然可以使用。因为我编写的是单页面应用,页面只会在加载index.html的时

    候出这个问题,只需在index.html中的模板中换成ng-bind就行。其他的模板是我们动态加载的,就可以放心使用{{}}了。

    4.6.5 自定义指令示例

    下面我们来解析下指令的例子(例07)。

    1.首先,我们定义一个名为userInfo的指令:

    demoApp.directive('userInfo',function(){

    return {

            restrict : 'E',

            templateUrl : 'userInfoTemplate.html',

            replace : true,

            transclude : true,

            scope : {

                mytitle : '=etitle'

            },

            link : function(scope,element,attrs){

                scope.showText = false;

                scope.toggleText = function(){

                    scope.showText = ! scope.showText;

                }

            }

        };

    })  

    Restrict为'E':用作标签;replace为true:用模板替换当前标签;transclude为true:将当前元素的内容转移到模板中;scope 为 {mytitle : '=etitle'}:定义一个名为mytitle的MODEL,其值指向当前元素的etitle属性;templateUrl为'userInfoTemplate.html':模板内容为ng-template定义ID为userInfoTemplate.html的内容;link:指定所包含的行为。其具体的说明及其他参数,请参考:6.2指令详解。

    2. userInfoTemplate.html模板为:

    <script type="text/ng-template" id="userInfoTemplate.html">

    <div class="mybox">

    <div class="mytitle" style="cursor: pointer;" ng-click="toggleText()">

    { {mytitle} }

    </div>

    <div ng-transclude ng-show="showText">

    </div>

    </div>

    </script>

    将当前元素的内容添加到有ng-transclude属性的这个DIV下,默认是隐藏的。

    3.Controller信息:

    demoApp.controller("test7Controller", function($scope){

    $scope.title = '个人简介';

    $scope.text = '大家好,我正在研究AngularJs,欢迎大家与我交流。';

    $scope.updateInfo = function (){

    $scope.title = '个人信息';

    $scope.text = '大家好,今天天气真好!';

    }

    });

    4.指令使用方式(View信息)为:

    <user-info etitle="title">{ {text} }</user-info>

    Etitle指向Controller中的$scope.title。注意命名方式:指令名为userInfo,对应的标签为user-info。

    4.7服务(service

    4.7.1服务介绍

      服务这个概念其实并不陌生,在其他语言中如java便有这样的概念,其作用就是对外提供某个特定的功能,如消息服务,文件压缩服务等,是一个独立的模块。ng的服务是这样定义的:

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

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

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

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

      ng提供了很多内置的服务,可以到API中查看http://docs.angularjs.org/api/。知道了概念,我们来拉一个service出来溜溜,看看到底是个什么用法。  

      我们在controller中直接声明$location服务,这依靠ng的依赖注入机制。$location提供地址栏相关的服务,我们在此只是简单的获取当前的地址。

      服务的使用是如此简单,我们可以把服务注入到controller、指令或者是其他服务中。

    4.7.2自定义服务

      如同指令一样,系统内置的服务以$开头,我们也可以自己定义一个服务。定义服务的方式有如下几种:

    l 使用系统内置的$provide服务;

    l 使用Module的factory方法;

    l 使用Module的service方法。

      下面通过一个小例子来分别试验一下。我们定义一个名为remoteData服务,它可以从远程获取数据,这也是我们在程序中经常使用的功能。不过我这里没有远程服务器,就写死一点数据模拟一下。

    //使用$provide来定义

    var app = angular.module('MyApp', [], function($provide) {

        $provide.factory('remoteData', function() {

     var data = {name:'n',value:'v'};

            return data;

        });

    });

    //使用factory方法

    app.factory('remoteData',function(){

        var data = {name:'n',value:'v'};

        return data;

    });

    //使用service方法

    app.service('remoteData',function(){

        this.name = 'n';

        this.value = 'v';

    });

    Module的factory和$provide的factory方法是一模一样的,从官网文档看它们其实就是一回事。至于Module内部是如何调用的,我此处并不打算深究,我只要知道怎么用就好了。

    再看Module的service方法,它没有return任何东西,是因为service方法本身返回一个构造器,系统会自动使用new关键字来创建出一个对象。所以我们看到在构造器函数内可以使用this,这样调用该服务的地方便可以直接通过remoteData.name来访问数据了。

    4.7.3管理服务的依赖关系

      服务与服务中间可以有依赖关系,例如我们这里定义一个名为validate的服务,它的作用是验证数据是否合法,它需要依赖我们从远程获取数据的服务remoteData。代码如下:

       在factory的参数中,我们可以直接传入服务remoteData,ng的依赖注入机制便帮我们做好了其他工作。不过一定要保证这个参数的名称与服务名称一致,ng是根据名称来识别的。若参数的名次与服务名称不一致,你就必须显示的声明一下,方式如下:

    app.factory('validate',['remoteData',function(remoteDataService){

        return function(){

            if(remoteDataService.name=='n'){

                alert('验证通过');

            }

        };

    }]);  

    我们在controller中注入服务也是同样的道理,使用的名称需要与服务名称一致才可以正确注入。否则,你必须使用$inject来手动指定注入的服务。比如:

    function testC(scope,rd){

        scope.getData = function(){

            alert('name:'+rd.name+'   value:'+rd.value);

        }

    }

    testC.$inject = ['$scope','remoteData'];

     

      在controller中注入服务,也可以在定义controller时使用数组作为第二个参数,在此处

    把服务注入进去,这样在函数体中使用不一致的服务名称也是可以的,不过要确保注入的顺序是一致的,如:

    app.controller('testC',['$scope','remoteData',function($scope,rd){

        $scope.getData = function(){

            alert('name:'+rd.name+'   value:'+rd.value);

        }

    }]); 

    4.7.4 自定义服务示例

    接下来让我们看下例子(例08 自定义服务)代码,自定义userService服务:

    demoApp.factory('userService', ['$http', function($http) {

    var doGetUser = function(userId, path) {

    //return $http({

    //method: 'JSONP',

    //url: path

    //});

    /*手动指定数据*/

    var data = {userId:"woshishui",userName:"我是谁",userInfo:"我是谁!我是谁!"};;

    if(userId=='zhangsan'){

    data = {userId:"zhangsan",userName:"张三",userInfo:"我是张三,我为自己"};

    }else if(userId=='lisi'){

    data = {userId:"lisi",userName:"李四",userInfo:"我是李四,我为卿狂!"};

    }

    return data;

    }

    return {

    /*userService对外暴露的函数,可有多个*/

    getUser: function(userId) { 

    return doGetUser(userId, '../xxx/xxx.action'); 

    }

    };

    }]);

    我们创建了一个只有一个方法的userService,getUser为这个服务从后台获取用户信息的函数,并且对外暴露。当然,由于这是一个静态的例子,无法访问后台,那么我们便制定其返回的数据。

    然后我们把这个服务添加到我们的controller中。我们建立一个controller并加载(或者注入)userService作为运行时依赖,我们把service的名字作为参数传递给controller 函数:

    demoApp.controller("test8Controller", function($scope,userService){

    /*文章信息*/

    $scope.articles = [{

    title : "爱飞像风",

    userId : "zhangsan",

    userName : "张三"

    },{

    title : "无法停止的雨",

    userId : "lisi",

    userName : "李四"

    }];

    $scope.showUserInfo = false;//显示作者详细信息开关

    $scope.currentUser = {}; //当前选中的作者

    $scope.getUserInfo = function(userId){

    $scope.currentUser = userService.getUser(userId);

    //调用 userService的getUser函数

    $scope.showUserInfo = true;

    setTimeout(function(){//定时器:隐藏作者详细信息

    $scope.showUserInfo = false;

    },3000);

    }

    });

    我们的userService注入到我们的test8Controller后,我们就可以像使用其他服务(我们前面提到的$http服务)一样的使用userService了。

    相关的HTML代码如下:

    /* View HTML*/

    <tr ng-repeat="article_ in articles">

    <td>

    {{article_.title}}

    </td>

    <td>

    <a href="javascript:void(0);" target="_blank" rel="nofollow">

    </td>

    </tr>

    ......

    <div ng-show="showUserInfo">

    用户ID:{{currentUser.userId}}<br/>

    用户名:{{currentUser.userName}}<br/>

    用户简介:{{currentUser.userInfo}}<br/>

    </div>

  • 相关阅读:
    SQLMAP注入教程-11种常见SQLMAP使用方法详解
    VS2012/2013/2015/Visual Studio 2017 关闭单击文件进行预览的功能
    解决 IIS 反向代理ARR URLREWRITE 设置后,不能跨域跳转 return Redirect 问题
    Spring Data JPA one to one 共享主键关联
    JHipster 问题集中
    Spring Data JPA 定义超类
    Spring Data JPA查询关联数据
    maven命名
    maven仓库
    Jackson读取列表
  • 原文地址:https://www.cnblogs.com/ndos/p/8331793.html
Copyright © 2011-2022 走看看