文档章节

带你走近AngularJS - 体验指令实例

葡萄城技术团队
 葡萄城技术团队
发布于 2014/06/11 12:17
字数 2566
阅读 84
收藏 1

带你走近AngularJS系列:

  1. 带你走近AngularJS - 基本功能介绍

  2. 带你走近AngularJS - 体验指令实例

  3. 带你走近AngularJS - 创建自定义指令

------------------------------------------------------------------------------------------------


之前我们已经介绍了所有的AngularJS 基础知识,下面让我们通过实例来加深记忆,体验自定义指令的乐趣。

手风琴指令

我们展示的第一个例子是手风琴效果指令:

效果图如下:

image    
在线实例地址:手风琴指令

不使用AngularJS的纯HTML源码如下:

<div class="accordion" id="accordion2">
  <div class="accordion-group">
    <div class="accordion-heading">
      <a class="accordion-toggle" data-toggle="collapse"
         data-parent="#accordion2" href="#collapseOne">
        Collapsible Group Item #1
      </a>
    </div>
    <div id="collapseOne" class="accordion-body collapse in">
      <div class="accordion-inner">
        Anim pariatur cliche...
      </div>
    </div>
  </div>
  <div class="accordion-group">
    <div class="accordion-heading">
      <a class="accordion-toggle" data-toggle="collapse"
        data-parent="#accordion2" href="#collapseTwo">
        Collapsible Group Item #2
      </a>
    </div>
    <div id="collapseTwo" class="accordion-body collapse">
      <div class="accordion-inner">
        Anim pariatur cliche...
      </div>
    </div>
  </div>
</div>

以上纯 HTML源码也可以实现手风琴效果,但是它仅仅是一些标记,包含了大量的链接和id,不利于维护。

使用AngularJS自定义指令结合以下HTML源码同样可以得到预期效果:

<body ng-app="btst">
     <h3>BootStrap手风琴指令</h3>

    <btst-accordion>
        <btst-pane title="<b>基本功能</b>" category="{name:'test'}">
            <div>AngularJS......</div>
        </btst-pane>
        <btst-pane title="<b>创建自定义指令</b>">
            <div>使用过 AngularJS ......</div>
        </btst-pane>
        <btst-pane title="<b>体验实例</b>">
            <div>之前我们已经介绍了所有的AngularJS......</div>
        </btst-pane>
    </btst-accordion>
</body>

这一版使用的HTML标记更少,看起来清晰且易维护。

下面,让我们看下指令写法。

首先,我们定义模块"btstAccordion" 指令:

var btst = angular.module("btst", []);
btst.directive("btstAccordion", function () {
  return {
    restrict: "E",        
    transclude: true,      
    replace: true,        
    scope: {},            
    template:             
      "<div class='accordion' ng-transclude></div>",
    link: function (scope, element, attrs) {

      // 确保 accordion拥有id
      var id = element.attr("id");
      if (!id) {
        id = "btst-acc" + scope.$id;
        element.attr("id", id);
      }

      // set data-parent and href attributes on accordion-toggle elements
      var arr = element.find(".accordion-toggle");
      for (var i = 0; i < arr.length; i++) {
        $(arr[i]).attr("data-parent", "#" + id);
        $(arr[i]).attr("href", "#" + id + "collapse" + i);
      }

      // set collapse attribute on accordion-body elements 
      // and expand the first pane to start
      arr = element.find(".accordion-body");
      $(arr[0]).addClass("in"); // expand first pane
      for (var i = 0; i < arr.length; i++) {
        $(arr[i]).attr("id", id + "collapse" + i);
      }
    },
    controller: function () {}
  };
});

由于拥有内部HTML内容,所以设置指令的transclude 属性为true。模板使用ng-transclude 指令来声明对应的显示内容。由于模板中只有一个元素,所以没有设置其他选项。

代码中最有趣的部分是link 方法。它在参数element具有id时启作用,如果没有,会依据指令的 Scope自动创建ID。一旦元素拥有了ID值,方法将通过jQuery来选择具有"accordion-toggle"类的子元素并且设置它的 "data-parent" 和 "href" 属性。最后,通过寻找“accordion-body” 元素,并且设置"collapse" 属性。

指令同时声明了一个拥有空方法的controller 。声明controller 是必要的,因为Accordion会包含子元素,子元素将检测父元素的类型和controller 。


下一步需要定义手风琴选项卡的指令。

这一步比较容易,大多数操作将在这个模板中发生,但是它仅仅需要少量的代码:

btst.directive('btstPane', function () {
  return {
    require: "^btstAccordion",
    restrict: "E",
    transclude: true,
    replace: true,
    scope: {
      title: "@"
    },
    template:
      "<div class='accordion-group'>" +
      "  <div class='accordion-heading'>" +
      "    <a class='accordion-toggle' data-toggle='collapse'>{{title}}</a>" +
      "  </div>" +
      "<div class='accordion-body collapse'>" +
      "  <div class='accordion-inner' ng-transclude></div>" +
      "  </div>" +
      "</div>",
    link: function (scope, element, attrs) {
      scope.$watch("title", function () {
        // NOTE: this requires jQuery (jQLite won't do html)
        var hdr = element.find(".accordion-toggle");
        hdr.html(scope.title);
      });
    }
  };
});

require 属性值为"btstPane" ,所以该指令必须用于指令"btstAccordion"中。transclude 属性为true表明选项卡包含HTML标签。scope 下的 "title" 属性将会被实例所替代。

这个例子中的模板比较复杂。注意我们通过ng-transclude 指令来标记元素接收文本内容。

模板中"{{title}}" 属性将会显示标签名称。目前我们仅仅实现了纯文本显示,没有定义其样式。我们使用link 方法可以替换标题为HTML源码从而得到更丰富的样式。

就这样,我们完成了第一个具有实用价值的指令。它功能并不复杂但是足以展示一些AngularJS的重要知识点和技术细节:如何定义嵌套指令,如何生成唯一的元素ID,如何使用jQuery操作DOM以及如何使用$watch 方法监听scope变量的变化。

Google Maps 指令

下一个例子是创建Google地图的指令:

image  
Google Maps 指令

在我们创建指令之前,我们需要添加Google APIs 引用到页面中:

<!-- required to use Google maps -->

<script type="text/javascript"

src="https://maps.googleapis.com/maps/api/js?sensor=true">

</script>

接下来,我们创建指令:

