Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

ngx-feature-toggle

willmendesneto21.5kMIT12.0.0TypeScript support: included

Your module to handle with feature toggles in Angular applications easier.

angular, ngx, feature-toggle, feature-flag, feature, toggle, flag, ngx-feature-toggle

readme

NGX Feature Toggle

Dependency Status npm

NPM NPM

Build Status Coverage Status npm bundle size (minified + gzip) npm

Your module to handle with feature toggles in Angular applications easier.

Why Feature toggle?

This is a common concept, but why use this directive instead solve it via server-side rendering?

The idea of this directive is make this process transparent and easier. So the main point is integrate this directive with other tooling process, such as:

  • Server-side rendering;
  • Progressive rendering;
  • Any other that you like :)

You can integrate with WebSockets or handling this in a EventSourcing architecture. It's totally transparent for you and you can integrate easier in your application.

Demo

Try out the demos on Stackblitz:

Install

You can get it on NPM installing ngx-feature-toggle module as a project dependency.

npm install ngx-feature-toggle --save

Setup

You'll need to add FeatureToggleModule to your application module. So that, the featureToggle components will be accessible in your application.

...
import { FeatureToggleModule } from 'ngx-feature-toggle';
...
@NgModule({
  declarations: [
    YourAppComponent
  ],
  imports: [
    ...
    FeatureToggleModule,
    ...
  ],
  providers: [],
  bootstrap: [YourAppComponent]
})

export class YourAppComponent {}

Now you just need to add a configuration in your application root component. Your feature toggle configuration can be added using different approaches, such as:

  • RXJS subscribe information;
  • HTTP Request;
  • CQRS event data;
  • File information;
  • etc;

After that, you can use the featureToggle components and directives in your templates, passing the string based on the feature toggle configuration data.

Module

Components and Directives

  • feature-toggle-provider: Handle with feature toggle configuration in your application. It adds the default values of your enabled/disabled features;
  • *featureToggle: Directive that handles with feature toggle check. So that, the component will be rendered/removed based on the feature toggle configuration is enabled;
  • *featureToggleWhenDisabled: Directive that handles with feature toggle check. So that, the component will be rendered/removed when the feature toggle configuration is disabled;
import { Component } from '@angular/core';

@Component({
  selector: 'component-docs',
  template: `
    <feature-toggle-provider [features]="featureToggleData">
      <div *featureToggle="'enableSecondText'">
        <p>condition is true and "featureToggle" is enabled.</p>
      </div>
      <div *featureToggle="'enableFirstText'">
        <p>condition is false and "featureToggle" is disabled. In that case this content should not be rendered.</p>
      </div>
      <div *featureToggle="'!enableFirstText'">
        <p>
          condition is false and "featureToggle" is disabled
          <b>but it has "!" as a prefix of the feature toggle to be checked.</b>
          In that case this content should be rendered.
        </p>
      </div>
      <div
        class="combined-feature-toggles-with-truthly-option"
        *featureToggle="['!enableFirstText', 'enableSecondText']"
      >
        <p>
          This is a combined condition. It shows if <b>enableSecondText</b> is true and <b>enableFirstText</b> is falsy,
          but it has "!" as a prefix. If both cases are correct, then the "featureToggle" is enabled and rendering this
          component.
        </p>
      </div>
    </feature-toggle-provider>
  `,
})
export class ComponentDocsComponent {
  public featureToggleData: any = {
    enableFirstText: false,
    enableSecondText: true,
  };
}

Route Guards

In some scenarios when you need to prevent the route to be loaded, you can use NgxFeatureToggleCanLoadGuard, by passing the class and configuration of the feature toggle to be checked in your route data.

...
export const routes: Routes = [

  {
    path: 'home',
    component: HomeComponent,
    canActivate: [NgxFeatureToggleCanLoadGuard],
    data: {
      // Using array as configuration
      featureToggle: [
        // This configuration will check if feature toggle is enabled
        'enableSecondText',
        // This configuration will check if feature toggle is disabled
        // since it has `!` prefix in the configuration
        '!enableFirstText'
      ],
    },
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [NgxFeatureToggleCanLoadGuard],
    data: {
      // Using string as configuration
      featureToggle: 'enableSecondText',
    },
  },
];
...

Also, you can use NgxFeatureToggleRouteGuard to check if the route should be activated or not by passing the class and configuration of the feature toggle to be checked in your route data.

