让Directive支持传入数据

在第一节中,我们使用的Directive,可以直接获取并显示控制器中的数据(人的名称和性别)。但是,如果我们有多个人的信息需要显示怎么处理?这个问题其实非常常见,因为Directive通常是将需要在界面中重复使用的部分抽象出来,便于一次修改,多处地方生效(如博客的评论的列表显示)。

如果我们按照现在的代码结构(详见第一节),期望放置不同的人的信息,只能采取重复多个控制器的方式,可能的代码如下:

  1. <!DOCTYPE html>
  2. <html lang="zh" ng-app="App">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>{{"学习AngularJS 1.x"}}</title>
  6. <link type="text/css" rel="stylesheet" href="css/style.css">
  7. </head>
  8. <body>
  9. <div ng-controller="FirstCtrl">
  10. <!-- 第一个人的信息 -->
  11. <people></people>
  12. </div>
  13. <div ng-controller="SecondCtrl">
  14. <!-- 第二个人的信息 -->
  15. <people></people>
  16. </div>
  17. <script type="text/javascript" src="components/angular/angular.js"></script>
  18. <script type="text/javascript" src="js/app.js"></script>
  19. </body>
  20. </html>

这样做,无法实现动态化的列表,是无法实现我们期望的功能的。我们理想中的情况应该是什么样子呢?当然是能够并列放置这些Directive,通过传入不同的数据来让Directive展示不同的内容。

  1. <div ng-controller="FirstCtrl">
  2. <!-- 第一个人的信息 -->
  3. <div people='peopleOneInfo'></div>
  4. <!-- 第二个人的信息 -->
  5. <div people='peopleTwoInfo'></div>
  6. </div>

这样的功能,可以通过配置Directive的scope定义实现。

Directive的scope

在之前学习控制器ng-controller的使用过程中,我们使用了$scope功能。$scope用于提供对接HTML和JavaScript对应模块的功能。

而Directive在默认情况下,是没有自动绑定一个$scope的。也就是说,在默认情况下,Directive无法在JavaScript中接收传入的数据(因为缺少一个存储信息的载体),形成我们期望的效果。但是,Directive提供了非常简单的定义一个scope的功能:

  1. App.directive("people", function(){
  2. return {
  3. restrict: "A",
  4. scope:{
  5. info: "="
  6. },
  7. template : "<p>姓名:{{info.name}}</p><p>性别:{{info.sex}}</p>"
  8. }
  9. });
  10. App.controller("FirstCtrl", function ($scope) {
  11. $scope.harry = {
  12. name: "Harry",
  13. sex : "男"
  14. };
  15. });

注意,这里我将restrict从”E”(element元素)改变成为了”A”(attribute 属性),这样它的使用方法有了一些变化:

  1. <div ng-controller="FirstCtrl">
  2. <div people info="harry"></div>
  3. </div>

在HTML代码里,我们为div元素配置了一个people的属性和一个info属性;并将FirstCtrl的$scope.harry传入给了info。最终的显示效果如下:

图5-5 传入数据的Directive

scope中的配置

可以看到,在上方的JavaSciprt文件中,我们对scope的定义使用了如下结构:

  1. scope:{
  2. info: "="
  3. }

首先,scope:{}是告诉这个Directive它需要自己存储信息(类似于建立一个基于这个Directive的$scope)。

info: "=" 这段配置,告诉Directive从HTML标签中,获取名为info的属性,并将它的值存储在scope.info中。这样,我们就达到了存储数据的效果。

在一个ng-ontroller中放入多个相同的Directive

下面,我们在FirstCtrl中增加几个人的数据,并将它们通过Directive显示出来:

  1. //在FirstCtrl中加入如下代码
  2. $scope.anotherPerson = {
  3. name : "张三",
  4. sex : "男"
  5. };
  1. <div ng-controller="FirstCtrl">
  2. <div people info="harry"></div>
  3. <div people info="anotherPerson"></div>
  4. </div>

运行效果如下:

图5-6 在一个控制器中多个Directive

通过ng-repeat和directive一起显示数据

知道了如何传入数据,那么我们就可以将Directive的使用和ng-repeat结合起来,实现列表显示数据的效果。

