学习笔记之AngularJS(二)

一、控制器和作用域

控制器是通过AngularJS的Module对象所提供的controller方法而创建出来的。controller方法的参数是新建控制器的名字和一个将被用于创建控制器的函数。

当控制器声明了对于Scope服务的依赖时,就可以使得控制器通过其对应的作用域向视图提供各种能力。 但严格地说,$scope并不是一个服务,而是由一个叫作$rootScope的服务所提供的一个对象,在实际应用中,$scope的使用与服务极为相像,所以简单起见将其称为一个服务。

为什么使用控制器及作用域:

控制器是模型与视图之间的纽带。它从模型中暴露数据给视图,以及基于用户与试图的交互使模型产生变化所需的逻辑。

什么时候使用:

控制器的使用遍及整个AngularJS程序,它可用于向所支持的视图提供作用域。

(一)组织控制器

1. 单块控制器

(1)示例

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Controllers</title>
    <script src="../angular.js"></script>
    <link href="../bootstrap.css" rel="stylesheet" />
    <link href="../bootstrap-theme.css" rel="stylesheet" />
    <script>
        angular.module("exampleApp", [])
            .controller("simpleCtrl", function ($scope) {
            // 当控制器声明了对于Scope服务的依赖时,就可以使得控制器通过其对应的作用域向视图提供各种能力。
                $scope.addresses = {};

                $scope.setAddress = function (type, zip) {
                    console.log("Type: " + type + " " + zip);
                    $scope.addresses[type] = zip;
                }

                $scope.copyAddress = function () {
                    $scope.shippingZip = $scope.billingZip;
                }
            });
    </script>
</head>
<body ng-controller="simpleCtrl">

<div class="well">
    <h4>Billing Zip Code</h4>
    <div class="form-group">
        <input class="form-control" ng-model="billingZip">
    </div>
    <button class="btn btn-primary" ng-click="setAddress('billingZip', billingZip)">
        Save Billing
    </button>
</div>

<div class="well">
    <h4>Shipping Zip Code</h4>
    <div class="form-group">
        <input class="form-control" ng-model="shippingZip">
    </div>
    <button class="btn btn-primary" ng-click="copyAddress()">
        Use Billing
    </button>
    <button class="btn btn-primary"
            ng-click="setAddress('shippingZip', shippingZip)">
        Save Shipping
    </button>
</div>
</body>
</html>

(2)运行结果

 

2. 复用控制器

(1)作用域之间的通信

① 用于发送和接收事件的作用域方法

Method

Description

$broadcast(name, args)

Sends an event from the current scope down to all of the child scopes. The arguments are the name of the event and an object used to provide supplementary data with the event.

$emit(name, args)

Sends an event from the current scope up to the root scope.

$on(name, handler)

Registers a handler function that is invoked when the specified event is received by the scope.

 

② 示例:使用根作用域通信

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Controllers</title>
    <script src="../../angular.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
    <script>
        angular.module("exampleApp", [])
            .controller("simpleCtrl", function ($scope, $rootScope) {

                $scope.$on("zipCodeUpdated", function (event, args) {
                    $scope[args.type] = args.zipCode;
                });

                $scope.setAddress = function (type, zip) {
/*                  当“Save Billing”按钮被单击时,$broadcast方法在根作用域中被调用,
                    向下发送一个zipCodeUpdate事件给作用域的各个层级。*/
                    $rootScope.$broadcast("zipCodeUpdated", {
                        type: type, zipCode: zip
                    });
                    console.log("Type: " + type + " " + zip);
                }
/*                setAddress函数的触发,使作用域与真正知道和采集Billing ZIP Code的控制器得到了联系。
                    使得“Use Billing”按钮得以恢复使用*/
                $scope.copyAddress = function () {
                    $scope.zip = $scope.billingZip;
                }
            });
    </script>

</head>
<body>
<div class="well" ng-controller="simpleCtrl">
    <h4>Billing Zip Code</h4>
    <div class="form-group">
        <input class="form-control" ng-model="zip">
    </div>
    <button class="btn btn-primary" ng-click="setAddress('billingZip', zip)">
        Save Billing
    </button>
</div>
<div class="well" ng-controller="simpleCtrl">
    <h4>Shipping Zip Code</h4>
    <div class="form-group">
        <input class="form-control" ng-model="zip">
    </div>
    <button class="btn btn-primary" ng-click="copyAddress()">
        Use Billing
    </button>
    <button class="btn btn-primary" ng-click="setAddress('shippingZip', zip)">
        Save Shipping
    </button>
</div>
</body>
</html>

 

(2)使用服务调解作用域事件

① 示例

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Controllers</title>
    <script src="../../angular.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
    <script>
        angular.module("exampleApp", [])
/*            ZipCodes服务中声明了对$rootScope服务的依赖,并在setZipCoode方法中使用它来调用$broadcast事件。
        这种惯用法将可能被不同的控制器所需使用的代码放在同一地方,达到了减少重复的目的*/
            .service("ZipCodes", function($rootScope) {
                return {
                    setZipCode: function(type, zip) {
                        this[type] = zip;
                        $rootScope.$broadcast("zipCodeUpdated", {
                            type: type, zipCode: zip
                        });
                    }
                }
            })
            .controller("simpleCtrl", function ($scope, ZipCodes) {

                $scope.$on("zipCodeUpdated", function (event, args) {
                    $scope[args.type] = args.zipCode;
                });

                $scope.setAddress = function (type, zip) {
                    ZipCodes.setZipCode(type, zip);
                    console.log("Type: " + type + " " + zip);
                }

                $scope.copyAddress = function () {
                    $scope.zip = $scope.billingZip;
                }
            });
    </script>
</head>
<body>
<div class="well" ng-controller="simpleCtrl">
    <h4>Billing Zip Code</h4>
    <div class="form-group">
        <input class="form-control" ng-model="zip">
    </div>
    <button class="btn btn-primary" ng-click="setAddress('billingZip', zip)">
        Save Billing
    </button>
