转载请写明来源地址:http://www.jb51.cc/article/p-hqmpiisq-bph.html

理解Components

在angularjs中,Component是一种特殊的directive,它的配置更简单一些,非常适合组件化的app架构。使用web组件和使用Angular风格的app架构使得编写app更为简便。

Component的优点:

  • 比普通directive要简单很多
  • 更加严谨,更加规范化
  • 更加适合组件化架构
  • component更容易升级到angular2

不使用Component的情况

  • 需要在编译阶段和预链接阶段执行的directive,因为Component这时还不可用
  • 当你需要directive才有定义的选项时,如priority,terminal,multi-element
  • 当你需要directive由属性,css的class而不是元素触发时

Components的创建和配置

Components由angularjs的module使用.component()方法注册。这个方法接受2个参数:

  • Component的名称(字符串类型)
  • Component的配置对象(注意,和.directive()不一样,不是一个工厂方法而仅仅是个配置对象)

heroApp.html:

<!DOCTYPE html>
<html>
<head>
    <Meta charset="uft-8"/>
    <title></title>
</head>
<script src="script/angular.min.js"></script>
<body ng-app="heroApp">
<div ng-controller="MainCtrl as ctrl">
    <b>Hero</b><br>
    <hero-detail hero="ctrl.hero"></hero-detail>
</div>
</body>
<script> angular.module('heroApp',[]).controller('MainCtrl',function MainCtrl() { this.hero = { name: 'Spawn' }; }); function HeroDetailController() { } angular.module('heroApp').component('heroDetail',{ templateUrl: 'heroDetail.html',controller: HeroDetailController,bindings: { hero: '=' } }); </script>
</html>

heroDetail.html:

<span>Name: {{$ctrl.hero.name}}</span>

Directive和Component之间的定义比较

属性 Directive Component
bindings No Yes (binds to controller)
bindToController Yes (default: false) No (use bindings instead)
compile function Yes NO
controller Yes Yes (default function() {})
controllerAs Yes (default: false) Yes (default: $ctrl)
link functions Yes No
multiElement Yes No
priority Yes No
replace Yes (deprecated) No
require Yes Yes
restrict Yes No (restricted to elements only)
scope Yes (default: false) No (scope is always isolate)
template Yes Yes,injectable
templateNamespace Yes No
templateUrl Yes Yes,injectable
terminal Yes No
transclude Yes (default: false) Yes (default: false)

组件化app架构

如前所述,component使得使用组件化架构构建app更为容易,除此之外component还具备的能力具体如下:

  • Component只能控制它自己的视图和数据:Component不会修改它自身scope之外的任何数据或DOM。通常情况下,AngularJS中可以通过scope继承和watch可以修改任何地方的数据,这确实很实用,但是也会导致很难明确哪个部分对修改数据负责。这就是为什么Component使用隔离范围,也因此无法进行所有scope的操作。
  • Component有明确定义的公共api-输入输出:隔离范围并不是全部,因为AngularJS是双向绑定的。如果你传一个对象到组件中,类似bindings: {item: '='},然后修改对象的属性,修改会反映到它的父组件中。但是对于component来说,component确实只是修改了它自己的scope内的数据。这样就很清晰的得知什么数据什么时候被修改。就此,component遵循一些简单的约定:

    • 输入需要使用<@绑定。<符号在1.5之后表示单向绑定,和=不同的是,它绑定的属性在component的scope哪不会被watch,这意味着你可以在component的scope内给属性设置一个新的值,它并不会更新父scope里的值。但是如果父scope和component的scope引用的是同一个对象,比如你在component修改对象的属性或者数组中的元素,这种改变仍然会反映到父scope。因此在<绑定后不要在component内修改对象的属性和数组的元素。当输入的值是字符串时可以使用@绑定,特别是在绑定的值不会更改的时候。

      bindings: { hero: '<',comment: '@' }
    • 输出使用&进行绑定,绑定的函数将作为component事件的回调函数

      bindings: { onDelete: '&',onUpdate: '&' }
    • 在不操作输入数据的情况下,component可以调用相应的输出事件改变输出数据。比如删除,并不是hero自己删除自己,而是通过相应的事件把自己传给父component

      <!-- note that we use kebab-case for bindings in the template as usual -->
      <editable-field on-update="$ctrl.update('location',value)"></editable-field><br>
      <button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
    • 这样,父component来决定事件执行什么(删除item还是更新属性)

      ctrl.deleteHero(hero) {
          $http.delete(...).then(function() {
              var idx = ctrl.list.indexOf(hero);
              if (idx >= 0) {
                  ctrl.list.splice(idx,1);
              }
          }); 
      }
  • Component有明确定义的生命周期:每个组件都可以实现lifecycle hooks,这些方法会在component生命周期的相应的点被调用,可以实现的hook方法如下:

    • $onInit() - 在element上的所有controller构造和所有的绑定初始化之后,在element之上前缀&的函数链接之前,每一个controller调用这个钩子方法。这是个给controller写一些初始化方法的好地方
    • $onChanges(changesObj) - 当单向绑定更新时调用,changesObj是一个hash键值对,key是已被修改的绑定属性的name,value是一个对象,格式是{ currentValue,prevIoUsValue,isFirstChange() },使用这个hook可以只触发组件内的更新,比如克隆绑定属性的对象以防止它被外部意外更新
    • $doCheck() - 在每一个digest循环被调用,提供了一个机会可以在数据更改时验证或者做一些操作。任何你希望进行的操作(比如对更改的响应)都必须通过这个hook调用。当$onChanges被调用时,实现这个hook没什么作用。比如,比如你想实现一个深度的equal函数检查,或者检查一个Date对象时,他将会非常有用,因为这是AngularJS的变化检测器检测不到这个变化,自然$onChanges也不会被调用。这个hook不包含任何参数,因此,如果想要检测变化,你需要存储之前的值,然后和现在的值进行比较。
    • $onDestroy() - 当controller包含的scope销毁时,controller会调用这个hook。使用这个hook可以释放一些外部资源,watch和事件处理程序
    • $postLink() - 在controller的element和子元素被链接后调用。和post-link函数类似,这个hook可以设置DOM事件的处理程序或者直接进行DOM操作。包含templateUrl指令的element不会被编译和链接,因为它们要等template异步加载。它们的编译和链接要等template完成之后才会执行。这个hook非常和angular2中的ngAfterViewInit和ngAfterContentinit两个hook类似。因为angular1和angular2编译过程的不同,所以在anguar1升级到angular2时一定要小心。

    通过实现这些方法,你的component可以在生命周期中hook

  • app就是一棵component树:理想的情况下,整个app是一棵component构成的树,清晰的定义了输入输出,并尽量不使用双向绑定。这样更容易预测数据的变化和组件状态。