我们先将FirstCtrl的数据变化为一个array

  1. App.controller("FirstCtrl", function ($scope) {
  2. $scope.people = [
  3. {
  4. name: "Harry",
  5. sex: "男"
  6. },
  7. {
  8. name: "张三",
  9. sex: "男"
  10. }
  11. ];
  12. });
  1. <div ng-controller="FirstCtrl">
  2. <span ng-repeat="person in peopleList">
  3. <div people info="person"></div>
  4. </span>
  5. </div>

实现的效果与上一张图片一样。(具体的页面HTML代码会有差异,请您自行测试查看)

在Directive中修改控制器中的数据

以上我们看到的示例只是将数据显示了出来,如果我们期望在Directive中修改这些数据如何处理呢?

其实很简单,将template中原先显示的数据的部分,替换为input即可。

  1. App.directive("people", function () {
  2. return {
  3. restrict: "A",
  4. scope: {
  5. info: "="
  6. },
  7. template: "<input type='text' ng-model='info.name'><p>性别:{{info.sex}}</p>"
  8. }
  9. });
  1. <div ng-controller="FirstCtrl">
  2. {{ peopleList | json}}
  3. <span ng-repeat="person in peopleList">
  4. <div people info="person"></div>
  5. </span>
  6. </div>

刷新界面后,我们可以在输入框中尝试修改。效果如下:

图5-7 在Directive中修改数据

以只读的方式传入数据

除了以等号=直接传入对象之外,Directive也支持直接传入文本,使用@符号。

  1. App.directive("people", function () {
  2. return {
  3. restrict: "A",
  4. scope: {
  5. name: "@",
  6. sex : "@"
  7. },
  8. template: "<input type='text' ng-model='name'><p>性别:{{sex}}</p>"
  9. }
  10. });
  1. <div ng-controller="FirstCtrl">
  2. {{ peopleList | json}}
  3. <span ng-repeat="person in peopleList">
  4. <!-- 注意此处的数据传入方法 -->
  5. <div people name="{{person.name}}" sex="{{person.sex}}"></div>
  6. </span>
  7. </div>

运行效果:

图5-8 在Directive中传入字符串

可以看到,我们在Directive中传入的数据进行的数据修改,并未反馈到FirstCtrl中。

在Directive中进行函数回调

上面我们介绍了等号=@符号的使用方法,它们分别对应传入对象和文本。但是,如果我们期望传入一个回调函数呢?这样我们就可以实现如封装一个按钮为一个Directive,然后让它在点击后实现我们期望的功能的效果。

这就需要使用到&符号,下面我们来看看实际的例子(这个例子比较复杂,请仔细分析研读):

  1. var App = angular.module("App", []);
  2. App.directive("formDirective", function () {
  3. return {
  4. restrict: "A",
  5. scope: {
  6. //这里使用&符号来接受传入的函数
  7. btnClick: "&"
  8. //注意:这里没有加入下方的value模型
  9. },
  10. template:
  11. //一个用于输入文字的输入框,绑定到value上
  12. "<input type='text' ng-model='value'><br>" +
  13. //提交的按钮,绑定上方scope的btnClick方法
  14. //注意传入参数的方式和HTML中具体使用的方式
  15. "<input type='button' value='提交' ng-click='btnClick({message:value})'>"
  16. }
  17. });
  18. App.controller("FirstCtrl", function ($scope) {
  19. $scope.clickBtnCallback = function (msg) {
  20. alert("点击了按钮!信息是:" + msg);
  21. }
  22. });

对应的HTML代码:

  1. <!DOCTYPE html>
  2. <html lang="zh" ng-app="App">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>{{"学习AngularJS 1.x"}}</title>
  6. <link type="text/css" rel="stylesheet" href="css/style.css">
  7. </head>
  8. <body>
  9. <div ng-controller="FirstCtrl">
  10. <!-- 注意这里绑定btn-click/btnClick中传入的参数的命名 -->
  11. <div form-directive btn-click="clickBtnCallback(message)"></div>
  12. </div>
  13. <script type="text/javascript" src="components/angular/angular.js"></script>
  14. <script type="text/javascript" src="js/app.js"></script>
  15. </body>
  16. </html>

运行结果:

图5-8 在Directive中传入函数和数据回传