Angular - 指令

2019-04-26  本文已影响0人  direwolf_

前言

使用指令的优势在于,我们无需太多关心指令的内部实现(当给 Angular 应用添加所需指令后,Angular 内部会自行编译和运行所有指令),只需把重点放在如何使用指令上即可。

对于指令,我们需要了解一下几个问题:

  1. 什么是指令?
  2. 如何创建指令?
  3. 指令是如何编译和运行的?

什么是指令?

指令就是 AngularJS 扩展具有自定义功能的 HTML 元素的途径,指令又分为内置指令和自定义指令。

内置指令

AngularJS 提供了一系列的内置指令。其中一些指令重载了原生 HTML 元素,比如: <form><a> 标签,当在 HTML 中使用标签时,并不一定可以明确看出是否在使用指令。

基础 ng 属性指令
布尔属性

根据 HTML 标准的定义,布尔属性代表了 truefalse。当这个属性出现时即为 true,未出现为 false。AngularJS 提供了一组布尔属性,通过表达式的值来判断是否在元素上插入或移除该属性。

  1. ng-disabled
    使用 ng-disabled 可以对 disabled 属性进行绑定。
  2. ng-readonly
    使用 ng-readonly 可以对 readonly 属性进行绑定。
  3. ng-checked
    使用 ng-checked 可以对 checked 属性进行绑定。
  4. ng-selected
    使用 ng-selected 可以对 option 标签中的 selected 属性进行绑定。
  <div ng-controller='MyApp'>
        <p><button ng-disabled="isDisabled">ng-disabled = true</button></p>
        <p><button ng-disabled="!isDisabled">ng-disabled = false</button></p>
        <p><input type="text" ng-readonly="isReadonly" value="ng-readonly = true"></p>
        <p><input type="text" ng-readonly="!isReadonly" value="ng-readonly = false"></p>
        <p><input type="checkbox" ng-checked="isChecked">ng-checked = true</p>
        <p><input type="checkbox" ng-checked="!isChecked">ng-checked = false</p>
        <p>
            <select>
                <option>ng-selected = false</option>
                <option ng-selected="isSelected">ng-selected = true</option>
            </select>
        </p>
    </div>
angular.module('myApp', [])
        .controller('MyApp', ['$scope', function ($scope) {
            $scope.isDisabled = true;
            $scope.isReadonly = true;
            $scope.isChecked = true;
            $scope.isSelected = true;
        }])

注: 不是将属性值设为 true 或 false,而是该属性是否出现。

类布尔属性

ng-hrefng-src 等属性虽然不是标准的 HTML 布尔属性,但是由于行为相似,所以 AngularJS 是和布尔属性同等对待的。

  1. ng-href
    url 为动态绑定时,应使用 ng-href 代替 href,AngularJS 会在链接生效后再执行点击行为。
  2. ng-src
    src 地址为动态绑定时,应使用 ng-src 代替 src,AngularJS 会告诉浏览器,在 ng-src 对应的表达式生效之前不要加载图像。
在指令中使用子作用域

ng-appng-controller 是特殊的指令,因为他们会修改嵌套在它们内部的指令的作用域。

  1. ng-app
    任何具有 ng-app 属性的 DOM 元素将会被标记为 $rootScope$rootScope 是作用域链的起始点,任何嵌套在 ng-app 内的指令都会继承它)的起始点。
    注:如果需要在一个页面中放置多个 AngularJS应用,需要手动引导应用。
  2. ng-controller
    在 DOM 元素上放置一个控制器,为嵌套在其中的指令创建一个子作用域,避免将所有操作和模型都定义在 $rootScope 上。
    ng-controller 接收一个必备参数 expression(一个 AngularJS 表达式)。
    以下内置指令具有同样的特性:
  3. ng-include
    使用 ng-include 可以加载、编译并包含外部 HTML 片段到当前应用中。
  4. ng-switch
    ng-switchng-switch-whenon="propertyName" 一起使用,可以在 propertyName 发生变化时渲染不同指令到视图中。
<input type="text" ng-model='person.name'>
<div ng-switch on='person.name'>
    <p ng-switch-default>默认显示</p>
    <p ng-switch-when='show'>满足条件显示</p>
</div>
  1. ng-view
    ng-view 用来设置将被路由管理和放置在 HTML 中的视图的位置。
  2. ng-if
    ng-if 可以根据表达式的值在 DOM 中生成或移除一个元素。
  3. ng-repeat
    ng-repeat 用来遍历一个集合或为集合中的每个元素生成一个模板实例。