</div>
<div class="well" ng-controller="simpleCtrl">
    <h4>Shipping Zip Code</h4>
    <div class="form-group">
        <input class="form-control" ng-model="zip">
    </div>
    <button class="btn btn-primary" ng-click="copyAddress()">
        Use Billing
    </button>
    <button class="btn btn-primary" ng-click="setAddress('shippingZip', zip)">
        Save Shipping
    </button>
</div>
</body>
</html>

 

3. 使用控制器继承

当查找行为时,AngularJS 会从 该指令所应用到的控制器的作用域上开始查找。如果该行为存在,就将会被执行。 如果不存在,AngularJS将会向作用域层级的上一层继续查找,直到具有指定名称的行为被找到。可以利用这一特性在大多数时候使用父控制器中所提供的功能,而只改写需要自定义的部分。这允许你创建为应用程序的不同部分量身定做的控制器,而无需从父控制器中拷贝代码和数据。

(1)示例

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Controllers</title>
    <script src="../../angular.js"></script>
    <script src="controllers.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
</head>
<!--父级控制器-->
<body ng-controller="topLevelCtrl">

<div class="well">
    <h4>Top Level Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <button class="btn btn-default" type="button"
                        ng-click="reverseText()">Reverse</button>
                <button class="btn btn-default" type="button"
                        ng-click="changeCase()">Case</button>
            </span>
        <input class="form-control" ng-model="dataValue">
    </div>
</div>
<!--子控制器,嵌入在父级控制器内-->
<div class="well" ng-controller="firstChildCtrl">
    <h4>First Child Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <!--“Reverse”按钮调用顶层控制器中定义的reverseText行为,-->
                <button class="btn btn-default" type="button"
                        ng-click="reverseText()">Reverse</button>
                <!--“Case”按钮调用firstChildCtrl控制器的changeCase-->
                <button class="btn btn-default" type="button"
                        ng-click="changeCase()">Case</button>
            </span>
        <input class="form-control" ng-model="dataValue">
    </div>
</div>

<div class="well" ng-controller="secondChildCtrl">
    <h4>Second Child Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <button class="btn btn-default" type="button"
                        ng-click="reverseText()">Reverse</button>
                <button class="btn btn-default" type="button"
                        ng-click="changeCase()">Case</button>
                <button class="btn btn-default" type="button"
                        ng-click="shiftFour()">Shift</button>
            </span>
        <input class="form-control" ng-model="dataValue">
    </div>
</div>
</body>
</html>

(2)运行结果

 

因为reverseText行为和数据是在父控制器上定义的,然后被子控制器继承,所以点击任意一个“Reverse”,都能起作用

 

点击First Child Controller的“Case”后,点击任意一个“Reverse”,First Child Controller的值都没有变化。

原因如下:

当读取一个直接在作用域上定义的属性的值时,AngularJS会检查在这个控制器的作用域上是否有一个局部属性, 如果没有,就会沿着作用域层次结构向上查找是否有一个被继承的属性。然而,当使用ng-model指令来修改这样一个属性时,AngularJS会检查当前作用域是否有这样一个名称的属性,如果没有,就会假设你想隐式定义一个这样的属性。结果便是覆盖了该属性值,就像在前面一节中的行为那样。而至于编辑了子控制器中的输入框之后, 就会影响“Reverse”按钮的工作的原因是因为现在会有两个 dataValue 属性一个是被顶层控制器所定义的, 另一个是被编辑的子控制器所定义的。 reverseText行为是在顶层控制器中定义的,只对顶层作用域中定义的dataValue属性起作用,而不会改变子作用域中的data.Value属性。

③ 解决方案:

Inheritance solution.html

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Controllers</title>
    <script src="../../angular.js"></script>
    <script src="Inheritance Solution.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
</head>
<body ng-controller="topLevelCtrl">

<div class="well">
    <h4>Top Level Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <button class="btn btn-primary" type="button"
                        ng-click="reverseText()">Reverse</button>
                <button class="btn btn-primary" type="button"
                        ng-click="changeCase()">Case</button>
            </span>
        <input class="form-control" ng-model="data.dataValue">
    </div>
</div>

<div class="well" ng-controller="firstChildCtrl">
    <h4>First Child Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <button class="btn btn-primary" type="button"
                        ng-click="reverseText()">Reverse</button>
                <button class="btn btn-primary" type="button"
                        ng-click="changeCase()">Case</button>
            </span>
        <input class="form-control" ng-model="data.dataValue">
    </div>
</div>

<div class="well" ng-controller="secondChildCtrl">
    <h4>Second Child Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <button class="btn btn-primary" type="button"
                        ng-click="reverseText()">Reverse</button>
                <button class="btn btn-primary" type="button"
                        ng-click="changeCase()">Case</button>
                <button class="btn btn-primary" type="button"
                        ng-click="shiftFour()">Shift</button>
            </span>
        <input class="form-control" ng-model="data.dataValue">
    </div>
</div>
</body>
</html>

Inheritance solution.js

var app = angular.module("exampleApp", []);

app.controller("topLevelCtrl", function ($scope) {

    $scope.data = {
        dataValue: "Hello, Adam"
    }

    $scope.reverseText = function () {
        $scope.data.dataValue = $scope.data.dataValue.split("").reverse().join("");
    }

    $scope.changeCase = function () {
        var result = [];
        angular.forEach($scope.data.dataValue.split(""), function (char, index) {
            result.push(index % 2 == 1
                ? char.toString().toUpperCase() : char.toString().toLowerCase());
        });
        $scope.data.dataValue = result.join("");
    };
});

app.controller("firstChildCtrl", function ($scope) {

    $scope.changeCase = function () {
        $scope.data.dataValue = $scope.data.dataValue.toUpperCase();
    };
});

app.controller("secondChildCtrl", function ($scope) {

    $scope.changeCase = function () {
        $scope.data.dataValue = $scope.data.dataValue.toLowerCase();
    };

    $scope.shiftFour = function () {
        var result = [];
        angular.forEach($scope.data.dataValue.split(""), function (char, index) {
            result.push(index < 4 ? char.toUpperCase() : char);
        });
        $scope.data.dataValue = result.join("");
    }
});

此时,将文件中的其他对dataValue属性的引用修改为对data对象的该属性进行访问。所以,任意一个按钮对每个值都起作用。

 

