Add Feature Flags to Your Angular App in 10 Minutes

It’s needless to say that Single-Page Applications (SPA) took over the web development world by storm. Like it or not, SPA frameworks are here to stay, and one of the most popular ones—Angular—will be the topic of this article.

This is what the tutorial covers:

  • Create an Angular app from scratch using Angular CLI
  • Build simple CRUD app using Angular components, services, models, and routing
  • Expand your app’s functionality with feature flags branching by Split
  • Dive deeper into Split features with event tracking and monitoring

What will the app be about? Hopefully, you’re a sports fan as you’ll be building a simple app to track favorite basketball players!

Prerequisites

To follow this tutorial on adding feature flags to your angular app, you should have:

  • Basic knowledge of how client-side web applications work
  • Basic familiarity with the command line
  • Beginner level understanding of Angular and TypeScript
  • Node.js and npm installed
  • A Split account

The full code example is available inside this splitio-examples GitHub repo if you prefer to follow the existing codebase while reading.

Bootstrap Angular App Using the CLI

Angular development team made it really simple to bootstrap a hello world example app. The only precondition is to install the Angular CLI on your machine locally, in case you don’t already have it.

npm install -g @angular/cli
Code language: CSS (css)

This will now allow you to use the ng command(s) anywhere in your terminal. Position yourself wherever in the local folder structure you want to create the app project folder, and run:

ng new angular-basketball-app --strict false
Code language: Arduino (arduino)

No need to worry about creating the project directory. The CLI will create it for you as a child directory in whichever folder you run the above command. The --strict false flag will create a project without strict Typescript compiler rules, so you don’t get tangled up in compiler warnings in these early stages. You’ll get prompted for some information by the CLI console. Opt-in for answers like in the screenshot below.

Navigate to the newly formed project directory:

cd angular-basketball-app
Code language: Bash (bash)

Don’t get scared with the endless amount of files this operation generates. Besides application source code, the CLI’s app-bootstrap command creates additional files for unit and end-to-end (e2e) tests by default. You’ll want to focus on the content of the src/app folder, as the main part of Angular’s app elements – like components and services – are placed inside it.

To have a bit more sense of what’s going on here use this command:

ng serve --open
Code language: Arduino (arduino)

The ng serve command launches the local server created by the framework, watches for file changes, and rebuilds the app as you save those changes. The --open (or just -o) option automatically opens the browser at http://localhost:4200. At this URL browser now renders a placeholder Angular page. You’ll be using the ng serve command a lot during the development. Each time you’d want to start the Angular server and see your app in the browser.

Take a look at the generated src/app/app.component.html file. It contains some placeholder HTML and CSS you’ll remove shortly and put in some of your own logic. But it’s important to notice that this template file has its TypeScript counterpart src/app/app.component.ts. Both of those files make up a key building element of Angular—a component. And Angular is all about components. The whole application is just a tree of components that starts with the top-level root AppComponent class exported from src/app/app.component.ts.

Replace the whole content of app.component.html with this and save:

<span>Hello, this is my app called {{title}}</span>
Code language: Django (django)

The browser auto-reloads on save and now displays the name of your app in the place of {{title}}. And it actually displays whatever you put as the value of the title field inside the AppComponent class. Try changing it back in the TypeScript file and the name will change again in the browser. The bottom line is that properties defined on the component class level are bound to the template file of that component, and eventually displayed to the user in the browser through HTML markup. And it all happens dynamically, of course. These bindings between component (TypeScript) and template (HTML) represent workhorses of Angular and make it a great framework for building cool interactive client-side apps.

Build a Model, Build a Service

Let’s start building the basketball app. It’ll have two different screens (pages or client-side routes). The first screen will show the list of basketball players with some additional data and CRUD action buttons, while the other screen should display a form to add a new player. Each screen will be associated with a different component and both of those components will be communicating with shared classes—services.

It’s always a good idea to start with the model. Make a dedicated models folder placed inside src/app. Once in the models folder, create a file named player.model.ts and add the following inside it:

export interface IPlayer { id: string; name: string; points: number; championshipCount: number; mvpCount: number; }
Code language: CSS (css)

This will come in handy in all other files working with a player entity, as TypeScript will help maintain the type safety and ease up the development through the exposed IPlayer model interface.

The next thing in line is to create an Angular service. Services in Angular are tailor-made to extract pieces of logic that will be used by different components and shared across the app. Go back to the src/app and create a folder named services. Navigate to src/app/services and utilize the CLI to generate a service:

ng generate service player

