{"id":21820301,"url":"https://github.com/trademe/tm-feature-toggle","last_synced_at":"2025-06-14T15:39:57.209Z","repository":{"id":148328177,"uuid":"152000283","full_name":"TradeMe/tm-feature-toggle","owner":"TradeMe","description":"Feature toggle module for Angular. AoT friendly, lazy-loaded component and route based feature toggling.","archived":false,"fork":false,"pushed_at":"2019-05-17T01:03:12.000Z","size":127,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-27T16:49:36.981Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TradeMe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-08T01:09:35.000Z","updated_at":"2019-08-28T12:08:27.000Z","dependencies_parsed_at":"2023-05-10T22:30:37.111Z","dependency_job_id":null,"html_url":"https://github.com/TradeMe/tm-feature-toggle","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TradeMe%2Ftm-feature-toggle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TradeMe%2Ftm-feature-toggle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TradeMe%2Ftm-feature-toggle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TradeMe%2Ftm-feature-toggle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TradeMe","download_url":"https://codeload.github.com/TradeMe/tm-feature-toggle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248812738,"owners_count":21165471,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-27T16:31:13.991Z","updated_at":"2025-04-14T02:44:32.931Z","avatar_url":"https://github.com/TradeMe.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tm-feature-toggle\n\n[![npm version](https://img.shields.io/npm/v/tm-feature-toggle.svg)](https://img.shields.io/npm/v/tm-feature-toggle.svg)\n\n`tm-feature-toggle` is a super experimental proof-of-concept for enabling AoT-friendly, lazy-loadable components. It is primarily for the purposes of feature toggling and AB testing, but has the secondary benefit of enabling more granular bundle splitting. It also provides helpers for enabling route-based feature toggles. It attempts to provide a nice API for configuring features and whether or not a user can access them, *given the currently available APIs within Angular*. Some of those APIs are ***private*** and ***internal***, so this module should be used with an appropriate level of discomfort.\n\n___\n\n## Installation\n\n```sh\nnpm install tm-feature-toggle --save\n```\n\n___\n\n## Hacks\n\nBefore you try anything, you need to decide if you want to enable the sweet hacks on [@angular/router](https://angular.io/guide/router) that make all this possible. It can still work without the hacks, but the API is a little bit worse. \n\n### Enabling hacks\n\nTo enable the hacks, do the following somewhere near the entry point of your application:\n\n```typescript\nimport { patchRouter } from 'tm-feature-toggle';\n\npatchRouter();\n\n@NgModule({\n    // ... as uaual\n})\nexport class AppModule { }\n```\n\n### Disabling hacks\n\nIf you chose to not call **`patchRouter()`**, then you will need to manually configure route paths for each of your lazy-loaded feature modules. See the [\"Defining features\"](https://github.com/TradeMe/tm-feature-toggle#defining-features) section of this README for more details.\n\n### How it works\n\nTo see more details about the hacks that enable this functionality, check out [`'./lib/patch/router.ts'`](https://github.com/TradeMe/tm-feature-toggle/blob/master/projects/tm-feature-toggle/src/lib/patch/router.ts).\n\n___\n\n## API\n\n### Defining features\n\n#### `FeatureToggleModule.forFeature(features: Array\u003cFeature\u003e)`\n\nThe **`forFeature()`** method is used to declare list of feature variants which can be enabled or disabled for different users.\n\nA **`Feature`** has the following signature:\n\n```typescript\ninterface Feature {\n    path?: string; // REQUIRED if you *do not* run `patchRouter`. This path *must* be unique.\n    name: string;\n    loadChildren: string;\n    variant?: string;\n}\n```\n\n* `name` should be a unique name for the feature.\n\n* `loadChildren` should be a path to the module that should be lazy-loaded. It follows the same pattern as `RouterModule.forChild` from `@angular/router`, e.g. `./path/to/my/module.module#MyModule`. The root component of the feature should be added as the `bootstrap` component of the module.\n\n* `variant` should an optional identifier for a different version of the same feature. This is useful for when you want to do more than just enable or disable a feature. Using `variant` allows you to test multiple different versions of a feature.\n\nAdding a feature with **two** variants looks something like this:\n\n```typescript\nimport { FeatureToggleModule } from 'tm-feature-toggle';\n\n@NgModule({\n    // ...\n    imports: [\n        FeatureToggleModule.forFeature([{\n            path: '__feature__1', // A unique path is required if you *do not* run `patchRouter()`. Can be omitted otherwise.\n            name: 'feature1',\n            variant: '1',\n            loadChildren: './lazy-1/lazy-loaded.module#LazyLoadedModule'\n        }, {\n            path: '__feature__2', // A unique path is required if you *do not* run `patchRouter()`. Can be omitted otherwise.\n            name: 'feature1',\n            variant: '2',\n            loadChildren: './lazy-2/lazy-loaded.module#LazyLoadedModule'\n        }]\n    ],\n    // ...\n})\nexport class AppModule { }\n```\n\n**`FeatureToggleModule.forFeature()`** can be used from other lazy-loaded modules, so any variant of a feature can contain their own feature toggle definitions.\n\n\n\n### Configuring feature flags\n\nA **`Feature`** can is enabled via the **`Flags`** object. It should be an object where each `key` is either the name of a feature (for component-based toggles), or a router path to a feature (for route-based toggles). If the value is `fasle`, the feature will be disabled. If the value is not *exactly* `false`, the feature will be enabled. This means that a feature will default to on until a flag with a matching name is specified. While this is a bit counter-intuitive, it keeps the behaviour consistent between component-based and route-based features.\n\nThe value can be set to be a `string` to enable a matching variant of the feature.\n\n#### `FeatureToggleModule.forRoot(flags?: Flags)`\n\nThe most basic way to provide the configuration flags to your application is to use the **`forRoot()`** method.\n\n```typescript\nimport { FeatureToggleModule } from 'tm-feature-toggle';\n\n@NgModule({\n    // ...\n    imports: [\n        FeatureToggleModule.forRoot({\n            feature1: '1',\n            feature2: '4',\n            'routed-feature-2': false\n        })\n    ],\n    // ...\n})\nexport class AppModule { }\n```\n\nThis can be useful for testing out feature toggles, but it is unlikely that you want to hardcode your feature configuration like this. It is more likely that you will want to provide your own `FlagsService` implementation.\n\n#### `FlagsService`\n\n```typescript\nexport class FlagsService {\n    public getFlags (): Observable\u003cFlags\u003e {\n}\n```\n\nThe default **`FlagsService`** consumes the statically defined configuration from the **`forRoot()`** method. You probably want to override this in your `AppModule`.\n\n```typescript\nimport { FeatureToggleModule, FlagsService } from 'tm-feature-toggle';\n\n@NgModule({\n    // ...\n    imports: [\n        FeatureToggleModule.forRoot()\n    ],\n    provides: [\n        { provide: FlagsService, useClass: MyFlagsService }\n    ]\n    // ...\n})\nexport class AppModule { }\n```\n\nA custom implementation can then request the feature configuration asynchronously on a per-user basis, and rearrange that into a set of **`Flags`**.\n\n\n\n### Consuming features\n\n#### `FeatureToggleDirective`\n\nThe `[tmFeatureToggle]` directive is used to activate a feature within your application. You'll need to provide the `FeatureToggleModule` to use it.\n\n```typescript\nimport { FeatureToggleModule } from 'tm-feature-toggle';\n\n@NgModule({\n    // ...\n    imports: [\n        FeatureToggleModule\n    ],\n    // ...\n})\nexport class MyComponentModule { }\n```\n\nThen you can use it in your component:\n\n```html\n\u003cng-container\n    tmFeatureToggle=\"feature1\"\u003e\n\u003c/ng-container\u003e\n```\n\nThis can of course be combined with `*ngIf` to enable components based on application state (rather than user state):\n\n```html\n\u003cng-container\n    *ngIf=\"isAprilFoolsDay\"\n    tmFeatureToggle=\"sweetJoke\"\u003e\n\u003c/ng-container\u003e\n```\n\n#### `CanUseFeatureGuard`\n\nYou can also use the **`CanUseFeatureGuard`** to easily enable or disable route-based features.\n\n```typescript\nimport { CanUseFeatureGuard } from 'tm-feature-toggle';\n\n@NgModule({\n    // ...\n    imports: [\n        RouterModule.forChild([{\n            path: 'routed-feature',\n            loadChildren: './path/to/my/feature.module#FeatureModule',\n            canActivate: [CanUseFeatureGuard]\n        }])\n    ],\n    // ...\n})\nexport class AppModule { }\n```\n\nIt can be used with either [`canActivate`](https://angular.io/guide/router#milestone-5-route-guards) or [`canActivateChild`](https://angular.io/guide/router#milestone-5-route-guards).\n\n___\n\n## 🎉🎉🎉🎉🎉\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrademe%2Ftm-feature-toggle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrademe%2Ftm-feature-toggle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrademe%2Ftm-feature-toggle/lists"}