4. 使用多控制器

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Controllers</title>
    <script src="../../angular.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
    <script>
        var app = angular.module("exampleApp", []);

        app.controller("firstController", function ($scope) {

            $scope.dataValue = "Hello, Adam";

            $scope.reverseText = function () {
                $scope.dataValue = $scope.dataValue.split("").reverse().join("");
            }
        });

        app.controller("secondController", function ($scope) {

            $scope.dataValue = "Hello, Jacqui";

            $scope.changeCase = function () {
                $scope.dataValue = $scope.dataValue.toUpperCase();
            };
        });
    </script>
</head>
<body>
<div class="well" ng-controller="firstController">
    <h4>First Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <button class="btn btn-primary" type="button"
                        ng-click="reverseText()">Reverse</button>
            </span>
        <input class="form-control" ng-model="dataValue">
    </div>
</div>

<div class="well" ng-controller="secondController">
    <h4>Second Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <button class="btn btn-primary" type="button"
                        ng-click="changeCase()">
                    Case
                </button>
            </span>
        <input class="form-control" ng-model="dataValue">
    </div>
</div>
</body>
</html>

 

(二)使用无作用域的控制器

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Controllers</title>
    <script src="../angular.js"></script>
    <link href="../bootstrap.css" rel="stylesheet" />
    <link href="../bootstrap-theme.css" rel="stylesheet" />
    <script>
        var app = angular.module("exampleApp", [])
            .controller("simpleCtrl", function () {
                this.dataValue = "Hello, Adam";

                this.reverseText = function () {
                    this.dataValue = this.dataValue.split("").reverse().join("");
                }
            });
    </script>
</head>
<body>
<div class="well" ng-controller="simpleCtrl as ctrl">
    <h4>Top Level Controller</h4>
    <div class="input-group">
            <span class="input-group-btn">
                <button class="btn btn-primary" type="button"
                        ng-click="ctrl.reverseText()">Reverse</button>
            </span>
        <input class="form-control" ng-model="ctrl.dataValue">
    </div>
</div>
</body>
</html>

(三) 显式地更新作用域

1. 作用域集成方法

Method

Description

$apply(expression)

Applies a change to the scope

$watch(expression, handler)

Registers a handler that will be notified when the value referred to by the expression changes

$watchCollection(object, handler)

Registers a handler that will be notified when any of the properties of the specified object change

2. 示例

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Controllers</title>
    <script src="../angular.js"></script>
    <link href="../bootstrap.css" rel="stylesheet" />
    <link href="../bootstrap-theme.css" rel="stylesheet" />
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js">
    </script>
    <link rel="stylesheet" href=
            "http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/themes/sunny/jquery-ui.min.css">
    <script>
        $(document).ready(function () {
            $('#jqui button').button().click(function (e) {
                angular.element(angularRegion).scope().$apply('handleClick()');
            });
        });

        var app = angular.module("exampleApp", [])
            .controller("simpleCtrl", function ($scope) {
                $scope.buttonEnabled = true;
                $scope.clickCounter = 0;

                $scope.handleClick = function () {
                    $scope.clickCounter++;
                }

                $scope.$watch('buttonEnabled', function (newValue) {
                    $('#jqui button').button({
                        disabled: !newValue
                    });
                });
            });
    </script>

</head>
<body>
<div id="angularRegion" class="well" ng-controller="simpleCtrl">
    <h4>AngularJS</h4>
    <div class="checkbox">
        <label>
            <input type="checkbox" ng-model="buttonEnabled"> Enable Button
        </label>
    </div>
    Click counter: {{clickCounter}}
</div>
<div id="jqui" class="well">
    <h4>jQuery UI</h4>
    <button>Click Me!</button>
</div>
</body>
</html>

 

3. 运行结果


 

二、 使用过滤器

(一)过滤单个数据的值

Name

Description

currency

This filter formats currency values.

date

This filter formats date values.

json

This filter generates an object from a JSON string.

number

This filter formats a numeric value.

uppercase

These filters format a string into all uppercase or lowercase letters.

lowercase

(二)date过滤器支持的格式化字符串成分

Component

 Description

yyyy

 A four-digit representation of the year (e.g., 2050)

yy

A two-digit representation of the year (e.g., 50)

MMMM

The full month name (e.g., January)

MMM

Short representation of the month (e.g., Jan)

MM

Numeric month, padded to two characters (e.g., 01)

M

Numeric month, without padding (e.g., 1)

dd

Day of month, padded to two characters (e.g., 02)

d

Day of month, no padding (e.g., 2)

EEEE

Full name of day of week (e.g., Tuesday)

EEE

Short name of day of week (e.g., Tue)

HH

Hour in day, 24-hour clock with padding to two characters (e.g., 02)

H

Hour in day, 24-hour clock without padding (e.g., 2)

hh

Hour in day, 12-hour clock with padding to two characters (e.g., 02)

h

Hour in day, 12-hour clock without padding (e.g., 2)

mm

Minute in hour, padded to two characters (e.g., 02)

m

Minute in hour without padding (e.g., 2)

ss

Second in minute, padded to two characters (e.g., 02)

s

Second in minute without padding (e.g., 2)

a

Marker for a.m./p.m.

Z

Four-character representation of time zone

(三)date过滤器支持的快捷格式字符串

Formatting

String Description

medium

Equivalent to MMM d, y h:mm:ss a

short

Equivalent to M/d/yy h:mm a

fullDate

Equivalent to EEEE, MMMM d,y

longDate

Equivalent to MMMM d, y

mediumDate

Equivalent to MMM d, y

shortDate

Equivalent to M/d/yy

mediumTime

Equivalent to h:mm:ss a

shortTime

Equivalent to h:mm a

medium

Equivalent to MMM d, y h:mm:ss a

(四)示例

Filter.js