Component树示例

下面这个示例对上面对例子进行了扩展,并和我们刚讲过到概念结合起来:

不再使用ngController,现在使用拥有多个hero的heroList组件象,为每个hero创建一个heroDetail组件。

heroDetail组件包含的新功能:

  • 一个delete按钮,可以调用绑定到heroList组件的onDelete函数
  • 一个可以改变hero的location的输入控件,该控件是可重用的editableField组件。不是editableField组件自己操作hero对象,它将改变传给heroDetail组件,heroDetail组件再传给heroList组件,由heroList组件更新原始数据。

heroApp.html

<!DOCTYPE html>
<html>
<head>
    <Meta charset="uft-8"/>
    <title></title>
</head>
<script src="script/angular.min.js"></script>
<body ng-app="heroApp">
<hero-list></hero-list>
</body>
<script> /** * index.js */ angular.module('heroApp',[]); /** * heroList.js */ function HeroListController($scope,$element,$attrs) { var ctrl = this; // This would be loaded by $http etc. ctrl.list = [ { name: 'Superman',location: '' },{ name: 'Batman',location: 'Wayne Manor' } ]; ctrl.updateHero = function(hero,prop,value) { hero[prop] = value; }; ctrl.deleteHero = function(hero) { var idx = ctrl.list.indexOf(hero); if (idx >= 0) { ctrl.list.splice(idx,1); } }; } angular.module('heroApp').component('heroList',{ templateUrl: 'heroList.html',controller: HeroListController }); /** * heroDetail.js */ function HeroDetailController() { var ctrl = this; ctrl.delete = function() { ctrl.onDelete({hero: ctrl.hero}); }; ctrl.update = function(prop,value) { ctrl.onUpdate({hero: ctrl.hero,prop: prop,value: value}); }; } angular.module('heroApp').component('heroDetail',bindings: { hero: '<',onDelete: '&',onUpdate: '&' } }); /** * editableField.js */ function EditableFieldController($scope,$attrs) { var ctrl = this; ctrl.editMode = false; ctrl.handleModeChange = function() { if (ctrl.editMode) { ctrl.onUpdate({value: ctrl.fieldValue}); ctrl.fieldValuecopy = ctrl.fieldValue; } ctrl.editMode = !ctrl.editMode; }; ctrl.reset = function() { ctrl.fieldValue = ctrl.fieldValuecopy; }; ctrl.$onInit = function() { // Make a copy of the initial value to be able to reset it later ctrl.fieldValuecopy = ctrl.fieldValue; // Set a default fieldType if (!ctrl.fieldType) { ctrl.fieldType = 'text'; } }; } angular.module('heroApp').component('editableField',{ templateUrl: 'editableField.html',controller: EditableFieldController,bindings: { fieldValue: '<',fieldType: '@?',onUpdate: '&' } }); </script>
</html>

heroList.html

<b>Heroes</b><br>
<hero-detail ng-repeat="hero in $ctrl.list" hero="hero" on-delete="$ctrl.deleteHero(hero)" on-update="$ctrl.updateHero(hero,value)"></hero-detail>

heroDetail.html

<hr> <div> Name: {{$ctrl.hero.name}}<br> Location: <editable-field field-value="$ctrl.hero.location" field-type="text" on-update="$ctrl.update('location',value)"></editable-field><br> <button ng-click="$ctrl.delete()">Delete</button> </div>

editableField.html

<span ng-switch="$ctrl.editMode"> <input ng-switch-when="true" type="{{$ctrl.fieldType}}" ng-model="$ctrl.fieldValue"> <span ng-switch-default>{{$ctrl.fieldValue}}</span> </span> <button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? 'Save' : 'Edit'}}</button> <button ng-if="$ctrl.editMode" ng-click="$ctrl.reset()">Reset</button>