Replace the generated contents of player.service.ts with this:

import { Injectable } from '@angular/core'; import { IPlayer } from '../models/player.model'; @Injectable({ providedIn: 'root' }) export class PlayerService { players: IPlayer[] = [{ id: Math.random().toString(), name: 'Michael Jordan', points: 32292, championshipCount: 6, mvpCount: 6 }, { id: Math.random().toString(), name: 'Kobe Bryant', points: 33683, championshipCount: 5, mvpCount: 1 }, { id: Math.random().toString(), name: 'LeBron James', points: 35367, championshipCount: 4, mvpCount: 3 }]; public addPlayer(player: IPlayer) { this.players.push(player); } public deletePlayer(id: string) { this.players = this.players.filter(player => player.id !== id); } }
Code language: JavaScript (javascript)

Here you’ll be using a hardcoded list of players defined in the players field on the service class. You’ll be able to add or remove the players from the list by using addPlayer and deletePlayer methods. Obviously, this approach is used to simplify the tutorial. In a real-world app, you’d want to communicate with some server’s REST API to get, add or remove the data. This way, the whole state will be reset once the app gets reloaded in the browser, but that’s enough for the tutorial’s purpose.

Create Your First Angular Component

Off you go to the components. Navigate to src/app and utilize Angular CLI yet again to generate a PlayersComponent to display a screen for a list of players.

ng generate component players

Replace the code in the players.component.ts file with:

import { Component } from '@angular/core'; import { Router } from '@angular/router'; import { PlayerService } from '../services/player.service'; @Component({ selector: 'app-players', templateUrl: './players.component.html', styleUrls: ['./players.component.scss'] }) export class PlayersComponent { constructor( private router: Router, public playerService: PlayerService ) { } addPlayer() { this.router.navigate(['/add-player']); } deletePlayer(id: string) { this.playerService.deletePlayer(id); } }
Code language: JavaScript (javascript)

Notice how this component exposes methods to add and delete a player. The deletePlayer component method is just forwarding the call to the deletePlayer service method, previously defined in the PlayerService. Therefore PlayerService is used as a dependency of the component inside the component class’s constructor. But you’re far from done here. The component still doesn’t render anything to the browser. Replace everything in players.component.html with this HTML code:

<header class="header"> My basketball list <button (click)="addPlayer()">Add player</button> </header> <main> <div class="player_row"> <div class="cell">Name</div> <div class="cell">Points</div> <div class="cell">Championships</div> <div class="cell">MVP titles</div> <div class="cell">Actions</div> </div> <div *ngFor="let player of playerService.players" class="player_row"> <div class="cell">{{ player.name}}</div> <div class="cell">{{ player.points}}</div> <div class="cell">{{ player.championshipCount}}</div> <div class="cell">{{ player.mvpCount}}</div> <div class="cell"> <button (click)="deletePlayer(player.id)">delete</button> </div> </div> </main>
Code language: Django (django)

This code lists all the players defined in the PlayerService utilizing Angular’s *ngFor directive – an extremely popular one to render listed items in a loop. It also displays two different button types, one to add a player that invokes the addPlayer method defined on the component, and the other to delete a player that invokes the deletePlayer method, which gets rendered for each player row, and also “lives” in the component.