angular.module("exampleApp")
    .filter("labelCase", function () {
        return function (value, reverse) {
            if (angular.isString(value)) {
                var intermediate =  reverse ? value.toUpperCase() : value.toLowerCase();
                return (reverse ? intermediate[0].toLowerCase() :
                    intermediate[0].toUpperCase()) + intermediate.substr(1);
            } else {
                return value;
            }
        };
    })
    .filter("skip", function () {
        return function (data, count) {
            if (angular.isArray(data) && angular.isNumber(count)) {
                if (count > data.length || count < 1) {
                    return data;
                } else {
                    return data.slice(count);
                }
            } else {
                return data;
            }
        }
    })
    // take过滤器依赖于skip和limitTo过滤器,让他们执行自己的校验并应用自己的转化过程,就像被直接使用一样。
    .filter("take", function ($filter) {
    return function (data, skipCount, takeCount) {
        // 过滤器在worker函数中通过名称来访问和调用
        var skippedData = $filter("skip")(data, skipCount);
        return $filter("limitTo")(skippedData, takeCount);
    }

});

Filter.html

<html ng-app="exampleApp">
<head>
    <title>Filters</title>
    <meta charset="utf-8">
    <script src="../angular.js"></script>
    <link href="../bootstrap.css" rel="stylesheet"/>
    <link href="../bootstrap-theme.css" rel="stylesheet"/>
    <script>
        angular.module("exampleApp", [])
            .controller("defaultCtrl", function ($scope) {
                $scope.products = [
                    {name: "Apples", category: "Fruit", price: 1.20, expiry: 10},
                    {name: "Bananas", category: "Fruit", price: 2.42, expiry: 7},
                    {name: "Pears", category: "Fruit", price: 2.02, expiry: 6},

                    {name: "Tuna", category: "Fish", price: 20.45, expiry: 3},
                    {name: "Salmon", category: "Fish", price: 17.93, expiry: 2},
                    {name: "Trout", category: "Fish", price: 12.93, expiry: 4},

                    {name: "Beer", category: "Drinks", price: 2.99, expiry: 365},
                    {name: "Wine", category: "Drinks", price: 8.99, expiry: 365},
                    {name: "Whiskey", category: "Drinks", price: 45.99, expiry: 365}
                ];

                $scope.getExpiryDate = function (days) {
                    var now = new Date();
                    return now.setDate(now.getDate() + days);
                }

                $scope.limitVal = "5";
                $scope.limitRange = [];
                for (var i = (0 - $scope.products.length);
                     i <= $scope.products.length; i++) {
                    $scope.limitRange.push(i.toString());
                }
                $scope.selectItems = function (item) {
                    return item.category == "Fish" || item.name == "Beer";
                };
                $scope.myCustomSorter = function (item) {
                    return item.expiry < 5 ? 0 : item.price;
                };

            });
    </script>
    <script src="Filter.js"></script>
    <script src="angular-locale_fr-fr.js"></script>
</head>
<body ng-controller="defaultCtrl">
<div class="panel panel-default">
    <div class="panel-heading">
        <h3>
            Products
            <span class="label label-primary">{{products.length}}</span>
        </h3>
    </div>
    <div class="panel-body">
        Limit: <select ng-model="limitVal"
                       ng-options="item for item in limitRange"></select>
    </div>
    <div class="panel-body">
        <table class="table table-striped table-bordered table-condensed">
            <thead>
            <tr>
                <td>Name</td>
                <td>Category</td>
                <td>Expiry:date</td>
                <td>Expiry:shortDate</td>
                <td>Price:currency</td>
                <td>Price:number</td>
            </tr>
            </thead>
            <tbody>
            <!--对products数组应用了limitTo过滤器:limitTo:limitVal-->
            <!--选取项:filter:selectItems或filter:{category:'Fish'}形式-->
            <!--对项目排序:orderBy:'-price',隐式为升序,加“-”为降序-->
            <!--使用函数排序:orderBy:myCustomSorter-->
            <!--使用多个谓语进行排序:orderBy:[myCustomSorter,'-price']-->
            <!--使用自定义集合过滤器:skip:2-->
            <!--在已有的过滤器上搭建take过滤器:take:2:5,值用冒号分隔-->
            <tr ng-repeat="p in products | limitTo:limitVal | orderBy:myCustomSorter| take:2:5">
                <!--改变字符串大小写:uppercase过滤器和lowercase过滤器-->
                <td>{{p.name | uppercase}}</td>
                <!--使用自定义过滤器-->
                <td>{{p.category |  labelCase}}</td>
                <!--格式化日期:date过滤器-->
                <td>{{getExpiryDate(p.expiry)| date:"dd MMM yy"}}</td>
                <!--本地化过滤器输出:-->
                <td>{{getExpiryDate(p.expiry) | date:"shortDate"}}</td>
                <!--格式化货币值:currency过滤器-->
                <td class="text-right">{{p.price | currency:"¥"}}</td>
                <!--格式化其他数字值:number-->
                <td class="text-right">{{p.price | number: 0}}</td>
            </tr>
            <tr ng-repeat="p in products">
                <td colspan="4">{{p|json}}</td>
            </tr>
            </tbody>
        </table>
    </div>
</div>
</body>
</html>

 

三、 创建自定义指令

(一)实现链接函数并打破数据属性的依赖

1. 示例:

<html ng-app="exampleApp">
<head>
    <title>Directives</title>
    <script src="../angular.js"></script>
    <link href="../bootstrap.css" rel="stylesheet" />
    <link href="../bootstrap-theme.css" rel="stylesheet" />
    <script>
        angular.module("exampleApp", [])
            .directive("unorderedList", function () {
                // 因为scope, element, attrs参数只是普通的Javascripte参数,而不是通过依赖注入提供的。所以传入链接函数的对象的顺序是固定的
                return function (scope, element, attrs) {
                    // 从作用域获取数据,使用unorderedList来获取unordered-list属性的值
                    var data = scope[attrs["unorderedList"]];
                    // 添加对指令属性的支持
                    var propertyName=attrs["listProperty"]
                    if (angular.isArray(data)) {
                        // 调用angular.element方法创建一个新元素并且在element参数上使用append方法向文档中插入这个新元素
                        var listElem = angular.element("<ul>");
                        element.append(listElem);
                        for (var i = 0; i < data.length; i++) {
                            // 如果没有jqLite对象却需要一个(例如要创建一个新元素时),就可以使用angular.element方法
                            // 两种方法都返回jqLite对象,可以用于调用其他的jqLite方法,称为方法链技术
                            listElem.append(angular.element('<li>')
                                .text(data[i][propertyName]));
                        }
                    }
                }
            })
            .controller("defaultCtrl", function ($scope) {
                $scope.products = [
                    { name: "Apples", category: "Fruit", price: 1.20, expiry: 10 },
                    { name: "Bananas", category: "Fruit", price: 2.42, expiry: 7 },
                    { name: "Pears", category: "Fruit", price: 2.02, expiry: 6 }
                ];
            });
    </script>
