在最近的文章中,我使用 angular 2 中新的 " ng-repeat " 也就是 " NgFor " 结合通用组件创建(自己的)组件。在这篇文章里,我将继续展示,结合 Angular 2进一步开发的 Echoes Player 项目。这次为了更加动态化的展示我们将添加一个无限滚动指令。在Angular 2中无限滚动作为一个属性指令。
在当前的 Echoes Player 生产版本中,为了在滚动的同时添加更多的视频,我使用 " ng-infinite-scroll "。在创建无限滚动方面它是一个非常好的、极简的,指示性的api,并且它在Echoes Player中使用非常简单。如下所示:
<div class="view-content youtube-results youtube-videos" infinite-scroll="youtubeVideos.searchMore()" infinite-scroll-distance="2" > .... </div>
作为一个api它有很多属性来完成无限滚动指令,但是,这次我并不使用它。
在这次投递时,我没有找到任何 angular 2的无限滚动条指令/组件。因此我认为这是一个好机会将angular 1.x中的 " ng-infinite-scroll " 指令迁移到angular 2中,并在迁移的过程中学习如何创建一个无限滚动条指令。请注意 angular 1.x的无限滚动条的源码由" coffeescript "编写。但是生产商最终准备按照ES5编译代码。
将源代码转换回JavaScript (用这个 http://js2.coffee/ ), 我开始隔离代码理解它并能够迁移到类ES2015 。
最重要的逻辑被编写为一个angular 1.x控制器. 我想这段代码应该是类 ES2015 。实际上,我想要迁移的逻辑控制器到类ES2015上,这将无关任何框架或库, 能够在任何地方使用它 ——主要应用于相同的 ponyfoo's dragula 和其他极好的组件.
但首先,我必须了解angular 2 指令的概念。
在Angular 2中,除了控件我们还可以使用指令。Angular 2有许多内置的标签可以使用,例如:NgFor,NgIf,NgModel,NgClass。除此之外,还可以使用创建自定义指令的API。
本质上来说,一个指令就像是一个组件。
一共有3种类型的指令:
1. 组件 - 使用@Component()
2. 结构化指令 -使用@Directive() - 通常会改变一个元素的DOM - 例如NgIf
3. 属性指令 -使用@Directive() - 不会改变DOM,但是会添加额外的行为
无限滚动条非常适合使用 属性指令 - 它添加了一个滚动事件(行为)到一个元素并且在此元素上执行滚动。
接下来我们使用Angular 2来封装这个指令。
首先,导入相关的依赖
import { Directive, ElementRef, Input, Output, EventEmitter } from 'angular2/core'; import { Scroller } from './scroller';
逻辑和从Angular 1.x中移植的代码会从“scroller.ts”文件引入。
我们将会使用Angular 2的核心对象来定义相关的属性。
为了声明一个属性指令,类似组件,我们使用“ @Directive() ”装饰器,在此基础上指定一个属性类选择器(即CSS选择器),示例如下:
@Directive({ selector: '[infinite-scroll]' })
接下来,我们将为这个指令定义一个类作为控制器,我们希望从元素获取一个输入数据和绑定的事件,因此这个指令将按如下方式对外开放。
export class InfiniteScroll { @Input() set infiniteScrollDistance(distance: Number) { this._distance = distance; } @Output() scroll = new EventEmitter(); //... }
这个" infiniteScrollDistance " 属性被期望从指令的外部进行设置,它将作为这个指令的一个属性API。同样地“scroll”事件将触发一个从外部绑定的方法。综合上述设定,我们将以如下方式使用这个指令:
<div class="search-results" infinite-scroll [infiniteScrollDistance]="2" (scroll)="onScroll()"> </div>
注意上面“div”元素中每个属性是如何匹配指令中的响应声明的。
在angular 1.x的DI(依赖注入)系统中,允许我们使用 "$element" 来获取引用的DOM指令元素:
controller: function ($element) { $element.on('scroll', onScroll); }
在angular 2中,我们使用 " ElementRef " 类型定义。使用 Typescript的前提下,我们将能引用“this”上线文访问绑定的DOM指令元素的属性。
constructor(private element: ElementRef) { // now, we can reference to: this.element }
现在,我们将使用 angular 2的钩子 — " ngOnInit " — 当指令元素已经准备就绪,钩子将运行并且一次性的初始化一个滚动器.注意我在onScroll函数上绑定"this"元素,这能保证当前滚动事件激活事件被触发者的属性时,(本文是)滚动(属性),我们能引用当前DOM指令元素的上下文。
ngOnInit() { this.scroller = new Scroller(window, setInterval, this.element, this.onScroll.bind(this), this._distance, {}); } private scroller: Scroller; private _distance: Number; onScroll() { this.scroll.next({}); }
“scroller.ts”就是一个 ES2015的类实现 。它的大部分实现是从angular1.x的“ng-infinite-scroll”中复制过来的,它的语法符合ES2015语法规范,并尽力实现了相关更新。
尽管无限滚动和原angular1.x版本中的许多特性都没有直接实现,但是,angular2中也提供了一些代替的方法,如:angular2中获取实际DOM元素的特殊语法“ this.$elementRef.nativeElement ”。
ng2开发的Echoes Player是一个开源项目。同时,你也可以fork一份ES2015规范的angular1版本的Echoes,自己完成这个项目从ES5向ES2015的迁移。
echoes-ng2旨在以angular2的更多特性完成整个项目的代码迁移。