angular-feature-toggle
Version:
feature toggling capabilities for angular
146 lines (132 loc) • 5.93 kB
Markdown
[](http://badge.fury.io/js/angular-feature-toggle)
# angular-feature-toggle
## Synopsis
This library lets you manage features in the frontend using a semver notation.
Inspired by [angular-feature-flags](https://github.com/mjt01/angular-feature-flags).
## Examples and Demo
Check out the [example](https://github.com/yairhaimo/angular-feature-toggle/tree/master/example) directory for a simple usage of the library or this [plunker](http://plnkr.co/edit/j49u6oqQ6ulppqUphhMq) for the same demo.
## Installation
```sh
npm install angular-feature-toggle --save
```
```js
angular.module('main.app', ['yh.featureToggle'])
```
## Configuration
angular-feature-toggle uses a semver notation per feature and expects a configuration of this nature:
```js
{
"feature1": "1.5.1",
"feature2": "0.5.6"
}
```
This configuration toggles features inside the code according to their version conditions.
```
//Example for "feature1" : "1.5.1"
//^1.0.0 - true
//~1.5.0 - true
//~1.6.0 - false
//^2 - false
//* - true
```
For more information regarding the semver notation head over to the [semver](http://semver.org/) and the [node-semver](https://github.com/npm/node-semver) sites.
**NOTE**: In order to configure itself at angular's config phase angular-feature-toggle is, at the moment, dependant on a property named 'angularFeaturesConf' on the global window object.
##### Manual setting
For hardcoded values you can set the property manually:
```js
window.angularFeaturesConf = {dashboard: '1.0.0', admin: '0.5.1'};
```
For a dynamic feature loading method take a look at the [serverside feature loading example](#serversideLoading).
## ui.router
angular-feature-toggle detects if ui.router is in use and wraps it with a feature-toggle helper function.
You can now define your states this way:
```js
$stateProvider
.state('master.dashboard',
{
url: '/dashboard',
templateUrl: 'dashboard/dashboard.html',
controller: 'DashboardController',
controllerAs: 'dashboard',
feature: 'dash', //optional
version: '^0.5.1'
}
)
.state('master.dashboard',
{
url: '/dashboard',
templateUrl: 'NEWdashboard/newDashboard.html',
controller: 'NewDashboardController',
controllerAs: 'dashboard',
feature: 'dash', //optional
version: '^1'
}
);
```
Note that both states have the same name but different version conditions (***^0.5.1*** vs ***^1***).
If the version is not satisfied for a specific state definition then that definition will be **ignored**.
If more than one version of the same state is satisfied the first one will be defined and a warning message will be logged for the following ones.
The 'feature' property is optional, if omitted the state name is considered to be the feature name.
**NOTE**: if you omit the version parameter in the state definition then a regular state will be defined.
## Toggle features using a directive
There are two directives you can use in order to toggle features: **show-if-feature** and **hide-if-feature**.
Basically they act as ng-if and the opposite of ng-if. They add/remove elements from the DOM if a feature is enabled or satisfies a certain version.
```html
<div show-if-feature="admin">
This is the admin panel
</div>
```
With a specific version:
```html
<!-- will be shown if the admin feature exists and satisfies the version ^1 -->
<div show-if-feature="admin ^1">
This is the admin panel
</div>
<!-- will be shown if the admin feature exists and satisfies the version ~2.0.1 -->
<div show-if-feature="admin ~2.0.1">
This is the NEW and improved admin panel
</div>
<!-- will not be shown if the widget feature is enabled -->
<div hide-if-feature="widgets">
Widgets coming soon...
</div>
```
## Toggle features programmatically
You can use the ***featureToggle*** factory to check feature version:
```js
.controller('HomeController', function(featureToggle) {
if (featureToggle.isEnabled('admin')) {
this.message = 'welcome administrator!';
}
if (featureToggle.isVersion('admin', '^2')) {
this.items = [1,2,3];
}
});
```
The featureToggle is also a provider and can be used inside ***.config*** blocks:
```js
.config(function(featureToggleProvider) {
if (featureToggleProvider.isVersion('dashboard', '~3.5.0')) {
// do something
}
});
```
**NOTE**: since angular-feature-toggle configures itself in a ***.config*** block it must be defined as a module dependancy in order for its .config block to run prior to the one that is using it.
## Serverside feature configuration loading<a name="serversideLoading"></a>
Since we intialize the feature configuration in a ***.config*** block (in order to support state versioning) we cannot use the $http service to load the feature configuration from the server ($http is not injectable into a config block).
What we can do is first load the configuration with vanilla javascript and only once its loaded manually bootstrap the angular applcation:
```js
angular.element(document).ready(function() {
fetch('/example/features.json').then(function(response) {
response.json().then(function(features) {
window.angularFeaturesConf = features;
angular.bootstrap(document, ['app']);
})
});
});
```
This example uses the new [***fetch***](https://developers.google.com/web/updates/2015/03/introduction-to-fetch?hl=en) API for brevity but you can use any ajax function you wish.
## Limitations and TODOs
* Dependency on a global window property - angularFeaturesConf
* Cannot load feature configuration for a specific user since the feature initialization is done in the config phase
* Reduce library size (uses the entire node-semver library atm, need only a subset of that)