Component作为route模版

当使用ngRoute时,Component作为route的模版也是非常有用的。一个组件化的app,每一个视图都是一个组件:

var myMod = angular.module('myMod',['ngRoute']);
myMod.component('home',{
  template: '<h1>Home</h1><p>Hello,{{ $ctrl.user.name }} !</p>',controller: function() {
    this.user = {name: 'world'};
  }
});
myMod.config(function($routeProvider) {
  $routeProvider.when('/',{
    template: '<home></home>'
  });
})

使用$routeProvider时,你常常可以避开一些样板将已解决达route依赖传到component中。1.5版本之后,ngRoute可以自动分配resolve到route的scope属性$resolve,你也可以通过resolveAs配置属性名。当用component时,你可以利用component的优势,直接把resolve传到你的component中,而不用创建额外的route控制器。

var myMod = angular.module('myMod',bindings: {
    user: '<'
  }
});
myMod.config(function($routeProvider) {
  $routeProvider.when('/',{
    template: '<home user="$resolve.user"></home>',resolve: {
      user: function($http) { return $http.get('...'); }
    }
  });
});

Component间的通信

Directive需要其他Directive的controller来相互通信。在component中可以为require属性提供一个对象map来实现,map的key是所需controller构成的属性,而map的值是所需组件的名称。

不过要注意的是,所需的controller在它初始化完成之前是不可用的,但是可以确定的是当前controller在$onInit执行之前所需的controller是可用的。

下面的例子是将上一篇文章由Directive方式改为了component方式:

docsTabsExample.html

<!DOCTYPE html> <html> <head> <Meta charset="uft-8"/> <title></title> </head> <script src="script/angular.min.js"></script> <body ng-app="docsTabsExample"> <my-tabs> <my-pane title="Hello"> <p>Lorem ipsum dolor sit amet</p> </my-pane> <my-pane title="World"> <em>Mauris elementum elementum enim at suscipit.</em> <p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p> </my-pane> </my-tabs> </body> <script> angular.module('docsTabsExample',[]) .component('myTabs',{ transclude: true,controller: ['$scope',function MyTabsController($scope) { var panes = this.panes = []; this.select = function (pane) { angular.forEach(panes,function (pane) { pane.selected = false; }); pane.selected = true; }; this.addPane = function (pane) { if (panes.length === 0) { this.select(pane); } panes.push(pane); }; }],templateUrl: 'my-tabs.html' }) .component('myPane',{ require: { tabsCtrl: '^myTabs' },transclude: true,bindings: { title: '@' },controller: function () { this.$onInit = function () { this.tabsCtrl.addPane(this); console.log(this); }; },templateUrl: 'my-pane.html' }); </script> </html>

my-tabs.html

<div class="tabbable"> <ul class="nav nav-tabs"> <li ng-repeat="pane in $ctrl.panes" ng-class="{active:pane.selected}"> <a href="" ng-click="$ctrl.select(pane)">{{pane.title}}</a> </li> </ul> <div class="tab-content" ng-transclude></div> </div>

my-pane.html

<div class="tab-pane" ng-show="$ctrl.selected"> <h4>{{title}}</h4> <div ng-transclude></div> </div>

