{"id":27609,"date":"2018-11-12T00:06:04","date_gmt":"2018-11-12T07:06:04","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/premier-developer\/?p=27609"},"modified":"2019-02-14T20:17:44","modified_gmt":"2019-02-15T03:17:44","slug":"angular-how-to-implement-feature-flags","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/premier-developer\/angular-how-to-implement-feature-flags\/","title":{"rendered":"Angular How-to: Implement Feature Flags"},"content":{"rendered":"<p>Senior Dev Consultant <a href=\"https:\/\/www.linkedin.com\/in\/atkinsonlaurie\/\">Laurie Atkinson<\/a> demonstrates how to turn features on and off via configuration or from a database. This may include hiding and disabling UI elements, changing code flow, or preventing routing to components.<\/p>\n<hr \/>\n<p>In an agile development environment, there may be partially completed features or features with external dependencies that are not ready. Instead of relying on multiple code branches, instead you may opt for deploying code with a feature turned off. Later, that feature should be turned on via a configuration or database change. This post provides sample code that you can use to implement feature flags in your Angular app.<\/p>\n<h3>Option 1: Define features in a config file<\/h3>\n<p>One option for storing feature flags is in a JSON configuration file. Refer to <a href=\"https:\/\/na01.safelinks.protection.outlook.com\/?url=https%3A%2F%2Fblogs.msdn.microsoft.com%2Fpremier_developer%2F2018%2F03%2F01%2Fangular-how-to-editable-config-files%2F&amp;data=02%7C01%7Creedr%40microsoft.com%7C41ecb509c93544d9af3308d63ebdee58%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636765382806589673&amp;sdata=38j9wNInCE3fx%2FBJTXU5OuC1KqKEqb292kDVrBUI3Zk%3D&amp;reserved=0\">this post<\/a> for an explanation on consuming an editable configuration file.<\/p>\n<p><strong>config.dev.json<\/strong><\/p>\n<pre class=\"lang:default decode:true \">{\r\n  . . .\r\n  \"features\": {\r\n    \"feature1\": true,\r\n    \"feature2\": false\r\n  }\r\n}<\/pre>\n<p><strong>app-config.model.ts<\/strong><\/p>\n<pre class=\"lang:default decode:true\">export interface IAppConfig {\r\n  . . .\r\n  features: {\r\n    [name: string]: boolean;\r\n  };\r\n}<\/pre>\n<h3>Create a feature flag service<\/h3>\n<p>Centralize the management of determining whether a feature is turned on or off into an Angular service.<\/p>\n<p><strong>feature-flag.service.ts<\/strong><\/p>\n<pre class=\"lang:default decode:true \">import { Injectable } from '@angular\/core';\r\nimport { AppConfig } from '..\/..\/app.config';\r\n \r\n@Injectable()\r\nexport class FeatureFlagService {\r\n \r\n  featureOff(featureName: string) {\r\n    \/\/ Read the value from the config service\r\n    if (AppConfig.settings.features.hasOwnProperty(featureName)) {\r\n        return !AppConfig.settings.features[featureName];\r\n    }\r\n    return true; \/\/ if feature not found, default to turned off\r\n  }\r\n \r\n  featureOn(featureName: string) {\r\n    return !this.featureOff(featureName);\r\n  }\r\n}<\/pre>\n<h3>Option 2: Use an API instead of a config file to get feature flags<\/h3>\n<p>Instead of reading feature flags from a config file, another option is to retrieve the features from the server via an API that could read these values from a database. The feature-flag service could be adapted to fetch the feature flag values as soon as the app starts.<\/p>\n<p><strong>feature-flag.service.ts<\/strong><\/p>\n<pre class=\"lang:default decode:true \">export class FeatureFlagService {\r\n \r\n  private _featureFlags: Array&lt;string&gt; = []; \/\/ A list of all features turned ON\r\n  private _initialized = false;\r\n \r\n  constructor(private featureFlagDataService: FeatureFlagDataService) { }\r\n \r\n  featureOff(featureName: string) {\r\n    return !this.featureOn(featureName);\r\n  }\r\n \r\n  featureOn(featureName: string) {\r\n    if (!featureName) {\r\n      return true;\r\n    }\r\n    \/\/ Find the feature flag that is turned on\r\n    return this._featureFlags &amp;&amp; !!this._featureFlags.find(feature =&gt; {\r\n      return feature === featureName;\r\n    });\r\n    \/\/ if feature not found, default to turned off\r\n  }\r\n \r\n  get initialized() {\r\n    return this._initialized;\r\n  }\r\n \r\n  \/\/ This method is called once and a list of features is stored in memory\r\n  initialize() {\r\n    this._featureFlags = [];\r\n    return new Promise((resolve, reject) =&gt; {\r\n      \/\/ Call API to retrieve the list of features and their state\r\n      \/\/ In this case, the method returns a Promise,\r\n      \/\/ but it could have been implemented as an Observable\r\n      this.featureFlagDataService.getFeatureFlags()\r\n        .then(featureFlags =&gt; {\r\n          this._featureFlags = featureFlags;\r\n          this._initialized = true;\r\n          resolve();\r\n        })\r\n        .catch((e) =&gt; {\r\n          resolve();\r\n        });\r\n  });\r\n}<\/pre>\n<h3>Create attribute directives to hide and disable elements<\/h3>\n<p>To hide an element based on whether a feature is turned on or off, use the following code to create a directive. This will enable the Angular templates to use this syntax:<\/p>\n<pre class=\"lang:default decode:true \">&lt;div [myRemoveIfFeatureOff]=\"'feature1'\"&gt;\r\n&lt;div [myRemoveIfFeatureOn]=\"'feature2'\"&gt;<\/pre>\n<p><strong>remove-if-feature-off.directive.ts<\/strong><\/p>\n<pre class=\"lang:default decode:true \">import { Directive, ElementRef, Input, OnInit } from '@angular\/core';\r\nimport { FeatureFlagService } from '..\/..\/services\/feature-flag.service';\r\n \r\n@Directive({\r\n  selector: '[myRemoveIfFeatureOff]'\r\n})\r\nexport class MyRemoveIfFeatureOffDirective implements OnInit {\r\n  @Input('myRemoveIfFeatureOff') featureName: string;\r\n \r\n  constructor(private el: ElementRef,\r\n              private featureFlagService: FeatureFlagService) {\r\n  }\r\n \r\n  ngOnInit() {\r\n    if (this.featureFlagService.featureOff(this.featureName)) {\r\n      this.el.nativeElement.parentNode.removeChild(this.el.nativeElement);\r\n    }\r\n  }\r\n}\r\n<\/pre>\n<p><strong>remove-if-feature-on.directive.ts<\/strong><\/p>\n<pre class=\"lang:default decode:true\">. . .\r\n@Directive({\r\n  selector: '[myRemoveIfFeatureOn]'\r\n})\r\nexport class MyRemoveIfFeatureOnDirective implements OnInit {\r\n  @Input('myRemoveIfFeatureOn') featureName: string;\r\n  \/\/ Same as myRemoveIfFeatureOff except call featureOn()\r\n}<\/pre>\n<h3>Call the feature flag service elsewhere in the app<\/h3>\n<p>In addition to the attribute directives, the feature flag service can be called throughout the app. For instance, a menu service could use the featureOn method to hide menu items if the user does not have the required permission.<\/p>\n<p><strong>menu.service.ts<\/strong><\/p>\n<pre class=\"lang:default decode:true \">private showMenuItem(featureName: string) {\r\n  return this.featureFlagService.featureOn(featureName);\r\n}<\/pre>\n<h3>Prevent routing to component<\/h3>\n<p>If access to a component should be prevented when a feature is turned off, this can be accomplished by using the canActivate option available in Angular\u2019s router.<\/p>\n<p><strong>my-routing.module.ts<\/strong><\/p>\n<pre class=\"lang:default decode:true\">{\r\n  path: 'my-component',\r\n  component: MyComponent,\r\n  canActivate: [FeatureFlagGuardService],\r\n  data: { featureFlag: 'feature1' }\r\n},<\/pre>\n<p><strong>feature-flag-guard.service.ts<\/strong><\/p>\n<pre class=\"lang:default decode:true \">@Injectable()\r\nexport class FeatureFlagGuardService implements CanActivate {\r\n \r\n  constructor(private featureFlagService: FeatureFlagService) { }\r\n \r\n  canActivate(route: ActivatedRouteSnapshot): Promise&lt;boolean&gt; | boolean {\r\n    \/\/ Get the name of the feature flag from the route data provided\r\n    const featureFlag = route.data['featureFlag'];\r\n    if (this.featureFlagService.initialized) {\r\n      if (featureFlag) {\r\n        return this.featureFlagService.featureOn(featureFlag);\r\n      }\r\n      return true; \/\/ no feature flag supplied for this route\r\n    } else {\r\n      const promise = new Promise&lt;boolean&gt;((resolve, reject) =&gt; {\r\n        this.featureFlagService.initialize()\r\n          .then(() =&gt; {\r\n            if (featureFlag) {\r\n              resolve(this.featureFlagService.featureOn(featureFlag));\r\n            } else {\r\n              resolve(true);\r\n            }\r\n          }).catch(() =&gt; {\r\n            resolve(false);\r\n          });\r\n      });\r\n      return promise;\r\n    }\r\n  }\r\n}<\/pre>\n<p>This implementation of feature flags has proven to be helpful on my current project and has allowed us to proceed with the development of new features without the risk of introducing new functionality before QA is ready. I\u2019ve introduced a few enhancements along the way, including:<\/p>\n<ul>\n<li>addition of a scope for the feature \u2013 meaning the feature is available only for certain criteria<\/li>\n<li>an option for the route guard to check for any of an array of features\u2019 being turned on<\/li>\n<\/ul>\n<pre class=\"lang:default decode:true\">if (this.featureFlagService.anyFeatureOn(['feature1', 'feature2', 'feature3']))\r\n \r\n{  canActivate: [FeatureFlagGuardService],\r\n   data: { anyFeatureFlags: ['feature1', 'feature2', 'feature3'] }\r\n}<\/pre>\n<p>Also, it is worth highlighting that if a feature name is misspelled either in the configuration or in the code, then it is considered turned off. The effect of this approach is that a feature will not accidentally be turned on.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In an agile development environment, there may be partially completed features or features with external dependencies that are not ready. Instead of relying on multiple code branches, instead you may opt for deploying code with a feature turned off. Later, that feature should be turned on via a configuration or database change. This post provides sample code that you can use to implement feature flags in your Angular app.<\/p>\n","protected":false},"author":582,"featured_media":27510,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[122],"tags":[51,3],"class_list":["post-27609","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-angular","tag-angular","tag-team"],"acf":[],"blog_post_summary":"<p>In an agile development environment, there may be partially completed features or features with external dependencies that are not ready. Instead of relying on multiple code branches, instead you may opt for deploying code with a feature turned off. Later, that feature should be turned on via a configuration or database change. This post provides sample code that you can use to implement feature flags in your Angular app.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/27609","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/users\/582"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/comments?post=27609"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/posts\/27609\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media\/27510"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/media?parent=27609"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/categories?post=27609"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/premier-developer\/wp-json\/wp\/v2\/tags?post=27609"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}