scroll

Angular 2: Attribute @Directive() & Creating An Infinite Scroll Directive

In the recent article, I used the new “ng-repeat” in angular 2, “NgFor, and created component that consumes other custom component. In this article, I continue to show further development for Echoes Player with angular 2, this time – making it more dynamic by adding infinite scroll directive as what is known in angular2 as an attribute directive.

Angular 1.x Infinite Scroll

In the current production of Echoes Player, in order to add more videos to the result while scrolling, I used “ng-infinite-scroll“. It has a nice minimal directive api for triggering an infinite scroll – and the usage for Echoes Player is quite simple:

There are more attributes as an api for this directive, however, in this case – I didn’t use it.

As of this time of writing this post, I didn’t found any angular 2 infinite scroll directive / component, so, I figured it is a great opportunity to migrate “ng-infinite-scroll” directive from angular 1.x to angular 2 while learning how to create one.

Please note that the source code for this angular 1.x infinite scroll directive is written with “coffeescript“. However, the production ready code is compiled eventually to ES5.

Migration Process to Angular 2 Infinite Scroll

After converting the source code back to javascript (using http://js2.coffee/), I started isolating the code to understand what it does and being able to migrate it to ES2015 class.

Most of the important logics is written as an angular 1.x controller. I figured this code should be an ES2015 class. Actually, I wanted the migrate the logics of this controller to an ES2015, So it will be agnostic to any framework/library, being able to use it anywhere – the same principal applied to ponyfoo’s dragula and his other awesome components.

But first, I had to understand angular 2 directive concepts.

Angular 2 Directive In a Nutshell

In Angular 2, aside from components, there are still directives. There are built-in directive to the framework, such as: NgFor, NgIf, NgModel, NgClass and there’s an api for creating custom directives.

Essentially, a directive is something like a component.

There are 3 kinds of directives:

  1. A Component – using @Component()
  2. A Structural Directive – using @Directive() – usually changes the DOM of an element – NgIf
  3. An Attribute Directive – using @Directive() – doesn’t change the DOM, but adding behaviour

Infinite Scroll fits well to an Attribute Directive (More on that on the official documentation) – It adds a scroll event (behaviour) to an element and acts upon it.

Lets create the angular 2 wrapping code for this directive.

First, lets import the relevant dependencies:

The logics and migrated code of angular 1.x is imported from the “scroller.ts” class file.

We’re going to use some of angular’s 2 core objects to define the relevant properties.

Directive Definition

To declare an attribute as a directive, similar to Component, we use the “@Directive()” decorator, while specifying an attribute class selector (css selector):

Directive Bindings & Events

Next, we’ll define the class which will used as a controller for this directive, an input data that we’ll expect to get from the element and an event that this directive will expose.

The “infiniteScrollDistance” property is expected to be set from outside the directive, as an attribute api. The same goes for the “scroll” event, which will trigger a function that is bind from outside. This means, that we’ll use this directive like so:

Notice how each attribute in the above “div” element is matching a different declaration in this directive code.

Referencing Directive’s Element in Angular 2

With angular 1.x, the DI system allowed us to require “$element” and expect to get a reference to the directive’s DOM element:

With angular 2, we use the “ElementRef” type definition. Also with the use of Typescript, we’ll attach its property reference to “this” directive context:

Hook the Scroll Event with ngOnInit to Directive’s Element

Now, we’ll us angular 2 hook – “ngOnInit” – which will run when the directive is ready and will instantiate a new scroller, only once. Notice that I bind “this” context to the onScroll function reference to keep the context of this directive when the scroll event will trigger the event emitter’s property, scroll:

Migration of Scroller Logic

The “scroller.ts” is an ES2015 class (full source code). Much of this code has been copied from the source implementation of “ng-infinite-scroll” of angular 1.x and has been adapted to follow ES2015 syntax and relevant updates to the code as much as possible.

Final Thoughts

Still, the infinite-scroll directive is not complete and there are more features to port from the original angular 1.x version. There are also some points in the code where it is points to angular 2 specific “this.$elementRef.nativeElement” in order to get the actual DOM element.

Echoes Player implementation with ng2 is open source. You can also fork Echoes ES2015 with angular 1.x version and follow the complete conversion from ES5 to ES2015 of this project.

The Goal of echoes-ng2 is migrating the whole application code to use angular2 various features.

  • Gerard Sans

    Well done! Looking forward to the series.

  • Cool article I like the code comparison of one versus two.

    Thanks!

  • Naveed Ahmed

    Hi, thank you for an excellent article, but this directive has an issue that when we move away from the component using this directive, the binding doesn’t destroy. And if you scroll window on next page, the scrolled function is still being called.

  • thanks Duncan!

  • Thanks @gerardsans:disqus
    working on it.

  • Thanks @disqus_vzlDR52CrS:disqus
    please feel free to open an issue in the github repo.