.even {
    background: #abcdef;
}
.odd {
    background: #eee;
}
.first {
    background: #ff0;
}
.last {
    background: #f00;
}
<p  ng-repeat="val in list" 
    ng-class="{even: $even, odd: $odd, first: $first, last: $last}">{{$index}}</p>
  1. ng-init
    ng-init 用来设置指令被调用时的内部作用域的初始状态。
  2. {{ }}
    {{ }} 是 AngularJS 内置的模板语法,在 $scope 和视图之间创建绑定,$scope 变化时,视图就会随之自动更新。
    注: {{ }} 实际上是 ng-bind 的简略形式,但在屏幕可视区内使用 {{ }} 会导致页面加载时未渲染的元素发生闪烁,用 ng-bind 可以避免这个问题。
  3. ng-bind
    {{ }}
  4. ng-cloak
    除了使用 ng-bind 来避免闪烁外,还可以在含有 {{ }} 的元素上使用 ng-cloak 指令。
  5. ng-bind-template
    ng-bind 类似,ng-bind-template 用来在视图中绑定多个表达式。
<p ng-bind-template='{{name}}  {{position}}'></p>
  1. ng-model
    ng-model 用来将表单控件同包含它们的作用域中的属性进行绑定。
  2. ng-show / ng-hide
    ng-showng-hide 是通过 CSS 控制元素的显隐(ng-if 则是通过添加或移除 dom)。
  3. ng-change
    在表单输入发生变化时触发。需要与 ng-model 一起使用。
  4. ng-form
    ng-form 用来在一个表单内部嵌套另一个表单。普通的 HTML <form> 标签不允许嵌套,但是 ng-form 可以(所以,只有内部所有子表单都合法时,外部表单才合法)。
    下列 CSS 类会根据表单的验证状态自动设置:
input {
    margin-bottom: 10px;
}
input.ng-invalid {
    border: 1px solid red;
}
input.ng-valid {
    border: 1px solid green;
}
<form name="signup_form" ng-controller="FormCtrl" ng-submit="submitForm()" novalidate>
    <div ng-repeat="field in fields" ng-form="signup_form_input">
        <input type="text"
                name="dynamic_input"
                ng-required="field.isRequired"
                ng-model="field.name"
                placeholder="{{field.placeholder}}" />
        <div ng-show="signup_form_input.dynamic_input.$dirty && signup_form_input.dynamic_input.$invalid">
            <span class="error" ng-show="signup_form_input.dynamic_input.$error.required">必填项</span>
        </div>
    </div>
    <button type="submit" ng-disabled="signup_form.$invalid">提交</button>
</form>
angular.module('myApp', [])
    .controller('FormCtrl', function($scope) {
    $scope.fields = [
        {placeholder: '请输入用户名', isRequired: true},
        {placeholder: '请输入密码', isRequired: true},
        {placeholder: '请输入邮箱(选填)', isRequired: false}
    ];
    $scope.submitForm = function() {
        alert("it works!");
    };
});
  1. ng-click
    ng-click 用来指定元素被点击是所触发的事件。
  2. ng-select
    ng-select 用来将数据同 HTML 的 <select> 元素进行绑定。可以和 ng-modelng-options 一同使用。
    ng-options 的值可以使一个内涵表达式(comprehension expression),简单来说就是它可以接受一个数组或对象,并对它们进行循环,将内部内容提供给 select 标签内部的选项。

(1). 数组作为数据源:

(2). 对象作为数据源:

<div ng-controller="Directive">
    <select ng-model="item" ng-options="item.name for item in list">
        <option value="">who</option>
    </select>
    <p>Winner:{{item.name}}</p>
</div>
angular.module('directive', [])
        .controller('Directive', Directive);
    Directive.$inject = ['$scope'];
    function Directive ($scope) {
        $scope.list = [
            {
                id: 1,
                name: 'player1'
            }, {
                id: 2,
                name: 'player2'
            }, {
                id: 3,
                name: 'player3'
            }
        ];
    }
  1. ng-submit
    ng-submit 用来将表达式同 onsubmit 时间进行绑定。这个指令会阻止默认行为(发送请求并重新加载页面),但前提是表单不含有 action 属性。
  2. ng-class
    ng-class 可以动态设置元素的类,给要动态添加的类绑定一个表达式,当表达式为 true 时,这个类会被添加,反之会被移除。
  3. ng-attr-(suffix)
    当 AngularJS 编译 DOM 时会查找花括号 {{ some expression }} 内的表达式。这些表达式会被自动注册到 $watch 服务中并更新到 $digest 循环中,成为他的一部分:
<p>{{title}}</p>

有时候浏览器会对属性进行严格限制,如 SVG

<svg>
    <circle cx="{{ cx }}"></circle>
</svg>

此时会报错,指出有一个非法属性。这时可以用 ng-attr-(suffix) 解决。

<svg>
    <circle ng-attr-cx="{{ cx }}"></circle>
</svg>
  1. ng-style
    ng-style 为 HTML 元素添加 style 属性,且属性值必须是对象。
<p ng-style="{{style}}"></p>
$scope.style = {
    width: '100px',
    height: '100px',
    background: '#000'
}

自定义指令

定义一个指令
angular.module('directive', [])
    .directive('myDirective', myDirective);

function myDirective () {
    // 一个指令定义对象
    return {
        // 通过设置项定义指令
    }
}

