Feature Flagging
Feature flagging allows you to enable or disable specific features based on the user's environment (managed or self-hosted) and billing plan. This section covers:
- How to enable/disable an existing feature.
- How to add a new feature for flagging.
- How to show a placeholder when a feature is unavailable.
Enabling/Disabling an Existing Feature
To enable or disable a feature, use the FeatureService and the isFeatureEnabled method. Here's an example of adding a conditional check for a feature in the navbar:
<a routerLink="/settings" *ngIf="isAuthenticated">
- <button pButton icon="pi pi-wrench" label="Settings"></button>
+ <button
+ pButton
+ icon="pi pi-wrench"
+ label="Settings"
+ *ngIf="isAuthenticated && (settingsEnabled$ | async)"
+ ></button>
</a>In the corresponding component:
+ import { FeatureService } from '~/app/services/features.service';
export class NavbarComponent {
+ settingsEnabled$ = this.featureService.isFeatureEnabled('accountSettings');
constructor(
+ private featureService: FeatureService
) {}
}Adding a New Feature for Flagging
To add a new feature:
- Update the
featuresConfiguration: Add the feature tosrc/app/constants/feature-options.ts:
export const features: FeatureDefinitions = {
+ newFeature: {
+ default: false,
+ managed: {
+ free: false,
+ pro: true,
+ enterprise: true,
+ },
+ selfHosted: true,
+ },
};- Add a Description: Update
featureDescriptionsto include a label and description:
export const featureDescriptions: Record<keyof FeatureDefinitions, { label: string; description: string }> = {
+ newFeature: {
+ label: 'New Feature',
+ description: 'This is a description of the new feature.',
+ },
};- Use the Feature in Components: Use the
FeatureServiceto check if the feature is enabled:
newFeatureEnabled$ = this.featureService.isFeatureEnabled('newFeature');Showing a Placeholder for Unavailable Features
If a feature is unavailable, use the FeatureNotEnabledComponent to display a message to the user:
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FeatureNotEnabledComponent } from '~/app/components/misc/feature-not-enabled.component';
@Component({
standalone: true,
selector: 'app-example-component',
imports: [CommonModule, FeatureNotEnabledComponent],
template: \`
<ng-container *ngIf="featureEnabled$ | async; else featureNotAvailable">
<p>The feature is enabled and functional.</p>
</ng-container>
<ng-template #featureNotAvailable>
<app-feature-not-enabled feature="newFeature"></app-feature-not-enabled>
</ng-template>
\`,
})
export class ExampleComponent {
featureEnabled$ = this.featureService.isFeatureEnabled('newFeature');
constructor(private featureService: FeatureService) {}
}The FeatureNotEnabledComponent automatically determines the reason a feature is unavailable and shows a tailored message.
Enabling/Disabling a Feature in TS
It's easier to use Promises instead of Observables when feature flagging something in the Typescript code.
For this, we can use the isFeatureEnabledPromise method instead.
if (!(await this.featureService.isFeatureEnabledPromise('writePermissions'))) {
this.globalMessagingService.showWarn(
Write Permissions Disabled',
'It\'s not possible to add subdomains on the demo instance.',
);
return;
}Notes
- Dynamic Feature Updates: Features are resolved reactively based on user plan and environment.
- Default Behavior: Features without specific configuration fallback to the
defaultvalue. - Error Logging: If a feature's value cannot be resolved correctly, an error is logged, and the feature is disabled.
flowchart TD
subgraph App_Init
EnvService["EnvService.getEnvironmentType"] --> FeatureService
BillingService["BillingService.getUserPlan"] --> FeatureService
FeatureService --> ResolveFeatures
ResolveFeatures --> ActiveFeatures["activeFeatures$ (BehaviorSubject)"]
end
subgraph Feature_Service
ActiveFeatures --> GetFeatureValue["getFeatureValue"]
GetFeatureValue --> IsFeatureEnabled["isFeatureEnabled"]
IsFeatureEnabled -->|if not boolean| ErrorHandler["ErrorHandlerService.handleError"]
IsFeatureEnabled -->|if boolean| FeatureFlagResult["Observable: boolean"]
IsFeatureEnabled --> IsFeatureEnabledPromise["isFeatureEnabledPromise (Promise)"]
end
subgraph Component_Usage
FeatureFlagResult --> AsyncPipe["*ngIf (async pipe)"]
AsyncPipe -->|false| Placeholder["FeatureNotEnabledComponent"]
IsFeatureEnabledPromise --> AwaitCheck["if (!featureEnabled) { ... }"]
end
subgraph Feature_Config
FeatureOptions["feature-options.ts"] --> ResolveFeatures
FeatureDescriptions --> Placeholder
end
style Feature_Service fill:#eef7ff,stroke:#93c5fd
style Component_Usage fill:#fef3c7,stroke:#facc15
style App_Init fill:#e7f9ed,stroke:#34d399
style Feature_Config fill:#f3e8ff,stroke:#c084fc