</head>
<body ng-controller="defaultCtrl">
<div class="panel panel-default">
    <div class="panel-heading">
        <h3>Products</h3>
    </div>
    <div class="panel-body">
        <!--应用自定义指令-->
        <!--这里将该指令用作div元素上的一个属性,但是请注意属性名和传给directive方法的参数有所不同:是unordered-list而不是unorderList.-->
        <div unordered-list="products" list-property="name"></div>
    </div>
</div>
</body>
</html>

2. 运行结果:

 

(二)处理数据变化

1. 示例:

<html ng-app="exampleApp">
<head>
    <title>Directives</title>
    <script src="../angular.js"></script>
    <link href="../bootstrap.css" rel="stylesheet" />
    <link href="../bootstrap-theme.css" rel="stylesheet" />
    <script>
        angular.module("exampleApp", [])
            .directive("unorderedList", function () {
                return function (scope, element, attrs) {
                    var data = scope[attrs["unorderedList"]];
                    var propertyExpression = attrs["listProperty"];

                    if (angular.isArray(data)) {
                        var listElem = angular.element("<ul>");
                        element.append(listElem);
                        for (var i = 0; i < data.length; i++) {
                            // 定义“立即调用函数的表达式”(IIFE),这个函数会被立即计算。结构:(function(){.....}());
                            (function () {
                                var itemElement = angular.element('<li>');
                                listElem.append(itemElement);
                                // 对闭包特性加以控制, 以便使用一个固定的或有界的变量来引用数据对象
                                var index=i;
                                var watcherFn = function (watchScope) {
                                    // 监听函数
                                    // IIFE是在一定义时就被执行的,所以index的值将不会被for循环的下一个迭代所更新
                                    return watchScope.$eval(propertyExpression, data[index]);
                                }
                                    // 添加watch方法来监控作用域中的变化,将监听器函数传给$watch方法并指定回调函数
                                    scope.$watch(watcherFn, function (newValue, oldValue) {
                                        // 回调函数使用jqLite 的 text 函数更新 li 元素的文本内容,以反映数据值的变化
                                        itemElement.text(newValue);
                                    });
                            }());
                        }
                    }
                }
            })

            .controller("defaultCtrl", function ($scope) {
                $scope.products = [
                    { name: "Apples", category: "Fruit", price: 1.20, expiry: 10 },
                    { name: "Bananas", category: "Fruit", price: 2.42, expiry: 7 },
                    { name: "Pears", category: "Fruit", price: 2.02, expiry: 6 }
                ];

                $scope.incrementPrices = function () {
                    for (var i = 0; i < $scope.products.length; i++) {
                        $scope.products[i].price++;
                    }
                }
            })
    </script>
</head>
<body ng-controller="defaultCtrl">
<div class="panel panel-default">
    <div class="panel-heading">
        <h3>Products</h3>
    </div>
    <div class="panel-body">
        <button class="btn btn-primary" ng-click="incrementPrices()">
            Change Prices
        </button>
    </div>
    <div class="panel-body">
        <div unordered-list="products" list-property="price | currency"></div>
    </div>
</div>
</body>
</html>

2. 运行结果:

 

(三)使用jqLite工作

1. 对文档对象模型导航及修改元素

(1)关于DOM导航的jqLite方法

Name

Description

children()

Returns the set of child elements. The jqLite implementation of this method does not support the selectors feature that jQuery provides.

eq(index)

Returns an element at the specified index from a collection of elements.

find(tag)

Locates all of the descendant elements with the specified tag name. The jQuery implementation provides additional options for selecting elements, which are not available in the jqLite implementation of this method.

next()

Gets the next sibling element. The jqLite implementation of this method does not support the selectors feature that jQuery provides.

parent()

Returns the parent element. The jqLite implementation of this method does not support the selectors feature that jQuery provides.

(2)用于修改元素的jqLite方法

Name

Description

addClass(name)

Adds all of the elements in the jqLite object to the specified class.

attr(name)

attr(name, value)

Gets the value of the specified attribute for the first element in the jqLite object or sets the specified value for all of the elements.

css(name)

css(name, value)

Gets the value of the specified CSS property from the first element or sets the property to the specified value for all the elements in the jqLite object.

hasClass(name)

Returns true if any of the elements in the jqLite object belong to the specified class.

prop(name)

prop(name, value)

Gets the value of the specified property for the first element in the jqLite object or sets the specified value for all of the elements.

removeAttr(name)

Removes the attribute from all of the elements in the jqLite object.

removeClass(name)

Removes the elements in the jqLite object from the specified class.

text()

text(value)

Gets the concatenated text content from all the elements or sets the text content for all the elements in the jqLite object.

toggleClass(name)

Toggles membership of the specified class for all the elements in the jqLite object. Those elements that were not in the class will be added to it, and those that were in the class will be removed from it.

val()

val(value)

Gets the value attribute for the first element or sets the value attribute for all the elements in the jqLite object.

(3)示例

<html ng-app="exampleApp">
<head>
    <title>Directives</title>
    <script src="../../angular.js"></script>
    <script>
        angular.module("exampleApp", [])
            .directive("demoDirective", function () {
                return function (scope, element, attrs) {
                    // var items = element.children();
                    var items = element.find("li");
                    // 使用css方法设置CSS颜色属性
                    items.css("color","red");
                    // 使用eq方法访问元素
                    for (var i = 0; i < items.length; i++) {
                        if (items.eq(i).text() == "Oranges") {
                            items.eq(i).css("font-weight", "bold");
                        }
                    }
                }
            })
            .controller("defaultCtrl", function ($scope) {
                // controller defines no data or behaviors
            })
    </script>