directive() 方法接收两个参数:

  1. name (字符串)
    指令的名字,用来在视图中引用该指令。
  2. factory_function (函数)
    该函数返回一对象,在其中定义这个指令的全部行为。$compile 服务利用这个方法返回的对象,在 DOM 调用指令时来构造指令的行为。
指令的配置项
angular.module('directive', [])
    .directive('myDirective', myDirective);

function myDirective () {
    return {
        restrict: String,
        priority: Number,
        terminal: Boolean,
        template: String or Template Function: function (tElement, tAttrs) {},
        templateUrl: String,
        replace: Boolean or String,
        scope: Boolean or Object,
        transclude: Boolean,
        controller: String or function (scope, element, attrs, transclude, otherInjectables) {},
        controllerAs: String,
        require: String,
        link: function (scope, iElement, iAttrs) {},
        compile: // 返回一个对象或连接函数
            function (tElement, tAttrs, transclude) {
                return {
                    pre: function (scope, iElement, iAttrs, controller) {},
                    post: function (scope, iElement, iAttrs, controller) {}
                }
                // 或者
                return function postLink () {}
            }
    }
}
template: '<div>template</div>',

折行时需在末尾加上反斜线,这样才能正确解析多行字符串。

template: '<div>\
               template\
               <p>wrap</p>\
           </div>',

若值为 true 则意味着模板会替换调用此指令的元素:

<div ng-app='myApp'>
  <div ng-controller='MyCtrl'>
    父:{{value}}
    <directive-dom></directive-dom>
  </div>
</div>
<script>
  angular.module('myApp', [])
    .controller('MyCtrl', ['$scope', function ($scope) {
      $scope.value = 1;
    }])
    .directive('directiveDom', function () {
      return {
        restrict: 'ECMA',
        template: '<div><input type="type" ng-model="value" />子:{{value}}</div>',
        scope: false
      }
    });
</script>
默认值 `false`

(2). scope: true,创建一个新的继承父作用域的新的独立作用域(互不影响);

<div ng-app='myApp'>
  <div ng-controller='MyCtrl'>
    父:{{value}}
    <directive-dom></directive-dom>
  </div>
</div>
<script>
  angular.module('myApp', [])
    .controller('MyCtrl', ['$scope', function ($scope) {
      $scope.value = 1;
    }])
    .directive('directiveDom', function () {
      return {
        restrict: 'ECMA',
        template: '<div><input type="type" ng-model="value" />子:{{value}}</div>',
        scope: true
      }
    });
</script>
scope: true

(3). scope: {} / {...}
a.{}:创建一个隔离父作用域的新作用域

<div ng-app='myApp'>
  <div ng-controller='MyCtrl'>
    父:{{value}}
    <directive-dom></directive-dom>
  </div>
</div>
<script>
  angular.module('myApp', [])
    .controller('MyCtrl', ['$scope', function ($scope) {
      $scope.value = 1;
    }])
    .directive('directiveDom', function () {
      return {
        restrict: 'ECMA',
        template: '<div><input type="type" ng-model="value" />子:{{value}}</div>',
        scope: {}
      }
    });
</script>
scope: {}

b. {...}:可以与父作用域的属性方法进行绑定
绑定的三种形式:
@ (or @attr):绑定字符串
= (or =attr):双向绑定
& (or &attr):绑定方法

<div ng-app='myApp'>
  <div ng-controller="MyCtrl">
    选手 {{obj.id}} 的成绩为: {{obj.value}}
    <directive-dom obj="obj" str="绑定字符串" fn="fn(num)"></directive-dom>
  </div>
</div>
<script>
    (function () {
        'use strict';
        angular
            .module('myApp', [])
            .controller('MyCtrl', MyCtrl)
            .directive('directiveDom', directiveDom);
        MyCtrl.$inject = ['$scope'];
        function MyCtrl($scope) {
            $scope.obj = {
                value: 85,
                id: 1
            };
            $scope.fn = function (num) {
                console.log('我被点击了' + num + '次');
            };
        }
        function directiveDom() {
            return {
                restrivt: 'ECMA',
                template: '<div><button ng-click="nextHandle()">下一位</button><p>{{str}}</p></div>',
                scope: {
                    str: '@',
                    obj: '=',
                    fn: '&'
                },
                link: function ($scope) {
                    var n = 0;
                    $scope.nextHandle = function () {
                        n = n + 1;
                        $scope.obj.id = $scope.obj.id < 10 ? $scope.obj.id + 1 : 1;
                        $scope.obj.value = $scope.obj.id < 10 ? Math.floor(Math.random() * 100 + 1) : 85;
                        $scope.fn({num: n});
                    }
                }
            }
        }
    })();
</script>
scope: {...}
angular.module('myApp', [])
  .directive('directiveDom', function () {
    return {
      restrivt: 'ECMA',
      controller: function ($scope, $element, $attrs, $transclude) {

      }
    }
  });

注:controller 主要是用来提供可以在指令间复用的行为,link 只能在当前内部指令中定义行为,无法在指令间复用

参考资料:
《Angular JS 权威教程》

上一篇 下一篇

猜你喜欢

热点阅读