Styling HTML is not a focus of this article. However, to make the browser display more readable content you can copy the code from src/app/players/players.component.scss from the repo(https://github.com/splitio-examples/split-angular-basketball-app-example) and use it in the local players.component.scss file.

Angular Routing To Make It All Work

This all may sound logical, but you’re probably wondering how all these pieces of code lying around can be picked up to display something meaningful in the browser. For that, one more step needs to be done, and that is routing.

To build an Angular app with multiple routes (and in the real world, that is usually the case), you’ll need to create a routing module. Create a file called app-routing.module.ts and place it inside src/app. Add this code to the file:

import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { PlayersComponent } from './players/players.component'; const routes: Routes = [ { path: 'players', component: PlayersComponent }, { path: '**', redirectTo: 'players' } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Code language: JavaScript (javascript)

This is how the routes are registered inside the Angular app. When a user navigates to /players, the PlayersComponent will render. If she navigates to any other route (marked with ** placeholder), the app will redirect to /players route. Probably you’re thinking it’s not too exciting to have just one route, but this code will start making more sense once we add another route (and another component) later in the tutorial.

If a user puts any route name in the address bar (or simply navigates via hyperlinks) there is no request for the new HTML page on the server. This is the core “feature” of a single page application – there are multiple screens rendered at multiple routes, but it’s all handled by Angular’s internal router that operates client-side exclusively.

The tradeoff is a bit bigger first load (because you need to load the whole Angular/JavaScript bundle needed to run the app), but subsequent navigation between screens (routes) is smooth and without any blank page flickering. Usually, the only requests your app makes after the initial load is to fetch various data from a server, often through a REST API.

You’re still not finished getting the routing in place. Replace the whole content of app.component.html with this single line of code:

<router-outlet></router-outlet>
Code language: Django (django)

router-outlet is a native directive of the framework (part of Angular’s own RouterModule which is imported inside AppRoutingModule) that instructs Angular where to render the content (meaning components) associated with different routes. In other words, at this place in the HTML file, the template bound to some component will be rendered as long as that component is matched to the route path inside AppRoutingModule.

You’re most probably seeing the Angular compiler error in the console now. Angular engine doesn’t recognize the router-outlet element if AppRoutingModule isn’t included as a dependency of the main AppModule. In the app.module.ts add the following line near the top:

import { AppRoutingModule } from './app-routing.module';
Code language: JavaScript (javascript)

Now add AppRoutingModule to the imports array of the module:

... imports: [ BrowserModule, AppRoutingModule // this is the new line ], ...
Code language: Arduino (arduino)

Finally, you can open the browser at localhost:4200/players and see the materialized list of three hardcoded players in front of you, luckily with some CSS in place:

The delete button should work as expected, but the same can’t be said for the one to add a new player. It currently leads to an unexisting route (which eventually redirects to the /players route, due to ** placeholder redirect rule), but you’ll get to that in the next chapter.

Using Angular Template Form

While in src/app generate one more component:

ng generate component add-player
Code language: C# (cs)

Replace everything in add-player.component.ts with:

import { Component } from '@angular/core'; import { NgForm } from '@angular/forms'; import { Router } from '@angular/router'; import { IPlayer } from '../models/player.model'; import { PlayerService } from '../services/player.service'; @Component({ selector: 'app-add-player', templateUrl: './add-player.component.html', styleUrls: ['./add-player.component.scss'] }) export class AddPlayerComponent { constructor( private playerService: PlayerService, private router: Router ) { } onSubmit(form: NgForm) { const player: IPlayer = { id: Math.random().toString(), name: form.value.name, championshipCount: form.value.championships, points: form.value.points, mvpCount: form.value.mvpTitles }; this.playerService.addPlayer(player); this.router.navigate(['/players']); } }
Code language: JavaScript (javascript)

The main purpose of the AddPlayerComponent will be for a user to create a new player via an HTML form and through the onSubmit method. The method receives Angular’s form instance and extracts the values from it before forwarding it to the addPlayer method of the PlayerService. The form fields are defined in the add-player.component.html:

<p class="title"> Add player</p><br><br> <form class="form" #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm)"> <div> <label class="label" for="name">Name</label> <input type="text" name="name" ngModel> </div> <div> <label class="label" for="email">Number of Points</label> <input type="number" id="points" name="points" ngModel> </div> <div> <label class="label" for="email">Number of Championships</label> <input type="number" id="points" name="championships" ngModel> </div> <div> <label class="label" for="email">Number of MVP titles</label> <input type="number" id="points" name="mvpTitles" ngModel> </div> <div><button type="submit">Submit</button></div> </form>
Code language: Django (django)

The name of each form field is defined via the value of the name attribute, and Angular’s native ngModel directive takes care that user input gets mapped correctly. To use template forms in Angular, FormsModule provided by Angular needs to be included in app.module.ts (otherwise you’ll again bump into the Angular compiler error in the terminal). Add these lines to the app.module.ts file:

... import { FormsModule } from '@angular/forms'; ... imports: [ BrowserModule, AppRoutingModule, FormsModule // this is the new line ], ...
Code language: C# (cs)

Again, if you want to use CSS styles referenced in the HTML above, just copy the contents of src/app/add-player/add-player.component.scss from the repo(*https://github.com/splitio-examples/split-angular-basketball-app-example) and use it in the local add-players.component.scss file.

Don’t forget to include the extra route in the app-routing.module.ts. First add the import statement at the beginning of the file:

import { AddPlayerComponent } from './add-player/add-player.component';
Code language: JavaScript (javascript)

Additionally, include the new route match rule in the routes array, just between the /players route and ** universal route.

... { path: 'add-player', component: AddPlayerComponent }, ...
Code language: CSS (css)

And that’s all there is to it. Feel free to open the browser at localhost:4200/players now. The add player button should navigate to the fully rendered localhost:4200/add-player route where you’ll be able to interact with the form and submit it.

Once submitted, the app navigates back to the /players route. That is taken care of by the this.router.navigate(['/players']) line in the onSubmit method inside the AddPlayerComponent.

Making a Feature Flag

Applications get released in cycles and each version usually brings a new set of features. Adding features isn’t always easy. At times it can be a simple implementation from the technical standpoint but a game-changer in the user experience, so you’d want to be careful about releasing it to the whole app’s audience at once. On other occasions it’s something users won’t even notice, but it brings error-prone code changes or refactorings you simply can’t release without making a thorough regression test first. In both cases feature flags can help make those processes less painful.

A good practice is to introduce feature flags in the early development stages, so branching logic can naturally scale up together with your project. Another advantage of feature flagging is that, if used properly, the testing phase of your app can be completely moved to the production environment, which in the end is the most relevant one to test against.

As an example, imagine that you’re still not sure if the add/remove buttons should be present on the players’ list screen. You’d want to perform some kind of A/B testing where only 50% of the users will see the buttons, while others won’t, and you’ll track user feedback to reach the final decision on the positioning of the buttons. Which user gets what kind of display will be completely random and decided by a configured feature flag.
To create a feature flag, you’ll need access to the Split application. If you don’t have it yet, you should register for a Split account here to continue. After you log in to Split, go to the Splits section on the left, and click Create Split. In the dialog, you should enter the name of the split, which you’ll simply call player_split, and for Traffic Type select user. Leave the default settings for other fields and click Create to proceed.

The following screen is shown:

To add the split settings, click Add Rules.

One of the most important concepts in understanding how feature flags work is called treatment. The term is used to describe the state of a feature in Split. Every feature you’ll release through Split has at least two treatments. The default values for treatments are on and off, but custom values can also be defined. Later in the tutorial, you’ll use on treatment to show add/remove buttons, while the same will remain hidden for the users with the off treatment.

To define the targeting audience for this split, proceed with setting up the targeting rules. You’ll define a percentage split, and it can be set up inside the Set The Default Rule section. Percentage split means treatments will be randomly distributed between users in a predefined ratio, which is 50:50 in this case.

In a rare case the given split isn’t wired up to your application at runtime, the users will branch according to what you’ve set up inside the Set The Default Treatment section. Recommended practice here is to have the off treatment as the default one, as you probably don’t want new features to be accessible to all users, especially if those were not tested before the release.

Click Save changes, then click Confirm to save the split settings.

Create a Split Service in Angular

Back in the root of the Angular application, you’ll need to use the Browser SDK provided by Split to successfully integrate feature flags inside your app. More information about how to consume SDK APIs can be found here, but for starters just install the npm library @splitsoftware/splitio-browserjs.

npm install @splitsoftware/splitio-browserjs
Code language: CSS (css)

Generate a dedicated split service where the whole logic regarding the feature flags will be encapsulated. While positioned in src/app/services run this command in the terminal:

ng generate service split
Code language: Swift (swift)

Replace the code in the split.service.ts with:

import { Injectable } from '@angular/core'; import { SplitFactory } from '@splitsoftware/splitio-browserjs'; import * as SplitIO from '@splitsoftware/splitio-browserjs/types/splitio'; import { Subject } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class SplitService { private splitClient: SplitIO.IClient; public sdkReady$: Subject<void> = new Subject<void>(); public initSdk(userId: string): void { const splitSDK: SplitIO.ISDK = SplitFactory({ core: { authorizationKey: 'YOUR_API_KEY', key: userId } }); this.splitClient = splitSDK.client(); this.splitClient.on(this.splitClient.Event.SDK_READY, () => { this.sdkReady$.next(); }); } public getTreatment(): string { return this.splitClient.getTreatment('player_split'); } }
Code language: C# (cs)

There’s a lot of stuff going on here. The method initSdk is needed to connect to the Split API in the first place. You can’t use any of your configured treatments unless you initialize Split in the app. Since this initialization is a precondition to all subsequent Split-related actions, it’s good to call this method as soon as the application starts.

A Split SDK instance is stored in the splitSDK variable and created by invoking the exposed SplitFactory method. authorizationKey and key are two values needed to invoke the method. The value for the authorizationKey is the API key provided to you by Split.

Instead of the YOUR_API_KEY placeholder, you should use the one from the Admin Settings of the Split dashboard. Click the square workspace icon in the upper left. Click Admin Settings, API keys. The key you’ll need is the JavaScript SDK of the type browser for the staging environment—the first one from the list below.

For the key property you’d want to use a value that is unique for the given user of the application. Split assures a consistent UI experience for each user based on the value of key. In other words, as long as the same value is sent for key, the Split API will send back the same treatment. A good idea to send here is some kind of unique user identification.

The Split client instance is created by invoking the client method on the splitSDK object. Note how you’d need to register a listener based on the client’s instance Event.SDK_READY event. It’s important to catch that event because it will signal to the rest of your app that Split APIs are ready to use. If a part of the app would try to use a Split API (like getTreatment) before this event triggers, it would simply not work, as the API connection isn’t established.

As part of the event’s callback, the line this.sdkReady$.next() runs. sdkReady$ is a subject registered in the service that emits events to all of its subscribers, once Event.SDK_READY fires. Another method exposed on the SplitService is getTreatment. It provides a treatment value for the given split name, that is sent as a parameter while invoking the getTreatment function from the Split API. The method takes a split name as an argument, so you’d want to put player_split there, as that name is used while creating the split in the dashboard.

Wire up Feature Flags to Alter Angular Content

The code in the SplitService is still not put to good use. Start with initializing the SDK as soon as the app starts. The ideal place for that is inside the AppComponent (defined in app.component.ts). Since there is still generated code in that file, you should completely replace it with this:

import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { SplitService } from './services/split.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { constructor( private splitService: SplitService, private route: ActivatedRoute ) { } ngOnInit() { this.route.queryParams.subscribe(params => { if (params.user) this.splitService.initSdk(params.user); }); } }
Code language: JavaScript (javascript)

There are two important things to notice here and both are related to some of the useful Angular features.

For the sake of the article the user identification will be pulled from the URL’s query param in the form of ?user=someone. There is an effective way to get the query params in Angular, by subscribing to queryParams observable exposed through the ActivatedRoute interface. The interface represents the route object where a given component (in this case the AppComponent) is rendered. User data is then forwarded to the initSdk method previously defined in the SplitService

All of the above is placed inside the ngOnInit method. It means this piece of code will run as soon as the OnInit lifecycle hook gets triggered. It happens when the AppComponent gets initialized, at the very start of unfolding the Angular’s component tree.

To show or hide places of the UI based on data given by Split API, there are still few changes needed in players.component.ts. Import a reference to the SplitService at the top:

import { SplitService } from '../services/split.service';
Code language: JavaScript (javascript)

Add a line just above the constructor to declare a flag for displaying crud buttons:

crudButtonsShown: boolean = false;
Code language: Arduino (arduino)

Inside the constructor, add a new dependency to SplitService:

private splitService: SplitService
Code language: Arduino (arduino)

Finally, add the ngOnInit method where you’ll get subscribed to sdkReady$ subject:

ngOnInit() { this.splitService.sdkReady$.subscribe(() => { const treatment = this.splitService.getTreatment(); console.log('treatment:', treatment) if (treatment === 'on') this.crudButtonsShown = true; else this.crudButtonsShown = false; }, error => { console.log('Error connecting to Split SDK'); }); }
Code language: JavaScript (javascript)

In the sibling HTML file, players.component.html, only two lines need to be changed. You’ll add one of the most common directives in Angular – *ngIf – to conditionally display the buttons. Replace the button tags already there with the following:

... <button (click)="addPlayer()" *ngIf="crudButtonsShown">Add player</button> ... <button *ngIf="crudButtonsShown" (click)="deletePlayer(player.id)">delete</button>
Code language: Django (django)

You’re now ready to test the feature flag.

Open localhost:4200/players?user=john in the browser (make sure your Angular server is running) and open the DevTools with F12 (if you are using Chrome). Position yourself to the Console tab in the DevTools. If the connection to Split SDK is established the code inside the ngOnInit method of PlayersComponent should successfully fetch the treatment for the user based on the value inside the query params (john). The treatment value is logged in the console, and it either shows or hides the crud buttons. Remember, since we configured a percentage split, you don’t know in advance if the buttons will be shown or hidden (the chance is 50% for both).

However, what you should rest assured with is the following:

Every new request for the same URL (localhost:4200/players?user=john) should give you back the same treatment since the user identification (key) didn’t change (it’s still john).

You can check this by refreshing the URL multiple times. The appearance of the buttons should remain in the same state as in the first load. But, if you want to change that state, try changing the URL’s query param. If ?user=tim gives you the same result, try ?user=angela and so on, until you get the buttons shown or hidden.

The only rule is that, once you get the treatment for a given user for the first time, it stays consistent for the user, as long as you don’t reset it via the Split dashboard. That consistency is one of the key Split features.

Events and Monitoring at Split Dashboard

Besides creating flags, Split offers a palette of various services. One of those is tracking and monitoring the behavior of flagged features and their users in the real world. Split client instance exposes .track() method that receives up to four arguments:

Traffic Type: The traffic type of the key in the track call. You’d want to use the same value that you added to the traffic type when creating a split (user).
Event Type: Basically it’s the name of the event in the Split monitor panel and it can be set to any value. In this example, you’ll use the value user_age_group, as you’ll want to track the app’s users by age, to see where most of your audience comes from.
Value: Optional metric value for the specific user, it’s a dynamic piece of data that changes between users.
Properties: Optional set of additional properties

To learn more about tracking APIs explore Split docs.

To show how events are working let’s create a simple loop that will send tracking information to Split. In the split.service.ts expose a new method called fireManySplitEvents:

public fireManySplitEvents() { const interval = setInterval(() => { this.initSdk(Math.random().toString()); const ageGroups = [20, 30, 40, 50]; this.sdkReady$.subscribe(() => { this.splitClient.getTreatment('player_split'); this.splitClient.track('user', 'user_age_group', ageGroups[Math.floor(Math.random() * 4)]) }); }, 1000); setTimeout(() => { clearInterval(interval); }, 10000); }
Code language: JavaScript (javascript)

The code in the loop will run every second, and after ten seconds the interval will be canceled. For each iteration, it’s needed to connect to the Split API as a different user, so the initSdk method is called with a random argument to emulate an unique user identifier (Math.random().toString()). Each mocked user will then get her treatment of the player_split; and most importantly, it will send randomized tracking data about her age group. Notice how the track method receives three arguments: traffic type user, event type user_age_group, and a random age group value. Options are omitted, as you don’t need additional customization here.

In a production app, you’d want to send an event value based on some real piece of information fetched from the user object, browser environment, or any other relevant data source.

Make sure fireManySplitEvents gets called by adding it to app.component.ts, at the end of ngOnInit method:

ngOnInit() { this.route.queryParams.subscribe(params => { if (params.user) this.splitService.initSdk(params.user); }); this.splitService.fireManySplitEvents(); // this is the new line }
Code language: C# (cs)

Before opening the browser, open the Data hub in the Split dashboard by clicking the icon on the left menu. Use the dropdown and change the Data type to Events.

Click the blue Query button.

Data hub will now show the event data in near real-time, as it arrives at Split servers. Open your browser at http://localhost:4200/players (or refresh it if it’s already opened – in both cases the tracking event loop should fire), and observe what happens on the monitoring screen in Split. You should be able to see the generated events:

Notice how all the records have the same event type (user_age_group), but a random key (that is the user identification), and random metric value (varying from 20 to 50). You can pause live tracking at any moment by pressing the pause icon. Hopefully, this was a vivid enough example of how Split is more than just a flagging tool. Possibilities with event [tracking and monitoring] are huge and give you a chance to fine-tune it by your needs.

Removing the Feature Flags from Angular

It’s simple to remove the feature flag if you don’t need it anymore. Just remove the *ngIf directives from the players.component.html and the contents of the ngOnInit method inside player.component.ts. If you’re worried the Split APIs won’t be available in your app anymore – don’t be – as the initSdk method is completely decoupled from all other split-related actions. That means the invocation of initSdk inside app.component.ts stays as is, and if you need to connect to new treatments in the future it could be easily done, as you’re already connected to the Split API.

Final Thoughts

Congrats, you made it to the end of the tutorial. If you’re just getting started with Angular, hopefully, this cleared up your foggy landscape a bit. And if you already have significant experience in the framework, maybe you found some useful tips on how to split(!) client-side logic between components and services or just using the Angular CLI. But in both cases, you can probably take some relevant feature flag knowledge with you.

It’s been shown how various UI outcomes for web applications’ end users can be configured exclusively via feature flags, which deliver a consistent user experience. Once you get accustomed to Split, the sky’s the limit, as there are numerous ways and strategies on how to use and test feature flags inside your codebase at different points of the application development lifecycle.

Learn More About Adding Feature Flags

Ready to explore feature flags? Here are a few articles to get you started:

To stay up to date on all things in feature flagging and app building, follow us on Twitter @splitsoftware, and subscribe to our YouTube channel!