</head>
<body ng-controller="defaultCtrl">
    <h3>Fruit</h3>
    <!--demoDirective指令处理所应用到元素的子元素-->
    <ol demo-directive>
        <li>Apples</li>
        <ul>
            <li>Bananas</li>
            <li>Cherries</li>
            <li>Oranges</li>
        </ul>
        <li>Oranges</li>
        <li>Pears</li>
    </ol>
</body>
</html>

(4)运行结果

 

2. 创建和移除元素

(1)用于创建和移除元素的jqLite

Name

Description

angular.element(html)

Creates a jqLite object that represents the element specified by the HTML string

after(elements)

Inserts the specified content after the element on which the method is called

append(elements)

Inserts the specified elements as the last child of each element in the jqLite object on which the method has been called

clone()

Returns a new jqLite object that duplicates the elements from the object on which the method is called

prepend(elements)

Inserts the specified elements as the first child of each element in the jqLite object on which the method has been called

remove()

Removes the elements in the jqLite object from the DOM

replaceWith(elements)

 

Replaces the elements in the jqLite object on which the method is called with the specified elements

wrap(elements)

Wraps each element in the jqLite object with the specified elements

 

3. 用于处理事件的jqLite方法

Name

Description

on(events, handler)

Registers a handler for one or more events emitted by the elements represented by the jqLite object. The jqLite implementation of this method does not support the selectors or event data features that jQuery provides.

off(events, handler)

Removes a previously registered handler for the specified events from the elements represented by the jqLite object. The jqLite implementation of this method does not support the selectors feature that jQuery provides.

triggerHandler(event)

Triggers all of the handlers for the specified event registered on the elements

4. 其他jqLite方法

Name

Description

data(key, value)

data(key)

Associates arbitrary data with all the elements or retrieves a value for the specified key from the first element represented by the jqLite object

removeData(key)

Removes data associated with the specified key from the elements represented by the jqLite object

html()

Returns an HTML representation of the content of the first element represented by the jqLite object

ready(handler)

Registers a handler function that will be invoked once the contents of the DOM are fully loaded

5. 从jqLite访问AngularJS的特性

(1)可以访问AngularJS特性的其他方法

Name

Description

controller()

controller(name)

Returns the controller associated with the current element or its parent.

injector()

Returns the injector associated with the current element or its parent.

isolatedScope()

Returns an isolated scope if there is one associated with the current element.

scope()

Returns the scope associated with the current element or its parent.

inheritedData(key)

This method performs the same function as the jQuery data method but will walk up the element hierarchy looking for a value to match the specified key.

6. 示例

<html ng-app="exampleApp">
<head>
    <title>Directives</title>
    <script src="../../angular.js"></script>
    <script>
        angular.module("exampleApp", [])
            .directive("demoDirective", function () {
                return function (scope, element, attrs) {
/*
                    原代码:
                    var listElem = element.append("<ol>");
                    for (var i = 0; i < scope.names.length; i++) {
                        listElem.append("<li>").append("<span>").text(scope.names[i]);
                    }
                    问题:每次for循环将字符串替换掉
*/
                    // 解决方案:使用angular.element方法创建jqLite对象并在单独的语句中对他们执行各种操作
                    var listElem = angular.element("<ol>");
                    element.append(listElem);
                    for (var i = 0; i < scope.names.length; i++) {
                        listElem.append(angular.element("<li>")
                            .append(angular.element("<span>").text(scope.names[i])));
                    }
                    // 添加数据处理器
                    var buttons = element.find("button");
                    buttons.on("click",function (e) {
                        element.find("li").toggleClass("bold");
                    });
                }
            })

            .controller("defaultCtrl", function ($scope) {
                $scope.names = ["Apples", "Bananas", "Oranges"];
            })
    </script>
    <style>
        .bold { font-weight: bold; }
    </style>
</head>
<body ng-controller="defaultCtrl">
    <h3>Fruit</h3>
    <div demo-directive>
        <button>Click Me</button>
    </div>
</body>
</html>

 

7. 运行结果

 

(四)使用jQuery替换jqLite

1. 示例

<html ng-app="exampleApp">
<head>
    <title>Directives</title>
    <!--注意:jQuery脚本元素出现在加载AngularJS的脚本元素之前。如果直到AngularJS的后面才加载jQuery,那么jqLite就会被使用。-->
    <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
    <script src="../../angular.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
    <script>
        angular.module("exampleApp", [])
            .directive("unorderedList", function () {
                return function (scope, element, attrs) {
                    var data = scope[attrs["unorderedList"]];
                    var propertyExpression = attrs["listProperty"];
                    if (angular.isArray(data)) {
/*
                        多行jqLite语句
                        var listElem = angular.element("<ul>");
                        element.append(listElem);

                        替换成jQuery语句:
*/
                        var listElem = angular.element("<ul>").appendTo(element);
                        for (var i = 0; i < data.length; i++) {
                            (function () {
                                var itemElement =
                                    angular.element("<li>").appendTo(listElem);
                                var index = i;
                                var watcherFn = function (watchScope) {
                                    return watchScope.$eval(propertyExpression,
                                        data[index]);
                                }
                                scope.$watch(watcherFn, function (newValue, oldValue) {
                                    itemElement.text(newValue);
                                });
                            }());
                        }
                    }
                }
            }).controller("defaultCtrl", function ($scope) {
            $scope.products = [
                { name: "Apples", category: "Fruit", price: 1.20, expiry: 10 },
                { name: "Bananas", category: "Fruit", price: 2.42, expiry: 7 },
                { name: "Pears", category: "Fruit", price: 2.02, expiry: 6 }
            ];

            $scope.incrementPrices = function () {
                for (var i = 0; i < $scope.products.length; i++) {
                    $scope.products[i].price++;
                }
            }
        })
    </script>
</head>

<body ng-controller="defaultCtrl">
<div class="panel panel-default">
    <div class="panel-heading">
        <h3>Products</h3>
    </div>
    <div class="panel-body">
        <button class="btn btn-primary" ng-click="incrementPrices()">
            Change Prices
        </button>
    </div>
    <div class="panel-body">
        <div unordered-list="products" list-property="price | currency"></div>
    </div>
