zoukankan      html  css  js  c++  java
  • 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接口,有了它,所有扩展条控件之间就可以进行通信了。




    努力将自己的温暖带给身边的人!!!!!
  • 相关阅读:
    <s:property>的用法(jsp获取action中的值或者方法)
    struts2 Action获取表单数据
    form标签中id和name属性的区别
    button和submit区别
    hibernate could not resolve property
    Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyExce
    宏定义#define和内联函数inline的区别
    线程绑定cpu
    posix系统线程调度-设置线程优先级
    std::lock_guard和std::unique_lock的区别
  • 原文地址:https://www.cnblogs.com/xiaoli52qd/p/6497734.html
Copyright © 2011-2022 走看看