angular学习十三——Component的更多相关文章

  1. HTML5 播放 RTSP 视频的实例代码

    目前大多数网络摄像头都是通过 RTSP 协议传输视频流的,但是 HTML 并不标准支持 RTSP 流。本文重点给大家介绍HTML5 播放 RTSP 视频的实例代码,需要的朋友参考下吧

  2. 利用Node实现HTML5离线存储的方法

    这篇文章主要介绍了利用Node实现HTML5离线存储的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. 详解如何通过H5(浏览器/WebView/其他)唤起本地app

    这篇文章主要介绍了详解如何通过H5(浏览器/WebView/其他)唤起本地app的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. H5混合开发app如何升级的方法

    本篇文章主要介绍了H5混合开发app如何升级的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. AmazeUI 折叠面板的实现代码

    这篇文章主要介绍了AmazeUI 折叠面板的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码

    这篇文章主要介绍了HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  7. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  8. html5唤起app的方法

    这篇文章主要介绍了html5唤起app的方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  9. HTML5拍照和摄像机功能实战详解

    这篇文章主要介绍了HTML5拍照和摄像机功能实战详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  10. ios – 在没有iPhone6s或更新的情况下测试ARKit

    我在决定下载Xcode9之前.我想玩新的框架–ARKit.我知道要用ARKit运行app我需要一个带有A9芯片或更新版本的设备.不幸的是我有一个较旧的.我的问题是已经下载了新Xcode的人.在我的情况下有可能运行ARKit应用程序吗?那个或其他任何模拟器?任何想法或我将不得不购买新设备?解决方法任何iOS11设备都可以使用ARKit,但是具有高质量AR体验的全球跟踪功能需要使用A9或更高版本处理器的设备.使用iOS11测试版更新您的设备是必要的.

随机推荐

  1. Angular2 innerHtml删除样式

    我正在使用innerHtml并在我的cms中设置html,响应似乎没问题,如果我这样打印:{{poi.content}}它给了我正确的内容:``但是当我使用[innerHtml]=“poi.content”时,它会给我这个html:当我使用[innerHtml]时,有谁知道为什么它会剥离我的样式Angular2清理动态添加的HTML,样式,……

  2. 为Angular根组件/模块指定@Input()参数

    我有3个根组件,由根AppModule引导.你如何为其中一个组件指定@input()参数?也不由AppModalComponent获取:它是未定义的.据我所知,你不能将@input()传递给bootstraped组件.但您可以使用其他方法来做到这一点–将值作为属性传递.index.html:app.component.ts:

  3. angular-ui-bootstrap – 如何为angular ui-bootstrap tabs指令指定href参数

    我正在使用角度ui-bootstrap库,但我不知道如何为每个选项卡指定自定义href.在角度ui-bootstrap文档中,指定了一个可选参数select(),但我不知道如何使用它来自定义每个选项卡的链接另一种重新定义问题的方法是如何使用带有角度ui-bootstrap选项卡的路由我希望现在还不算太晚,但我今天遇到了同样的问题.你可以通过以下方式实现:1)在控制器中定义选项卡href:2)声明一个函数来改变控制器中的散列:3)使用以下标记:我不确定这是否是最好的方法,我很乐意听取别人的意见.

  4. 离子框架 – 标签内部的ng-click不起作用

    >为什么标签标签内的按钮不起作用?>但是标签外的按钮(登陆)工作正常,为什么?>请帮我解决这个问题.我需要在点击时做出回复按钮workingdemo解决方案就是不要为物品使用标签.而只是使用divHTML

  5. Angular 2:将值传递给路由数据解析

    我正在尝试编写一个DataResolver服务,允许Angular2路由器在初始化组件之前预加载数据.解析器需要调用不同的API端点来获取适合于正在加载的路由的数据.我正在构建一个通用解析器,而不是为我的许多组件中的每个组件设置一个解析器.因此,我想在路由定义中传递指向正确端点的自定义输入.例如,考虑以下路线:app.routes.ts在第一个实例中,解析器需要调用/path/to/resourc

  6. angularjs – 解释ngModel管道,解析器,格式化程序,viewChangeListeners和$watchers的顺序

    换句话说:如果在模型更新之前触发了“ng-change”,我可以理解,但是我很难理解在更新模型之后以及在完成填充更改之前触发函数绑定属性.如果您读到这里:祝贺并感谢您的耐心等待!

  7. 角度5模板形式检测形式有效性状态的变化

    为了拥有一个可以监听其包含的表单的有效性状态的变化的组件并执行某些组件的方法,是reactiveforms的方法吗?

  8. Angular 2 CSV文件下载

    我在springboot应用程序中有我的后端,从那里我返回一个.csv文件WheniamhittingtheURLinbrowsercsvfileisgettingdownloaded.现在我试图从我的角度2应用程序中点击此URL,代码是这样的:零件:服务:我正在下载文件,但它像ActuallyitshouldbeBook.csv请指导我缺少的东西.有一种解决方法,但您需要创建一个页面上的元

  9. angularjs – Angular UI-Grid:过滤后如何获取总项数

    提前致谢:)你应该避免使用jQuery并与API进行交互.首先需要在网格创建事件中保存对API的引用.您应该已经知道总行数.您可以使用以下命令获取可见/已过滤行数:要么您可以使用以下命令获取所选行的数量:

  10. angularjs – 迁移gulp进程以包含typescript

    或者我应该使用tsc作为我的主要构建工具,让它解决依赖关系,创建映射文件并制作捆绑包?

返回
顶部