Angular(三)

一、使用Module(模块)组织依赖关系

模块提供了一种方法,可以用来组织应用中一块功能区域的依赖关系;同时还提供了一种机制,可以自动解析依赖关系(又叫做依赖注入)。一般来说,我们把这些叫做依赖服务,因为它们会负责为应用提供特殊的服务。

例如:如果购物站点中的一个控制器需要从服务器上获取一个商品列表,那么我们就需要某些对象——把它可以叫做Items——来处理从服务器端获取商品的工作。进而,Items对象就需要以某种方式与服务器上的数据库进行交互。

利用模块和模块内置的依赖注入功能,我们就可以把控制器写的更加简单,假设我们已经把Items对象定义成了一个服务,示例如下:

        function ShoppingController($scope,Items){
            $scope.items=Items.query();
        }

服务都是单例(单个示例)的对象,它们用来执行必要的任务,支撑应用的功能。angular内置了很多服务,例如$location服务,用来和浏览器的地址栏进行交互;$route服务,用来根据URL地址的变化切换视图;还有$http服务,用来和服务器进行交互。

当你需要在多个控制器之间进行交互和共享状态时,这些服务就是一种很好的机制。angular内置的服务以$开头。

以下3个函数可以用来创建一般的服务,它们的复杂度和功能不同。例如:provide、factory、service。

先看下针对Item的使用factory的例子,可以这样编写服务:

<!DOCTYPE html>
<html lang="en" ng-app='ShoppingModule'>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body ng-controller='ShoppingController'>
    <h1>Shop</h1>
    <table>
        <tr ng-repeat="item in items">
            <td>{{item.title}}</td>
            <td>{{item.description}}</td>
            <td>{{item.price|currency}}</td>
        </tr>
    </table>
    <script src='angular-1.3.0.js'></script>
    <script>
    //创建一个模型用来支撑我们的购物视图
        var shoppingModule=angular.module('ShoppingModule',[]);
    //设置好服务工厂,用来创建我们的Items接口,以便访问服务端数据库
        shoppingModule.factory('Items',function(){
            var items={};
            items.query=function(){
                //在真实的应用中,我们会从服务端拉取这块数据
                return [
                    {title:'paint',description:'pots full of paint',price:3.95},
                    {title:'polka',description:'dots with polka',price:2.95},
                    {title:'pebbles',description:'just little rocks',price:6.95}
                ];
            };
            console.log(items);
            return items;
        })
        shoppingModule.controller('ShoppingController',['$scope','Items',function($scope,Items){
            $scope.items=Items.query();
            console.log($scope.items)
        }]);
    </script>
</body>
</html>

当angular创建 ShoppingController 时,它会把$scope对象和刚定义的Items服务作为参数传递进去。这一点是通过参数名匹配来实现的,也就是说,angular会查看我们的 ShoppingController 类的函数签名,然后就会发现它需要一个Items对象。既然我们已经把Items定义成了一个服务,那么angular当然知道去哪里找这个服务了。

以字符串的形式查找这些依赖关系的结果是,可以进行注入的那些函数(例如控制器的构造器)的参数是没有顺序的。

 服务自身可以相互依赖,类似地,可以使用Module接口来定义模块之间的依赖关系。例如:如果你引入了model1和model2,那么应用的模块声明看起来可能会向下面这样:

var appMod = angular.module('app',['model1','model2']);

二、使用过滤器格式化数据

可不必受限于内置的过滤器,自己编写过滤器也非常简单。例如,我们需要为文字创建大写的字符串,代码如下:

<!DOCTYPE html>
<html lang="en" ng-app='MyModule'>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<div ng-controller='myAppCtrl'>
    <h1>{{pageHeading|titleCase}}</h1>
</div>
    <script src='angular-1.3.0.js'></script>
    <script>
        var myModule=angular.module('MyModule',[]);
        myModule.filter('titleCase',function(){
            var titleCaseFilter=function(input){
                var words=input.split(' ');
                console.log(words);
                for (var i=0;i<words.length;i++){
                    words[i]=words[i].charAt(0).toUpperCase()+words[i].slice(1);
                }
                return words.join(' ');
            }
            return titleCaseFilter;
        })
        myModule.controller('myAppCtrl',['$scope',function($scope){
            $scope.pageHeading='behold the majesty of your page title';
        }])      
    </script>