...
export const routes: Routes = [

  {
    path: 'home',
    component: HomeComponent,
    canActivate: [NgxFeatureToggleRouteGuard],
    data: {
      // Using array as configuration
      featureToggle: [
        // This configuration will check if feature toggle is enabled
        'enableSecondText',
        // This configuration will check if feature toggle is disabled
        // since it has `!` prefix in the configuration
        '!enableFirstText'
      ],
    },
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [NgxFeatureToggleRouteGuard],
    data: {
      // Using string as configuration
      featureToggle: 'enableSecondText',
    },
  },
];
...

In both route guards you can pass route data with feature toggle as an array. For scenarios when you need to check for feature toggles enabled and/or disabled you can easily configure it by passing ! if the application should check if the feature toggle is disabled

...
export const routes: Routes = [
  {
    path: 'home',
    component: HomeComponent,
    canActivate: [NgxFeatureToggleRouteGuard],
    data: {
      // Using array as configuration
      featureToggle: [
        // This configuration will check if feature toggle is enabled
        'enableSecondText',
        // This configuration will check if feature toggle is disabled
        // since it has `!` prefix in the configuration
        '!enableFirstText'
      ],
    },
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [NgxFeatureToggleRouteGuard],
    data: {
      // Using string as configuration
      featureToggle: 'enableSecondText',
    },
  },
];
...

In this case, we are combining the checks. So the component will be activated if enableSecondText is configured as true AND enableFirstText is configured as false. With that configuration you can have all the flexibility to cover different scenarios in your app.

Use NgxFeatureToggleRouteGuard to control when the child component of a specific component can be activate via routing. It can be passed as an array of items.

...
export const routes: Routes = [
  {
    path: 'customer',
    component: CustomerComponent,
    canActivateChild: [NgxFeatureToggleRouteGuard],
    children: [
      {
        path: ':id',
        component: CustomerDetailComponent,
        // This is the featureToggle configuration for
        // the child component. It can also use
        // a combination of feature toggles
        data: {
          featureToggle: [
            // This configuration will check if feature toggle is enabled
            'enableCustomerPage',
            // This configuration will check if feature toggle is disabled
            // since it has `!` prefix in the configuration
            '!enableChildrenNavigation'],
        },
      },
    ],
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivateChild: [NgxFeatureToggleRouteGuard],
    children: [
      {
        path: ':id',
        component: DashboardDetailsComponent,
        // This is the featureToggle configuration for
        // the child component. It can also use
        // a combination of feature toggles
        data: {
          // using string to configure
          featureToggle: 'enableDashboardDetailsPage',
        },
      },
    ],
  },
];
...

Redirects

You might have some specific requirements that you should redirect a user to a specific route in case of a feature flag is disabled. For that, you can use redirectTo as a mechanism to redirect a user in a specific route when it tries to access in a route with a CanActivate/CanActivateChild/CanLoad Feature Toggle Guard and the feature toggle is disabled.

For advanced scenarios you can use a combination of route guards AND redirects. E.G.

...
export const routes: Routes = [
  {
    path: 'customer',
    component: CustomerComponent,
    canLoad: [NgxFeatureToggleRouteGuard],
    canActivate: [NgxFeatureToggleRouteGuard],
    canActivateChild: [NgxFeatureToggleRouteGuard],
    // This is the featureToggle configuration for
    // the parent component
    data: {
      featureToggle: ['enableCustomerPage'],
      // If feature toggle is disabled, the user will be redirected to `/error` URL
      redirectTo: '/error'
    },
    children: [
      {
        path: ':id',
        component: CustomerDetailComponent,
        // This is the featureToggle configuration for
        // the child component. It can also use
        // a combination of feature toggles
        data: {
          featureToggle: ['enableCustomerPage', '!enableChildrenNavigation'],
          // If one (or all of them) of the feature toggle is disabled, the user will be redirected to `/customer-error` URL
          // Note that you can use redirects for the main url and their children
          redirectTo: '/customer-error'
        },
      },
    ],
  },
];
...

Development

Run demo locally

  1. This project uses Angular CLI as base. That means you just need to run npm start and access the link http://localhost:4200 in your browser

Run tests

  1. Run npm test for run tests. In case you want to test using watch, please use npm run tdd

Publish

