npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ng-cycler

v0.1.11

Published

A tiny Angular lifecycle helper library

Downloads

87

Readme

ng-cycler

ci build

Ng-Cycler is a tiny helper for releasing resources that are bound to Angular component's lifetime (a period between ngOnInit and ngOnDestroy).

gantt
    title An Angular Component
    axisFormat %d
    section Angular Runtime
    ngOnInit     :done, ngOnInit, 2023-01-01, 1d
    ngOnDestroy  :done, ngOnDestroy, 2023-01-09, 1d
    section Component
    A lifetime   :a1, 2023-01-02, 7d
    

Why?

It's important for Angular Components developer to make sure every resource (open connection, event bus subscription, http request) is finalized during the Component destroy phase.

Warning In other words: whenever you subscribe to anything you need to unsubscribe in the end, do not rely on Angular. Even HTTP-Client cold observables should be unsubscribed.

Because of that you can often find yourself doing one thing over and over again — manually connecting dependencies to component's lifetime. Usually it looks like this:

export class MyComponent implements OnInit, OnDestroy {
    // <3>
    private _mySubscription: Subscription = Subscription.EMPTY;

    ngOnInit() {
        // <1>
        this._mySubscription = this._myEventBus.subscribe(/* ... */);
    }

    ngOnDestroy() {
        // <2>
        this._mySubscription.unsubscribe();
    }
}
  1. you create something in ngOnInit;
  2. you dispose it in ngOnDestroy;
  3. you need a field to store some ref in the class;

This code might look cumbersome once you copy/paste it many times for various dependencies.

The objective of ng-cycler is to manage the dependency lifetime in just one place by a single line of code:

export class MyComponent implements OnInit, OnDestroy {
    ngOnInit() {
        this._cycler.manage(
            // this subscription will be unsubscribed
            // automatically on onNgDestroy
            this._myEventBus.subscribe(/* ... */)
            
            // Actually the `manage` method can accept a subscription,
            // a callback or a `Disposable` object. Depending on the
            // thing you pass the following will happen on `onNgDestroy`:
            //   - subscription will be unsubscribed
            //   - callback will be just called
            //   - `dispose` method will be called for Disposable instance
        );
    }
}

How?

Getting Started:

  1. Install the package to your Angular project
npm install ng-cycler
  1. Get you Component aware of the Cycler, there are two options for that.

Option 1 (recommended for small projects, playgrounds etc.)

Make your Component extend the Cycler class:

import { Cycler } from 'ng-cycler';


export class MyComponent extends Cycler implements OnInit {
    ngOnInit() {
        this.manage(
            // You can use `manage` and other
            // Cycler methods starting with "this."
            
            /* ... */
        );
    }
}

Option 2 (if you do not want to pollute your Component hierarchy)

Provide a Cycler instance by Angular DI:

import { Cycler } from 'ng-cycler';

@Component({
    //...
    providers: [Cycler]
    //...
})
export class MyComponent implements OnInit {
    constructor(
        private readonly _cycler: Cycler) {
    }
    
    ngOnInit() {
        this._cycler.manage(
            // You can use `manage` and other
            // Cycler methods starting with "this._cycler"
            
            /* ... */
        );
    }
}
  1. Start using Cycler by calling this.manage or this._cycler.manager depending on the option you chose in the previous step.

API and Use-Cases

1. manage method

Use manage method to bind a Dependency to the entire component's lifetime, starting from ngOnInit up to ngOnDestroy.

gantt
    title An Angular Component
    axisFormat %d
    
    section Angular Runtime
    ngOnInit     :done, ngOnInit, 2023-01-01, 1d
    ngOnDestroy  :done, ngOnDestroy, 2023-01-09, 1d

    section Component
    A lifetime   :a1, 2023-01-02, 7d
    
    section Dependencies
    Dep1   :active, d1, 2023-01-02, 7d
    Dep2   :active, d1, 2023-01-02, 7d
    Dep3   :active, d1, 2023-01-02, 7d

The Dependency could be a Subscription, a callback or a Disposable object:

import { Subscription } from 'rxjs';
import { Cycler } from 'ng-cycler';

export class MyComponent implements OnInit {
    ngOnInit() {
        const sub: Subscription = this._myEventBus.subscribe(/* ... */);
        
        // The `sub` Subscription will be unsubscribed on destroy.
        this._cycler.manage(sub);

        this._cycler.manage(() => {
            // This is a Callback, it will be
            // invoked on Component destroy.
        });
        
        this._cycler.manage({
            // This is a Disposable object
            dispose() {
                // `dispose` method will be called on destroy.
            }
        });
    }
}

manage method returns a Disposable object that can be used to force destruction manually if it's needed for whatever reason.

import { Subscription } from 'rxjs';
import { Cycler } from 'ng-cycler';

export class MyComponent implements OnInit {
    ngOnInit() {
        const sub: Subscription = this._myEventBus.subscribe(/* ... */);
        
        // The `sub` Subscription will be unsubscribed on destroy.
        const dependency = this._cycler.manage(sub);
        
        // Calling `dispose` causes dependency destroy right away.
        // In case of subscription it forces `unsubscribe`.
        dependency.dispose();
    }
}

2. manageAll method

manageAll is a shortcut to call manage for multiple dependencies:

import { Subscription } from 'rxjs';
import { Cycler } from 'ng-cycler';

export class MyComponent implements OnInit {
    ngOnInit() {
        // We pass multiple Subscriptions to manageAll
        // all of them will be unsubcsribed on Component destroy
        const dependency = this._cycler.manageAll(
            // Subscription 1
            this._myEventBus.subscribe(/* ... */),
            
            // Subscription 2
            this._myService1.subscribe(/* ... */),
            
            // Subscription 3
            this._myService2.subscribe(/* ... */));
    }
}

3. manageTransient method

Use manageTransient if you have a repeated action producing Dependencies and you need to make sure the previous Dependency has been finalized before the next one starts. In other words you want to prevent Dependencies lifetime overlapping like this:

gantt
    title An Angular Component
    axisFormat %d
    
    section Angular Runtime
    ngOnInit     :done, ngOnInit, 2023-01-01, 1d
    ngOnDestroy  :done, ngOnDestroy, 2023-01-09, 1d

    section Component
    A lifetime   :a1, 2023-01-02, 7d
    
    section Dependencies
    Dep1   :crit, d1, 2023-01-02, 4d
    Dep2   :crit, d1, 2023-01-04, 3d
    Dep3   :crit, d1, 2023-01-06, 3d

and turn it into this:

gantt
    title An Angular Component
    axisFormat %d
    
    section Angular Runtime
    ngOnInit     :done, ngOnInit, 2023-01-01, 1d
    ngOnDestroy  :done, ngOnDestroy, 2023-01-09, 1d

    section Component
    A lifetime   :a1, 2023-01-02, 7d
    
    section Dependencies
    Dep1   :active, d1, 2023-01-02, 2d
    Dep2   :active, d1, 2023-01-04, 2d
    Dep3   :active, d1, 2023-01-06, 3d

In rxjs-world you would typically overcome this using methods like switchMap.

First argument of manageTransient method is the unique string code to be used as Dependency type descriptor. Once manageTransient is called with the same code the previous Dependency will be finalized. In case of no additional calls the Dependency will be finalized OnDestroy just like for manage method.

import { Subscription } from 'rxjs';
import { Cycler } from 'ng-cycler';

export class MyComponent implements OnInit {
    search(query: string) {
        this._cycler.manageTransient(
            // Another transient dependency (if there is any) with
            // code 'search' will be finalized right away.
            'search',
            
            this._myService.search(query).subscribe(/* ... */));
    }
}