Write a Customized Structural Directive

Let’s implement custom structural directives.

We'll cover the following

In this lesson, we’ll practice implementing custom structural directives. We’ll present a few use cases and go through them together. Let’s get started!

User roles

In larger applications, we often have different roles in the system. The application requires a user to sign in, and it resolves a role assigned to the user. Based on this user role, the system is able to use only specific features available for this role. It’s a very similar approach to what we had in the previous lesson with premium and standard users. However, premium account types can be represented by a simple boolean flag, while the other roles could contain several variations.

In our case, let’s assume we have these four roles in the application:

  • Basic user
  • Trial user
  • Premium user
  • Administrator

We’ll represent all of these in the following enum:

export enum UserRole =
	BasicUser = "basic",
	TrialUser = "trial",
	PremiumUser = "premium",
	Admin = "admin",
}

We want to implement our directive so that it can be used like this:

<div *appForRoles="['basic']"> only for basic users </div>

<div *appForRoles="['premium']"> only for premium users </div>

<div *appForRoles="['trial', 'basic']"> for trial and basic users </div>

<div *appForRoles="['basic','premium','admin']"> for all users except trial </div>

As we see, our directive needs data—an array, to be precise—which defines which roles should have their elements rendered.

Another requirement is to support changes in the array of roles, like in this case:

@Component({
  selector: 'app',
  template: `<div *appForRoles="roles"> </div>`,
})
export class AppComponent {
  roles = ['basic'];

	ngOnInit() {
		setTimeout(() => roles = ['premium'], 5000);
	}
}

We bind roles using the roles array and, after five seconds of component initialization, replace it with a new array that has other roles specified. We also want our directive to support behavior like this and react accordingly to changes. The last requirement is that the current user roles can also be changed.

We have access to the current role through UserService, which looks like this:

@Injectable()
export class UserService {
	
	get userRole$(): Observable<UserRole>{
		...
	}

	...
}

So, the user role is exposed as an Observable, and its value changes in time. Therefore, our directive should support this and react to these changes, too.

Now, let’s start coding!

The app already contains the things we had discussed. So:

  • The Enum class represents roles in the system.
  • The UserService class exposes these roles.
  • The AppComponent class has a few HTML elements for different use cases.

There’s also a button that changes the current role of the signed-in user so that we can test our directive thoroughly.

Get hands-on with 1400+ tech skills courses.