this project is using np package to publish, which makes things straightforward. EX: np <patch|minor|major> --contents=dist/ngx-feature-toggle

For more details, please check np package on npmjs.com

Contribute

For any type of contribution, please follow the instructions in CONTRIBUTING.md and read CODE_OF_CONDUCT.md files.

Author

Wilson Mendes (willmendesneto)

changelog

Change Log

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog and this project adheres to Semantic Versioning.

Unreleased

12.0.0 - 2023-02-20

Updated

  • Upgrading library to Angular v15

7.0.0 - 2023-02-04

Added

  • Adding support for extending global theme added via NgxSkeletonLoaderModule.forRoot({ theme: /* ...list of CSS atributes */} })

By default when using NgxSkeletonLoaderModule.forRoot({ theme: /* ...list of CSS atributes */} }) the application is using this value as source of truth, overriding any local theming passed to <ngx-feature-toggle> component via [theme] input.

By using NgxSkeletonLoaderModule.forRoot({ theme: { extendsFromRoot: true, /* ...list of CSS atributes */} }) in your application, you should also be aware that:

  • By default, every <ngx-feature-toggle> component will use theme coming from NgxSkeletonLoaderModule.forRoot() as the source of truth
  • If there's any CSS attribute on the component locally which overrides the CSS spec, it combines both themes, but overriding global CSS attributes in favor of local ones.
<!-- 
  // ... E.G: App is using this configuration below

  NgxSkeletonLoaderModule.forRoot({
    theme: {
      // Enabliong theme combination
      extendsFromRoot: true,
      // ... list of CSS theme attributes
      height: '30px',
    },
  }),
-->

<div class="item">
  <ngx-feature-toggle></ngx-feature-toggle>
  <!-- above line will produce a skeleton component using `height: 30px;`" -->
  <ngx-feature-toggle [theme]="{background: 'blue'}"></ngx-feature-toggle>
  <!-- above line will produce a skeleton component using `height: 30px; background: blue;`" -->
  <ngx-feature-toggle [theme]="{height: '50px', background: 'red'}"></ngx-feature-toggle>
  <!-- above line will produce a skeleton component using `height: 50px; background: red;`" -->
</div>
  • Adding new custom-content appearance. From now on, consumers can now add their own content inside <ng-skeleton-loader></ng-skeleton-loader> component. So that, they can add some custom content, such as SVG, as an example
  • Adding examples for custom-content usage

Updated

  • Updagrading module to Angular v15

Fixed

  • Removing build warnings

6.0.0 - 2022-08-18

Updated

  • Adding Publishing setup using NPX
  • Replacing CSS class namespace from .loader to .skeleton-loader

Breaking Change

The CSS class used as namespace was changed. Previously, it was called .loader and now is .skeleton-loader. It might cause some issues in cases of :host DOM style scoping usage. For the sake of semantic versioning, please bear in mind this scenario in case of :host usage.

5.0.0 - 2022-02-08

Updated

Thanks @yharaskrik

Breaking Change

Bundle distribution are now esm2020, fesm2015 and fesm2020. UMD and CommonJS versions were support were removed from Angular CLI directly. So the next version for the package will be a major version to cover these changes accordingly.

  • Updating package bundle distribution
  • Updating @angular/cli to v13
  • Applying project changes to v13
  • Updating bundlesize check to point to fesm2020

4.0.0 - 2021-07-28

Fixed

Breaking Change

  • Rolling back "Adding mechanism to prevents calling forRoot() more than once if module is loaded asynchronously in a submodule.". Unfortunately, this was affecting consumers and it needed to be reverted to avoid friction in other applications.

If you need to have this feature in place, the suggestion is to create a specific module in your app and apply the changes on your application.

3.0.0 - 2021-07-23

Added

Breaking Change

  • Adding mechanism to prevents calling forRoot() more than once if module is loaded asynchronously in a submodule. This is required in order to avoid issues in consumers. To avoid that, consumers should load the module once on the main module instead - if loading submodules async.

2.10.1 - 2021-07-13

Fixed

  • Ensures every ARIA progressbar node has an accessible name. This is caused by missing aria-label on the <span> element.

Thanks @rkristelijn for raising the issue and the pull request!

2.10.0 - 2021-06-15

Added

  • Adding module configuration support via forRoot() method. Now you can add configure your module via forRoot(). You can now set the default of appearance, animation, theme, loadingText, count and/or items.E.G.