</body>
</html>

三、校验用户输入

angular自动为<form>元素增加了一些很好用的特性,使其更适合开发单页面应用。其中一个特性是,angular允许你为表单中的输入元素定义一个合法的状态,并且只有当所有元素都是合法状态时才允许提交表单。

<!DOCTYPE html>
<html lang="en" ng-app='myModule'>
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
    </head>
    <body ng-controller='signUpCtrl'>
        <h1>Sing Up</h1>
        <form name="addUserForm">
            <div ng-show='message'>{{message}}</div>
            <div>First name:<input type="" name="firstName" ng-model='user.first' required></div>
            <div>Last name:<input type="" name="lastname" ng-model='user.last' required></div>
            <div><button ng-click='addUser()' ng-disabled='!addUserForm.$valid'>submit</button></div>
        </form>
        <script src='angular-1.3.0.js'></script>
        <script>
            var myModule=angular.module('myModule',[]);
            myModule.controller('signUpCtrl',['$scope',function($scope){
                $scope.message='';
                $scope.addUser=function(){
                    $scope.message='thanks,'+$scope.user.first
                }
            }])
        </script>
    </body>
</html>

在控制器中,我们可以通过$valid属性获取表单的校验状态,当表单中的所有输入项都合法时,angular将会把这个属性设置为TRUE。我们可以利用这个属性来做很多很炫的事情,例如当表单没有输入完成时可以禁用submit按钮。

四、作用域

我们经常需要在指令中访问$scope对象,以便观察数据模型的值,当这些值发生变化时刷新UI。

  1. 绑定策略:

    @ 把当前属性作为字符串传递。

    = 绑定当前属性,它带有一个来自指令父scope的属性

    & 传递一个来自父scope的函数,稍后调用。

例如,我们要创建一个expender指令,它会显示一个很小的扩展条,点击的时候扩展条就会展开,显示额外的内容。

<!DOCTYPE html>
<html lang="en" ng-app='myModule'>
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style type="text/css">
            .expender{
                border: 1px solid black;
                width: 250px;
            }
            .expender >.title{
                background: black;
                color: white;
                padding: .1em .3em;
                cursor:pointer;
            }
            .expender > .body{
                padding: .1em .3em
            }
        </style>
    </head>
    <body ng-controller='scopeCtrl'>
        <expender class="expender" expender-title='title'>
            {{text}}
        </expender>
        <script src='angular-1.3.0.js'></script>
        <script>
            var myModule=angular.module('myModule',[]);
            myModule.controller('scopeCtrl',['$scope',function($scope){
                $scope.title='click me to expend';
                $scope.text='Hi there folks,i am the content' + 'that was hidder but is now shown.'
            }]),
            myModule.directive('expender',function(){
                return{
                    restrict:'EA',
                    replace:true,
                    transclude:true,
                    scope:{title:'=expenderTitle'},
                    template:'<div>'+'<div class="title" ng-click="toggle()">{{title}}</div>'+'<div class="body" ng-show="showMe" ng-transclude></div>'+'</div>',
                    link:function(scope,element,attrs){
                        scope.showMe=false;
                        scope.toggle=function toggle(){
                            scope.showMe=!scope.showMe;
                        }
                    }
                }
            })
        </script>
    </body>
</html>

效果图:

标题的值(click me to expend)以及文本(hi there folks...)都来自外层scope。指令中的每一个选项分别代表什么:

  • restrict:EA 可以把这个指令当作一个元素或者一个属性进行调用。也就是说<expender...>...</expender>和<div expender...>...</div>是等价的
  • replace:TRUE 使用我们提供的模板来替换原来的元素
  • transclude: true 把原来元素中的内容移动到我们所提供的模板中
  • scope:{title:=expenderTitle} 创建scope的一个局部属性,名为title,他与父scope中的一个定义在expender-title中的属性绑定。这里,为了方便起见,我们把title重命名为expenderTitle。我们可以把scope编写成{expenderTitle:“=”},然后在模板中用expenderTitle来引用它。注意:这里的命名方式与指令自身一样采用了驼峰法则。
  • template:<div>+... 声明当前指令需要插入的模板。注意:我们使用了ng-click和ng-show来显示隐藏模板,使用ng-transclude来声明吧原来的内容放到那里。同时请注意,用来替换的模板会访问父scope,而不会访问封装它的指令所属的scope。
  • link:... 设置showMe模型来跟踪扩展条的打开、关闭状态,并定义toggle函数,当用户点击所在的div时调用这个函数。

注意,在使用@策略时,我们仍然可以通过双花括号插值语法把title绑定到控制器scope上:

<expender class="expender" expender-title={{title}}>
   {{text}}
</expender>

五、操作DOM元素

link、compile传递的参数,都指向原始的DOM元素。如果加载了jQuery库,那么他们就会指向经过jQuery包装之后的元素。如果不使用jQuery,那么这些DOM元素都位于angular-native包装器jqLite中。jqLite是jQuery API的子集,在angular中我们需要用它来创建所有的东西。对于很多应用来说,只要使用这个轻量级API就可以实现所有你想做的事情了。

如果你需要直接访问原始的DOM元素,你就可以使用element[0]来访问对象中的第一个元素。

在angular文档中的angular.element()部分,可以看到目前支持的完整API列表,你可以使用angular.element()来创建包装在jqLite中的DOM元素。angular对象还带有addClass(),bind(),find(),toggleClass()等函数。当然,这些是jQuery中常用的核心函数,只是angular的实现代码更精致而已。

<!DOCTYPE html>
<html lang="en" ng-app='myModule'>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
        .expender{
            border: 1px solid black;
            width: 250px;
        }
        .expender >.title{
            background: black;
            color: white;
            padding: .1em .3em;
            cursor:pointer;
        }
        .expender > .body{
            padding: .1em .3em
        }
        .closed{
            display: none;
        }
    </style>
</head>
<body ng-controller='scopeCtrl'>
<expender class="expender" expender-title={{title}}>
    {{text}}
</expender>
<script src='angular-1.3.0.js'></script>
<script>
    var myModule=angular.module('myModule',[]);
    myModule.controller('scopeCtrl',['$scope',function($scope){
        $scope.title='click me to expend';
        $scope.text='Hi there folks,i am the content' + 'that was hidder but is now shown.'
    }]),
        myModule.directive('expender',function(){
            return{
                restrict:'EA',
                replace:true,
                transclude:true,
                scope:{title:'@expenderTitle'},
                template:'<div>'+'<div class="title">{{title}}</div>'+'<div class="body closed" ng-transclude></div>'+'</div>',
                link:function(scope,element,attrs){

                    var titleElement=angular.element(element.children().eq(0));
                    var bodyElement=angular.element(element.children().eq(1));

                    console.log(titleElement);
                    titleElement.bind('click',toggle);
                    function toggle() {
                        bodyElement.toggleClass('closed')
                    }
                }
            }
        })
</script>
</body>
</html>

六、控制器

要实现需要彼此通信的嵌套指令,可以使用控制器。<menu>需要知道内部<menu-item>元素的信息,这样它才能够正确地显示和隐藏它们。同样地,<tab-set>需要知道内部<tab>元素的信息。

如前所示,为了创建能够在指令之间进行通信的接口,可以使用controller属性语法把控制器声明成指令的一部分:

  controller:function controllerConstructor($scope,$element,$attrs,$transclude) {
        
    }

controller函数是通过依赖注入的,所以这里所列出的参数列表都是可选的,可以按照其他顺序将其列出,当然这些参数都是具有某种潜在的用途。他们还是可用的服务子集。

通过require属性语法,其他指令可以把这个控制器传递给自己。require的完整形式如下:

require: '^?directiveName'
  • directiveName: 这个以驼峰法则命名的选项名称指定了控制器应该带有哪一条指令,所以,如果<my-menu-item>指令需要在它的父指令<my-menu>上找到一个控制器,就需要把它写成myMenu
  • ^ : 默认情况下,angular会从同一个元素上的命名指令中获取控制器。添加^符号的意思是,需要同时遍历DOM树去查找指令。对于<my-menu>,我们需要添加这个符号
  • ?: 如果没有找到所需要的控制器,angular会抛出一个异常,告诉问题所在。在字符串中添加一个?号的意思是说这个控制器是可选的,如果没有找到,不需要抛出异常。

例如,重写expender指令,让它可以用在一个“accordion”集合中。它会保证当你打开一个扩展条时,集合中的所有其他扩展条都会自动关闭掉。

<!DOCTYPE html>
<html lang="en" ng-app='myModule'>
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
        .expender {
            border: 1px solid black;
            width: 250px;
        }

        .expender > .title {
            background: black;
            color: white;
            padding: .1em .3em;
            cursor: pointer;
        }

        .expender > .body {
            padding: .1em .3em
        }

    </style>
</head>
<body ng-controller='scopeCtrl'>
<accordion>
    <expender class="expender" ng-repeat="expender in expenders" expender-title={{expender.title}}>
        {{expender.text}}
    </expender>
</accordion>

<script src='angular-1.3.0.js'></script>
<script>
    var myModule = angular.module('myModule', []);
    myModule.controller('scopeCtrl', ['$scope', function ($scope) {

        $scope.expenders = [
            {
                title: 'click me to expend',
                text: 'Hi there folks,i am the content' + 'that was hidder but is now shown.'
            },
            {
                title: 'Click this',
                text: 'I am even better text than you have seen previously'
            },
            {
                title: 'No,click me',
                text: 'I am text that should be seen before seeing other texts'
            }
        ]


    }]);
    myModule.directive('accordion', function () {
        return {
            restrict: 'EA',
            replace: true,
            transclude: true,
            scope: {title: '@expenderTitle'},
            template: '<div ng-transclude></div>',
            controller: function () {
                var expenders = [];

                this.gotOpened = function (selectedExpender) {
                    console.log(222);
                    angular.forEach(expenders, function (expender) {
                        if (selectedExpender != expender) {
                            expender.showMe = false;
                        }
                    })
                }
                this.addExpender = function (expender) {
                    expenders.push(expender);
                    console.log(expenders)
                }

            }
        }
    });
    myModule.directive('expender', function () {
        return {
            restrict: 'EA',
            replace: true,
            transclude: true,
            require: '^?accordion',
            scope: {title: '@expenderTitle'},
            template: '<div>' +
            '<div class="title" ng-click="toggle()">{{title}}</div>' +
            '<div class="body" ng-show="showMe" ng-transclude></div>' +
            '</div>',
            link: function (scope, element, attrs, accordionController) {
                //此处的accordionController是由require: '^?accordion'的controller决定的
                scope.showMe = false;
                accordionController.addExpender(scope);
                console.log(accordionController);

                scope.toggle = function toggle() {
                    scope.showMe = !scope.showMe;
                    accordionController.gotOpened(scope);
                }
            }
        }
    })
</script>
</body>
</html>

 

效果图:

以上代码,通过编写accordion指令,做一些元素定位工作。我们把控制器的构造函数以及进行元素定位操作的方法添加到accordion指令中,定义了一个addExpander()函数,扩展条可以调用它来注册自身;还创建了一个可被扩展条调用的gotOpen()函数,通过它,accordion的控制器就知道把其他所有处于打开状态的扩展条都关闭。

在expender指令中,我们扩展它的时候要求accordion的控制器来自它的父元素,然后在合适的时候调用addExpender()和gotOpen()函数。

注意:accordion指令中的控制器创建了一个API接口,有了它,所有扩展条控件之间就可以进行通信了。

 




转载于:https://www.cnblogs.com/xiaoli52qd/p/6497734.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值