var app = angular.module("app", []);
app.directive("appMap", function () {
  return {
    restrict: "E",
    replace: true,
    template: "<div></div>",
    scope: {
      center: "=",        // Center point on the map
      markers: "=",       // Array of map markers
      width: "@",         // Map width in pixels.
      height: "@",        // Map height in pixels.
      zoom: "@",          // Zoom level (from 1 to 25).
      mapTypeId: "@"      // roadmap, satellite, hybrid, or terrain
    },

center 属性进行了双向绑定。这个应用可以改变地图中心和交互地图(当用户通过鼠标按钮选择地图位置时)。同时,地图也会在用户通过滚动选择地图位置时通知应用更新当前显示位置。

markers 属性被定义为引用因为它是数组形式,把它序列化为字符串比较耗时。link 方法可以实现以下功能:

1. 初始化地图

2. 在用户视图变量更改时更新地图

3. 监听事件

以下是实现代码:

link: function (scope, element, attrs) {
  var toResize, toCenter;
  var map;
  var currentMarkers;

  // listen to changes in scope variables and update the control
  var arr = ["width", "height", "markers", "mapTypeId"];
  for (var i = 0, cnt = arr.length; i < arr.length; i++) {
    scope.$watch(arr[i], function () {
      if (--cnt <= 0)
        updateControl();
    });
  }

  // update zoom and center without re-creating the map
  scope.$watch("zoom", function () {
    if (map && scope.zoom)
      map.setZoom(scope.zoom * 1);
  });
  scope.$watch("center", function () {
    if (map && scope.center)
    map.setCenter(getLocation(scope.center));
  });

监测方法正如我们在文章开始时描述的,变量发生变化后,它将调用updateControl 方法。updateControl 方法实际上使用selected 选项创建了新的地图。

"zoom" 和 "center" 变量将被分别处理,因为我们不希望每次在用户选择或缩放地图时都重新创建地图。这两个方法检测地图是否重新创建还是仅仅是简单的更新。

以下是updateControl 方法的实现方法:

// update the control
function updateControl() {

  // get map options
  var options = {
    center: new google.maps.LatLng(40, -73),
    zoom: 6,
    mapTypeId: "roadmap"
  };
  if (scope.center) options.center = getLocation(scope.center);
  if (scope.zoom) options.zoom = scope.zoom * 1;
  if (scope.mapTypeId) options.mapTypeId = scope.mapTypeId;
  // create the map and update the markers
  map = new google.maps.Map(element[0], options);
  updateMarkers();

  // listen to changes in the center property and update the scope
  google.maps.event.addListener(map, 'center_changed', function () {
    if (toCenter) clearTimeout(toCenter);
    toCenter = setTimeout(function () {
    if (scope.center) {
      if (map.center.lat() != scope.center.lat ||
          map.center.lng() != scope.center.lon) {
        scope.center = { lat: map.center.lat(), lon: map.center.lng() };
        if (!scope.$$phase) scope.$apply("center");
      }
    }
  }, 500);
}

updateControl 方法首先需要接收Scope设置相关参数,接着使用options 创建和初始化地图。这是创建JavaScript指令的常见模式。

创建地图之后,方法会在更新标记的同时添加检测事件,以便监视地图中心位置的变化。该事件会监测当前的地图中心是否和Scope中的相同。如果不同,即会更新scope,调用$apply 方法通知AngularJS属性已经更改。这种绑定方式为双向绑定。

updateMarkers 方法十分的简单,几乎和AngularJS分离,所以我们在这里就不介绍了。

除了这个地图指令特有的功能,这个例子还展示了:

1. 两个过滤器转换坐标为常规数字到地理位置,例如33&deg;38'24"N, 85&deg;49'2"W。

2. 一个地理编码器,转换成地址的地理位置(也是基于谷歌的API)。

3. 使用HTML5的地理定位服务来获取用户当前位置的方法。

Google地图 APIs 是极其丰富的。以下是一些资源入口:

Google地图APIs 文档: https://developers.google.com/maps/documentation/

Google许可条款:https://developers.google.com/maps/licensing


Wijmo Grid 指令

最后一个例子是可编辑的表格指令:

image

Wijmo Grid 指令

这里展示的图表插件是 Wijmo 前端插件套包中的一款插件 wijgrid 插件:

<wij-grid
  data="data"
  allow-editing="true"
  after-cell-edit="cellEdited(e, args)" >
    <wij-grid-column
      binding="country" width="100" group="true">
    </wij-grid-column>
    <wij-grid-column
      binding="product" width="140" >
    </wij-grid-column>
    <wij-grid-column
      binding="amount" width="100" format="c2" aggregate="sum" >
    </wij-grid-column>
</wij-grid>

"wij-grid" 指令定制表格的属性,"wij-grid-column" 指令定制特性表格列的属性。以上标记定义了一个拥有三列的可编辑表格,分别为:“country”, "product" 和 "amount"。并且,以country列分组并且计算每个分组的合计。

这个指令中最特别的一点是 “wij-grid”和“wij-grid-column”的连接。为了使这个连接起作用,父指令中定义了如下controller:

app.directive("wijGrid", [ "$rootScope", "wijUtil", function ($rootScope, wijUtil) {
  return {
    restrict: "E",
    replace: true,
    transclude: true,
    template: "<table ng-transclude/>",
    scope: {
      data: "=",          // List of items to bind to.
      allowEditing: "@",  // Whether user can edit the grid.
      afterCellEdit: "&", // Event that fires after cell edits.
      allowWrapping: "@", // Whether text should wrap within cells.
      frozenColumns: "@"  // Number of non-scrollable columns
    },
    controller: ["$scope", function ($scope) {
      $scope.columns = [];
      this.addColumn = function (column) {
        $scope.columns.push(column);
      }
    }],
    link: function (scope, element, attrs) {
      // omitted for brevity, see full source here: 
      // http://jsfiddle.net/Wijmo/jmp47/
    }
  }
}]);

关于controller 方法使用前文中提到的数组语法声明,在这个例子中,controller定义了addColumn 方法,它将会被"wij-grid-column" 指令调用。父指令会通过特定标记来访问列。

以下是"wij-grid-column" 指令的使用方法:

app.directive("wijGridColumn", function () {
  return {
    require: "^wijGrid",
    restrict: "E",
    replace: true,
    template: "<div></div>",
    scope: {
      binding: "@",     // Property shown in this column.
      header: "@",      // Column header content.
      format: "@",      // Format used to display numeric values in this column.
      width: "@",       // Column width in pixels.
      aggregate: "@",   // Aggregate to display in group header rows.
      group: "@",       // Whether items should be grouped by the values in this column.
      groupHeader: "@"  // Text to display in the group header rows.
    },
    link: function (scope, element, attrs, wijGrid) {
      wijGrid.addColumn(scope);
    }
  }
});


require 成员用于指定"wij-grid-column" 指令的父级指令"wij-grid"。link 方法接收父指令的引用 (controller) ,同时通过addColumn 方法传递自身的scope 给父指令。scope 包含了表格用于创建列的所有信息。

更多指令

链接为一些AngularJS 指令的在线实例: http://wijmo.gcpowertools.com.cn/demo/AngularExplorer/ ,你可以在例子的基础上进行练习。例子都是严格的安照本文中的描述制作的,所以你可以无障碍学习他们。

&#160;

资源推荐:

1. AngularJS by Google AngularJS主页。

2. AngularJS Directives documentation AngularJS 指令官方帮助文档。

3. AngularJS directives and the computer science of JavaScript 比较实用的AngularJS指令说明文章。

4. Video Tutorial: AngularJS Fundamentals in 60-ish Minutes AngularJS 介绍视频。

5. About those directives AngularJS 研发人员发布的视频教程。

6. Egghead.io AngularJS 使用系列视频教程。

7. Wijmo AngularJS Samples AngularJS 在线实例。

本文出自 “葡萄城控件博客” 博客,请务必保留此出处http://powertoolsteam.blog.51cto.com/2369428/1414342

© 著作权归作者所有

葡萄城技术团队

葡萄城技术团队

粉丝 404
博文 583
码字总数 900180
作品 20
西安
高级程序员
私信 提问
《AngularJS学习整理》系列分享专栏

《AngularJS学习整理》系列分享专栏 《AngularJS学习整理》已整理成PDF文档,点击可直接下载至本地查阅 https://www.webfalse.com/read/201748.html 文章 教你用AngularJS框架一行JS代码实现...

开元中国2015
2018/11/09
189
0
OSChina 技术专题之 AngularJS 更新版(201412)

Angular JS (Angular.JS) 是一组用来开发Web页面的框架、模板以及数据绑定和丰富UI组件。它支持整个开发进程,提供web应用的架构,无需进行手工DOM操作。 AngularJS很小,只有60K,兼容主流浏...

OSC编辑部
2014/10/17
11.2K
26
【前端】—聊聊我认识的Angular

前言 最近接触的项目前端用到了Angular框架,之前略有耳闻,从vue换到Angular,感觉东西差不多,还是要系统学习的,先来了解下。 正文 1、Angular 的发展 AngularJS 是一款来自Google的前端J...

zt15732625878
2018/05/19
0
0
ngular2 VS Angular4 深度对比:特性、性能

在Web应用开发领域,Angular被认为是最好的开源JavaScript框架之一。 Google的Angular团队已于3月23日发布了Angular4,而期待已久的Angular2版本则是之前版本的完全重构。 对于成熟的开发人员...

机器的心脏
2018/06/02
0
0
AngularJS 自定义控件

[AngularJS Custom Directives][1] 好讨厌不带日期的博客,而且说得好啰嗦 自定义指令介绍 AngularJS 指令作用是在 AngulaJS 应用中操作 Html 渲染。比如说,内插指令 ( ), 指令以及 指令。 ...

开源中国匿名会员
2014/07/11
13K
3

没有更多内容

加载失败,请刷新页面

加载更多

Android面试常客之Handler全解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/fnhfire_7030/article/details/79518819 前言:又到了一年...

shzwork
5分钟前
0
0
position sticky 定位

本文转载于:专业的前端网站➫position sticky 定位 1、兼容性 https://caniuse.com/#search=sticky chrome、ios和firefox兼容性良好。 2、使用场景 sticky:粘性。粘性布局。 在屏幕范围内时...

前端老手
12分钟前
1
0
CentOS 7 yum 安装 PHP7.3 教程

参考:https://www.mf8.biz/centos-rhel-install-php7-3/ 1、首先安装 EPEL 源: yum install epel-release 安装 REMI 源: yum install http://rpms.remirepo.net/enterprise/remi-release......

dragon_tech
26分钟前
1
0
Linux物理网卡聚合及桥接

Linux内部实现的bridge可以把一台机器上的多张网卡桥接起来,从而把自己作为一台交换机。同时,LInux bridge还支持虚拟端口,即桥接的不一定都是物理网卡接口,还可以是虚拟接口。目前主要表...

xiangyunyan
27分钟前
1
0
一起来学Java8(一)——函数式编程

在这篇文章中,我们将了解到在Java8下如何进行函数式编程。 函数式编程 所谓的函数式编程就是把函数名字当做值进行传递,然后接收方拿到这个函数名进行调用。 首先来看下JavaScript如何进行函...

猿敲月下码
58分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部