@NgModule({
  // ...
  imports: [NgxSkeletonLoaderModule.forRoot({ appearance: 'circle', count: 3 )],
  // ...
})

2.9.2 - 2021-04-11

Updated

  • Updating link in README.md

Fixed

  • Bumping dev dependencies to avoid security issues

2.9.1 - 2021-02-20

Fixed

  • Adding appearance attribute to be checked via ngOnChanges

Updated

  • Updating examples with new features

2.9.0 - 2021-02-19

Added

  • Adding validation for @Input attributes that needs internal manipulation. After these changes:
    • if count is not a numeric value, it will use the default value as 1
    • if animation is not a valid attribute, it will use the default value as progress
    • PS: The other values alredy have a fallback, so nothing to worry here
  • Adding error feedback for appearance attribute in case of wrong configuration. Now it will show a error message on the console in case of receiving a wrong value

Updated

  • Adding ngOnChange to validate count input in case of changes via binding
  • Updating README.md with information about appearance and theme usage.

2.8.0 - 2021-02-18

Fixed

Updated

2.7.0 - 2021-02-06

Added

  • Adding new loadingText attribute to be used as WAI-ARIA aria-valuetext. In this case, it will render the component using "Please wait ...". Otherwise, it defaults to "Loading..."
<!-- Passing loading text to be used as WAI-ARIA `aria-valuetext` -->
<!-- In this case, it will render the component using "Please wait ..." -->
<!-- Otherwise, it defaults to "Loading..." -->
<div class="skeleton-with-specific-loading-text">
  <ngx-feature-toggle loadingText="Please wait ..."></ngx-feature-toggle>
</div>

Updated

  • Using OnPush as changeDetection mechanism into ngx-feature-toggle component
  • Adding ability to pass false as string or boolean (coming from variable value via binding) on animation attribute in ngx-feature-toggle component configuration. animation will receive false as string when attribute field it's not using binding. Component now can receive false (boolean), "false" (string), or any other animation type via binding.
<div class="item">
  <!-- Disables the animation -->
  <ngx-feature-toggle animation="false"></ngx-feature-toggle>
  <!-- Disables the animation, but receiving boolean value from binding -->
  <!-- Via binding it can receive `false` (boolean), "false" (string), or any other animation type -->
  <ngx-feature-toggle [animation]="classAttributeWithBooleanFalseValue"></ngx-feature-toggle>
  <!-- Uses `progress` as animation -->
  <ngx-feature-toggle animation="progress"></ngx-feature-toggle>
  <ngx-feature-toggle></ngx-feature-toggle>
  <!-- Uses `pulse` as animation -->
  <ngx-feature-toggle animation="pulse"></ngx-feature-toggle>
</div>

2.6.2 - 2020-12-08

Fixed

  • Removing Lighthouse "Avoid non-composited animations" issue. Lighthouse shows warnings from ngx-feature-toggle.scss -file (progress).

  • "Avoid non-composited animations":

  • "Animations which are not composited can be janky and contribute to CLS"

To solve that, instead of using CSS background-position the module is now using CSS translate3d, which improves the animation by using GPU instead of CPU. Issue fixed and performance boost added 🎉

2.6.1 - 2020-11-30

Fixed

2.6.0 - 2020-11-15

Added

  • Adding NgxSkeletonLoaderModule.forRoot() method. Usage:
import { NgModule } from '@angular/core';
import { NgxSkeletonLoaderModule } from 'ngx-feature-toggle';
// ... list of other app dependencies

import { AppComponent } from './app.component';
// ... list of other app components/modules

@NgModule({
  declarations: [AppComponent],
  imports: [NgxSkeletonLoaderModule.forRoot()],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

2.5.0 - 2020-10-10

Fixed

  • Fixing bundle size command on CircleCI pipeline

Updated

  • Upgrading NodeJS to v14.11.0
  • Updating perf-marks package to v1.14.0
  • Improving skeleton animations fps by using cubic-bezier instead of ease-in-out

2.4.4 - 2020-08-21

Fixed

  • Remove check requirements if perf-marks is running in a browser or not in Angular apps 🔥

Added

  • Adding Angular Universal support for examples. Now we can run npm run dev:ssr and access http://localhost:4200/index.html and the page will run using angular universal 💪
  • Adding .prettierrc file with some of the code styling rules

2.4.3 - 2020-08-13

Fixed

  • Avoiding perf-marks call if running in Angular Universal applications

2.4.2 - 2020-08-01

Updated

  • Bumping perf-marks to latest version

2.4.1 - 2020-08-01

Updated

  • Bumping perf-marks to latest version

2.4.0 - 2020-08-01

Added

  • Adding User Timing API to track component render and content loader time

Updated

  • Updating examples with new skeleton simulation
  • Adding Stackblitz link for user card skeleton loading demo

2.3.0 - 2020-08-01

Added

  • Adding User Timing API to track component render and content loader time

Updated

  • Updating examples with new skeleton simulation
  • Adding Stackblitz link for user card skeleton loading demo

2.2.1 - 2020-06-30

Fixed

  • For compatibility with IE11 by using indexOf instead of includes

Updated

  • Updating npm run postinstall command to follow the new rules from update.angular.io website

2.2.0 - 2020-06-01

Added

2.1.0 - 2020-06-01

Updated

  • Upgrading @angular/cli to version 9
  • 🎉 Decreasing bundle size to 1.17KB 🎉

2.0.0 - 2020-05-15

Updated

  • Upgrading NodeJS to v12.16.2
  • Updating documentation with animation attribute

Added

  • Supporting for new animation progress-dark to enable users when using theme with darker color schema
  • Supporting for different animations 🎉

Now we can define the animation we want to use in <ngx-feature-toggle> component via animation input. It's a string that can defined the animation used during the loading, having as options:

  • false: it will disable the animation;
  • progress - default: it will use it progress as animation;
  • pulse: it will use pulse as animation;

progress is the default animation, used as the single one previously. If you don't pass the animation attribute, it defaults to progress.

<div class="item">
  <!-- Disables the animation -->
  <ngx-feature-toggle animation="false"></ngx-feature-toggle>
  <!-- Uses `progress` as animation -->
  <ngx-feature-toggle animation="progress"></ngx-feature-toggle>
  <ngx-feature-toggle></ngx-feature-toggle>
  <!-- Uses `pulse` as animation -->
  <ngx-feature-toggle animation="pulse"></ngx-feature-toggle>
</div>
  • Supporting enabling/disabling animations. Now the users will be able to enable/disable animations by using animation input. It's a string with false as value that the component receives to check if it should not load progress animation.

It works only to disable it. In case you want to keep enable it

<div class="item">
  <ngx-feature-toggle animation="false"></ngx-feature-toggle>
</div>

1.2.7 - 2020-04-13

Updated

  • Decreasing bundle size after disable Ivy in production build
  • Adding description, keywords and github information on package.json files

1.2.6 - 2020-02-26

Fixed

  • Changing angular library configuration to prod and forcing it at publish time

1.2.5 - 2020-02-25

Fixed

  • Changing angular library configuration to prod

1.2.4 - 2020-02-25

Updated

  • Updating Github templates
  • Updating Angular CLI to v9

1.2.3 - 2020-02-25

Fixed

  • Solving peerDependency warning when installing library in an Angular 9 project

1.2.2 - 2019-06-22

Fixed

  • Fixing component dimensions via theme

1.2.1 - 2019-06-08

Updated

  • Updating Angular CLI to v8

1.2.0 - 2019-04-19

Updated

  • Updating Angular CLI to 7.3.8

1.1.2 - 2019-01-07

Added

  • Adding badges for stackblitz, bundlephobia and license

Updated

  • Removing unnecessary CSS styles for skeleton

1.1.1 - 2018-12-17

Fixed

  • Fixing Stackblitz link demo link

1.1.0 - 2018-12-17

Added

  • Added GitHub urls into package.json
  • Added Circle CI integration
  • Added Coveralls integration
  • Added GitHub templates
  • Added CODE_OF_CONDUCT.md with the Code of conduct
  • Added unit tests for skeletons and demo components

Updated

  • Decreased bundle size
  • New gif showing ngx-feature-toggle in action

1.0.2 - 2018-12-16

Fixed

  • Added markdown files in dist folder in build time

1.0.1 - 2018-12-16

Fixed

  • Added markdown files in dist folder in build time

1.0.0 - 2018-12-16

Fixed

  • Fixed build script

0.0.1 - 2018-12-16

Added

  • Created ngx-feature-toggle
  • Created test automation for the module