</div>
</body>
</html>

四、 创建复杂指令

(一)定义复杂指令

1. 由指令定义对象所定义的属性

Name

Description

compile

Specifies a compile function.

controller

Creates a controller function for the directive.

link

Specifies the link function for the directive.

replace

Specifies whether the contents of the template replace the element that the directive has been applied to.

require

Declares a dependency on a controller.

restrict

Specifies how the directive can be applied.

scope

Creates a new scope or an isolated scope for the directive.

template

Specifies a template that will be inserted into the HTML document.

templateUrl

Specifies an external template that will be inserted into the HTML document.

transclude

Specifies whether the directive will be used to wrap arbitrary content.

2. 用于配置restrict定义选项的字母

Letter

Description

E

Allows the directive to be applied as an element

A

Allows the directive to be applied as an attribute

C

Allows the directive to be applied as a class

M

Allows the directive to be applied as a comment

实际项目中很少有一个指令能够以四种方式全部适用的,restrict定义属性的最常见值是A(指令仅能被用作一个属性)、E(指令仅能被用作一个元素),或者AE(指令可被用作一个元素或者一个属性)。正如下面各节中将要解释的,C和M选项鲜有用到。

 

3. 将指令当作一个元素使用

<div class="panel-body">
    <unordered-list list-source="products" list-property="price | currency" />
</div>

如果没有可用的unordered-list属性时,检查新属性的值:

var data = scope[attrs["unorderedList"] || attrs["listSource"]];

 

4. 将指令当作一个属性使用

<div class="panel-body">
  <div unordered-list="products" list-property="price | currency"></div>
</div>

5. 将指令当作一个类的属性值使用

<div class="panel-body">
    <div class="unordered-list: products, price | currency"></div>
</div>

6. 将指令当作一个注释来使用

这个注释必须以单词directive开始,跟随一个冒号、指令名以及可选的配置参数。

<div class="panel-body">
  <!-- directive: unordered-list products -->
</div>

用了可选参数来指定数据源井更新链接函数来设置属性表达式的默认值:

var propertyExpression = attrs["listProperty"] || "price | currency";

使用jqLite定位并操作注释元素的父元素,修改链接函数的操作方式以支持注释方式:

if (element[0].nodeName == "#comment") {
  element.parent().append(listElem);
} else {
  element.append(listElem);

(二)使用指令模板

<html ng-app="exampleApp">
<head>
    <title>Directives</title>
    <script src="../../angular.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
    <script>
        angular.module("exampleApp", [])
            .directive("unorderedList", function () {
                return {
                    link: function (scope, element, attrs) {
                        scope.data = scope[attrs["unorderedList"]];
                    },
                    restrict: "A",
                    template: "<ul><li ng-repeat='item in data'>"
                    + "{{item.price | currency}}</li></ul>"
                }
            }).controller("defaultCtrl", function ($scope) {
            $scope.products = [
                { name: "Apples", category: "Fruit", price: 1.20, expiry: 10 },
                { name: "Bananas", category: "Fruit", price: 2.42, expiry: 7 },
                { name: "Pears", category: "Fruit", price: 2.02, expiry: 6 }
            ];
        })
    </script>
</head>
<body ng-controller="defaultCtrl">
<div class="panel panel-default">
    <div class="panel-heading">
        <h3>Products</h3>
    </div>
    <div class="panel-body">
        <div unordered-list="products">
            This is where the list will go
        </div>
    </div>
</div>
</body>
</html>

1. 使用函数作为模板

......
template: function () {
return angular.element(
document.querySelector("#listTemplate")).html();
}
......

2. 使用外部模板

......
templateUrl: "itemTemplate.html"
......
 <div unordered-list="products">
    This is where the list will go
</div>
......

3. 通过函数选择一个外部模板

......
	templateUrl: function (elem, attrs) {
		return attrs["template"] == "table" ?
		"tableTemplate.html" : "itemTemplate.html";
}
......
    <div class="panel-body">
        <div unordered-list="products">
            This is where the list will go
        </div>
    </div>
    <div class="panel-body">
        <div unordered-list="products" template="table">
            This is where the list will go
        </div>
    </div>
......

4. 替换元素

replace属性不仅仅知识用模板替换了元素,还将元素中的属性也转移给了模板内容

(1)示例

<html ng-app="exampleApp">
<head>
    <title>Directives</title>
    <script src="../../angular.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
    <script>
        angular.module("exampleApp", [])
            .directive("unorderedList", function () {
                return {
                    link: function (scope, element, attrs) {
                        scope.data = scope[attrs["unorderedList"]];
                    },
                    restrict: "A",
                    templateUrl: "tableTemplate.html",
                    replace: true
                }

            }).controller("defaultCtrl", function ($scope) {
            $scope.products = [
                { name: "Apples", category: "Fruit", price: 1.20, expiry: 10 },
                { name: "Bananas", category: "Fruit", price: 2.42, expiry: 7 },
                { name: "Pears", category: "Fruit", price: 2.02, expiry: 6 }
            ];
        })
    </script>
</head>
<body ng-controller="defaultCtrl">
<div class="panel panel-default">
    <div class="panel-heading">
        <h3>Products</h3>
    </div>
    <div class="panel-body">
        <div unordered-list="products" class="table table-striped"
             ng-repeat="count in [1, 2, 3]">
            This is where the list will go
        </div>
    </div>
</div>
</body>
</html>

(2)运行结果:

无有 replace: true

 

(三)管理指令的作用域

默认情况下,链接函数被传入了控制器的作用域,而该控制器管理着的视图包含了指令所应用到的元素。

1.创建多个控制器并给每个指令实例创建其作用域

当无法控制所使用的指令的源代码时,可以重用指令来为指令的每个实例创建单独的控制器,使得每个实例有自己的作用域。

(1)代码示例:

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Directive Scopes</title>
    <script src="../../angular.js"></script>
    <link href="../../bootstrap.css" rel="stylesheet" />
    <link href="../../bootstrap-theme.css" rel="stylesheet" />
    <script type="text/ng-template" id="scopeTemplate">
        <div class="panel-body">
            <p>Name: <input ng-model="data.name" /></p>
            <!--data.name这个属性是在一个对象上定义的,意味着这个值将会在指令的各个实例之间所共享,而且所有绑定到该属性的输入框元素将会保持同步-->
            <p>City: <input ng-model="city" /></p>
            <!--city这个属性是在控制器的作用域上被直接赋值的,意味着指令所有的作用域将会从同一个初始值开始,但是在输入框元素被修改时会在自己的作用域上创建和修改自己的版本-->
            <p>Country: <input ng-model="country" /></p>
            <!--country这个属性没有被赋任何值。 当相应的输入框元素被修改时, 指令的每个实例将会创建出独立的country属性-->
        </div>
    </script>
    <script type="text/javascript">
        angular.module("exampleApp", [])
            .directive("scopeDemo", function () {
                return {
                    template: function() {
                        return angular.element(
                            document.querySelector("#scopeTemplate")).html();
                    },
                    // 给每个指令创建自己的作用域:设置scope属性为true将允许在同一个控制器里复用一个指令。
                    scope: true
                }
            })
            .controller("scopeCtrl", function ($scope) {
                $scope.data = { name: "Adam" };
                $scope.city = "London";
            })
            .controller("secondCtrl", function ($scope) {
                // do nothing - no behaviours required
            });
    </script>
</head>
<body>
<!--为每个指令创建单独控制器,使用两个控制器,于是有了两个作用域,这两个作用域都有自己的name属性,这允许输入框元素可以各自独立地运作。-->
<div ng-controller="scopeCtrl" class="panel panel-default">
    <!--一个指令的两个实例,在用一个控制器上更新同一个name属性-->
    <div class="panel-body" scope-demo></div>
    <div class="panel-body" scope-demo></div>
</div>
<div ng-controller="secondCtrl" class="panel panel-default">
    <div class="panel-body" scope-demo></div>
    <div class="panel-body" scope-demo></div>
</div>

</body>
</html>

(2)运行结果:

(3)作用域说明:

① 一个指令的不同实例在控制器作用域上操作:

② 为指令的每个实例创建一个控制器的效果:

③ 在单个控制器里给每个指令实例自己的作用域:

 

2.创建隔离的作用域

在创建一个打算在许多各种不同情况下复用的指令时,以及不想要任何由控制器或作用域层次上的其他地方定义的对象和属性导致的继承时,可以创建一个隔离的作用域。

(1)通过属性值进行单项绑定

①代码示例:

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Directive Scopes</title>
    <script src="../../../angular.js"></script>
    <link href="../../../bootstrap.css" rel="stylesheet" />
    <link href="../../../bootstrap-theme.css" rel="stylesheet" />
    <script type="text/ng-template" id="scopeTemplate">
        <div class="panel-body">
            <p>Data Value: {{local}}</p>
            <!--使用内联的绑定表达式来显示属性local的值。-->
        </div>
    </script>
    <script type="text/javascript">
        angular.module("exampleApp", [])
            .directive("scopeDemo", function () {
                return {
                    template: function() {
                        return angular.element(
                            document.querySelector("#scopeTemplate")).html();
                    },
                    // 在 scope 定义对象中,设置了在指令作用域内一个特性和一个属性之间的一个单项映射
                    // 指定了属性 local 的值应该从一个来自名为 nameprop 的特性的单向绑定来获得。
                    scope: {
                        local: "@nameprop"
                    }
                }
            })
            .controller("scopeCtrl", function ($scope) {
                $scope.data = { name: "Adam" };
            });
    </script>
</head>
<body ng-controller="scopeCtrl">
<div class="panel panel-default">
    <div class="panel-body">
        Direct Binding: <input ng-model="data.name" />
    </div>
    <!--在元素上定义nameprop特性-->
    <div class="panel-body" scope-demo nameprop="{{data.name}}"></div>
    <!--通过单项数据绑定宠用一个指令-->
    <div class="panel-body" scope-demo nameprop="{{data.name + 'Freeman'}}"></div>
</div>
</body>
</html>

② 运行结果:

③ 在隔离作用域上的单项数据绑定的效果:

3.创建双向绑定

(1)代码示例

<!DOCTYPE html>
<html ng-app="exampleApp">
<head>
    <title>Directive Scopes</title>
    <script src="../../../angular.js"></script>
    <link href="../../../bootstrap.css" rel="stylesheet" />
    <link href="../../../bootstrap-theme.css" rel="stylesheet" />
    <script type="text/ng-template" id="scopeTemplate">
        <div class="panel-body">
            <!-- -->
            <!--传递来自隔离作用域的数据,更新计算表达式的模板中的绑定,传入一个为表达式参数提供值的对象-->
            <p>Name: {{local}}, City: {{cityFn({nameVal:local})}}</p>
        </div>
    </script>
    <script type="text/javascript">
        angular.module("exampleApp", [])
            .directive("scopeDemo", function () {
                return {
                    template: function () {
                        return angular.element(
                            document.querySelector("#scopeTemplate")).html();
                    },
                    scope: {
                        // “@”替换为“=”
                        local: "=nameprop",
                        // 前缀“&”用于将city特性的值绑定到cityFn函数。
                        cityFn: "&city"
                    }
                }
            })
            .controller("scopeCtrl", function ($scope) {
                $scope.data = {
                    name: "Adam",
                    defaultCity: "London"
                };

                $scope.getCity = function (name) {
                    return name == "Adam" ? $scope.data.defaultCity : "Unknown";
                }
            });
    </script>
</head>
<body ng-controller="scopeCtrl">
<div class="panel panel-default">
    <div class="panel-body">
        Direct Binding: <input ng-model="data.name" />
    </div>
    <!--nameprop="{{data.name}}"去掉"{{}}"-->
    <!--修改表达式city="getCity(data.name)"以便传递给行为的参数是在控制器作用域上没有被定义过的属性名-->
    <div class="panel-body" scope-demo
         city="getCity(data.name)" nameprop="data.name"></div>
</div>
</body>
</html>

(2)运行结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值