Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Loading Components Dynamically in an Angular App
Okta profile imageAlisa
Alisa forOkta

Posted on • Originally published atdeveloper.okta.com

     

Loading Components Dynamically in an Angular App

Businesses have unique and complex needs. In addition to the user or organization-specific data to show, there might be a need to display different views and content conditionally. The conditions might include the user's role or which department they belong to. The information about a user might be part of the authenticated user'sID token as a profile claim.

InAngular, you can show different components or even parts of templates conditionally using built-in directives such as*ngIf. Using*ngIf works when the amount of conditional template changes are small, but what if the view has a lot changes, or if the number of conditions to evaluate increases? Managing the correct view only by using*ngIf becomes difficult. These types of scenarios are wheredynamic components are helpful. Angular has the mechanics to load components at runtime so you can dynamically display content.

After following the instructions in this post, you'll have an Angular app usingAngular Material UI controls that displays unique content based on an authenticated user's claim value.

The main flow for the application is after initially launching a welcome page; you'll log in using Okta from a button in the toolbar and redirect to the guarded content. The toolbar now displays your name, and you'll see a rotation of dynamically created components based on a user claim from your ID token.

animated image showing logging in and displaying dynamic contents

In this post, we'll

  • Set up an Angular application with routing and lazy-loaded modules
  • Create an OIDC client in Okta
  • Add authentication to the Angular application
  • Capture authenticated user information and user claims
  • Simulate an external server call and implement the dynamic components
  • Complete displaying the dynamic components using the user claim value

We'll be covering a lot in this post to build out a non-trivial Angular application. As a result, this post assumes some Angular knowledge. We'll be blazing through some basics to focus on working through the dynamic components and user profile information.

If you are new to Angular, check out the following fantastic resources and guides first:

Prerequisites

Scaffold the Angular application

TheAngular CLI automates creating Angular apps quickly. It makes the project folder and scaffolds the application shell. We'll pass in the parameters to

  • add routing
  • use scss for styles
  • use inline templates
  • use inline styles
  • skip tests (The code repo includes tests to show working tests with testbed setup and authentication service spies, so feel free to include tests if you want to try that yourself.)

We'll have a lot of small components in this application, so inline templates and styles will allow us to minimize the number of files to touch.

Run the following command to create an Angular v13 app.

npx @angular/cli@13 new dynamic-components--routing--style=scss--inline-template--inline-style--skip-tests
Enter fullscreen modeExit fullscreen mode

You now have a directory nameddynamic-components with a working application skeleton. All CLI commands in the rest of this post should be run inside the project directory.

Next, we'll add the Angular Material component library to the application using their schematic. We'll pass in the parameters to

  • set the theme
  • add typography
  • enable animations

Run the following command with preset values to add the Angular Material v13 library and affirm that you will proceed with the installation.

 ng add @angular/material@13--theme=custom--typography=true--animations=true
Enter fullscreen modeExit fullscreen mode

Feel free to serve the app usingng run ornpm start in a second terminal so you can view the changes as you progress through this post.

Create components for the initial view

With our application scaffolded and libraries added, we can now add our code. Start by creating three components: aHome component that contains the application's default view, aMenu component to handle logging in, and aProfile component to display your name after authentication by running the following code.

ng generate component homeng generate component menung generate component profile
Enter fullscreen modeExit fullscreen mode

We can update what we'll first see when loading the application with these components. Open the project in your favorite IDE and open thesrc/app/app-routing.module.ts file to addHomeComponent as a default route, as shown below.

constroutes:Routes=[{path:'',component:HomeComponent}];
Enter fullscreen modeExit fullscreen mode

Next opensrc/app/app.module.ts. We need to add some modules for the Material components we'll use.

In theNgModuleimports array, add the following Angular Material component modules:

  • MatToolbarModule from@angular/material/toolbar
  • MatIconModule from@angular/material/icon
  • MatButtonModule from@angular/material/button
  • MatMenuModule from@angular/material/menu

Now we can update the templates. Opensrc/app/app.component.ts and replace the entire component with the following code.

@Component({selector:'app-root',template:`      <mat-toolbar color="primary">      <h1>My favorite work app</h1>      <div>        <app-profile></app-profile>         <app-menu></app-menu>       </div>    </mat-toolbar>    <router-outlet></router-outlet>   `,styles:[`     .toolbar { display: flex; justify-content: space-between; } `]})exportclassAppComponent{}
Enter fullscreen modeExit fullscreen mode

We added a Material toolbar that displays text, along with the contents of theProfile andMenu components. We'll update the template of those components in a bit. Below the toolbar, the<router-outlet></router-outlet> shows the view for the current route. You should see the output of theHome component when you serve the app.

Opensrc/app/home/home.component.ts, which is your welcome landing page. Feel free to change the template and styles to whatever suits you.

I added text, an illustration, and styles like below for my tastes.

@Component({selector:'app-home',template:`      <div>      <h2>Welcome! Log in to get started.</h2>      <img src="assets/welcome.svg" alt="welcome illustration" />    </div> `,styles:[`      .welcome {      display: flex;      flex-direction: column;      align-items: center;        h2 { margin: 3rem; }        img { width: 40%; }     }   `]})exportclassHomeComponent{}
Enter fullscreen modeExit fullscreen mode

Create module for dynamic components

Next, we'll create a new module,Protected, to hold the view guarded by authentication. We can pass in the parameters for routing, creating the default component, and lazy-loading, by running the following command.

ng generate module protected--routing--route=protected--module=app
Enter fullscreen modeExit fullscreen mode

This module also contains the dynamic components, interfaces, and directives for dynamic loading, which holds most of the app's business logic.

There's a directive for the component view reference namedDynamicDirective, and a component to house the dynamic component view and orchestrate the loading calledDepartmentComponent.

We'll put all the code in the same folder by running the following code.

ng generate component protected/departmentng generate directive protected/department/dynamic
Enter fullscreen modeExit fullscreen mode

Now on to the dynamically created components themselves. There's a base interface for all dynamically created components that contains component data namedDynamicComponent. We're being a little tricky for the CLI here, so we need to rename the interface after we generate it manually. First, create the interface by running the following command.

ng generate interface protected/department/dynamic--type=component
Enter fullscreen modeExit fullscreen mode

Open thesrc/app/protected/department/dynamic.component.ts file and rename the interface fromDynamic toDynamicComponent to help us better keep track of what the interface provides.

We have three dynamically created components to display content:Clawesome,Pawesome, andSmiley.

ng generate component protected/department/clawesome--flatng generate component protected/department/pawesome--flatng generate component protected/department/smiley--flat
Enter fullscreen modeExit fullscreen mode

Let's get the main view for theProtected module set up. The default view in this module showsProtectedComponent, which displays a task list and theDepartmentComponent dynamic component loader. First, we'll import the Material component modules, then update theProtected component template and styles, and populate the task list.

Opensrc/app/protected/protected.module.ts and add the following Material component modules to the imports array:

  • MatCardModule from@angular/material/card
  • MatListModule from@angular/material/list

Next opensrc/app/protected/protected.component.ts. First, we'll set up the tasks. Create a public array for task items in the component and set the values to whatever you want. Here's my task list.

publictasks:string[]=['Respond to that one email','Look into the thing','Reply to their inquiry','Set up the automation'];
Enter fullscreen modeExit fullscreen mode

For theProtectedComponent's template, we'll use Material's List component. Update the inline template and styles code to look like the following.

@Component({selector:'app-protected',template:`      <div>      <main>        <h2>My tasks</h2>        <mat-selection-list #todo>          <mat-list-option *ngFor="let task of tasks">              {{task}}            </mat-list-option>        </mat-selection-list>      </main>      <app-department></app-department>     </div>   `,styles:[`      .dashboard {       margin-top: 2rem; display: flex;        main {         width: 75%;        h2 { text-align: center; }        .task-list { width: 80%; margin: auto; }        mat-selection-list { max-width: 800px; }      }    }  `]})
Enter fullscreen modeExit fullscreen mode

If you want to check out your work by running the application, you'll need to manually type in the route for theProtected module as part of the URL.

localhost:4200/protected
Enter fullscreen modeExit fullscreen mode

When we add authentication, we'll automatically route to it.

Dynamic component loading

Next, let's get into this exciting dynamic component loading part! Here's how this works. TheDepartment component is the container for the dynamic components, and controls which component to show. TheDepartment component HTML template contains anng-template element with a helper directive to identify where to add the dynamic component to the view.

Angular v13 included updates to theViewContainerRef API to make working with dynamic components more straightforward. We could use Angular Component Development Kit (CDK)Portals instead since it has extra helper functionality, but let's take the updated API out for a spin. 😁

Each of the dynamic components needs the same base component interface. In our case, the base component interface is theDynamicComponent interface. Open each dynamic component file,Clawesome,Pawesome, andSmiley, and implement theDynamicComponent interface to the class. The interface is empty now, but we'll add members later. Feel free to remove theOnInit lifecycle hook too. TheClawesome component class looks like the following example, and thePawesome andSmiley component classes should look similar.

exportclassClawesomeComponentimplementsDynamicComponent{// ...remaining scaffolded code here}
Enter fullscreen modeExit fullscreen mode

Opensrc/app/protected/department/dynamic.directive.ts to inject theViewContainerRef. Your code will look like the following.

@Directive({selector:'[appDynamic]'})exportclassDynamicDirective{constructor(publicviewContainerRef:ViewContainerRef){}}
Enter fullscreen modeExit fullscreen mode

Now on to the container component. Opensrc/app/protected/department/department.component.ts. First, we'll update the template and styles. Update the inline template to include the template reference with theDynamic directive. I added text, so my template and styles look like the following.

@Component({selector:'app-department',template:`     <h3>Relax, you got this</h3>   <ng-template appDynamic></ng-template>  `,styles:[`      h3 { text-align: center; }   `]})
Enter fullscreen modeExit fullscreen mode

In the component class code, we have a bit to do. We need to load the dynamic components and rotate the components to display. We'll hard-code the list of components to show for now, but later we'll add a service and add in the logic for handling the user claim value.

We use theViewChild decorator on theDynamic directive to access where to insert the component. When creating the component, we pass in the componentType. Copy the following class code and replace yourDepartmentComponent class code.

exportclassDepartmentComponentimplementsOnInit,OnDestroy{@ViewChild(DynamicDirective,{static:true})privatedynamicHost!:DynamicDirective;privateinterval:number|undefined;privatecurrentIndex=1;privatemessages:{type:Type<DynamicComponent>}[]=[{type:ClawesomeComponent},{type:PawesomeComponent},{type:SmileyComponent}];publicngOnInit():void{this.loadComponent();this.rotateMessages();}publicngOnDestroy():void{clearInterval(this.interval);}privateloadComponent():void{if(this.messages.length===0)return;this.currentIndex=(this.currentIndex+1)%this.messages.length;constmessage=this.messages[this.currentIndex];constviewContainerRef=this.dynamicHost.viewContainerRef;viewContainerRef.clear();constcomponentRef=viewContainerRef.createComponent<DynamicComponent>(message.type);}privaterotateMessages():void{this.interval=window.setInterval(()=>{this.loadComponent();},10000);}}
Enter fullscreen modeExit fullscreen mode

Let's talk through theloadComponent method in a little more detail. First, we make sure we're rotating through the messages sequentially by keeping track of where in the array we are, then clearing out the previous component. To dynamically load the component, we use the directive as an anchor and create the component into its position in the DOM. ThecreateComponent method requires the component type, not the instance. We use the base interface as a generic type for all the components, and use concrete component type in the method parameter.

If you look at your app, you'll see the components rotating!

Add authentication

Now we can start customizing based on the user information.

Before you begin, you’ll need a free Okta developer account. Install theOkta CLI and runokta register to sign up for a new account. If you already have an account, runokta login. Then, runokta apps create. Select the default app name, or change it as you see fit. ChooseSingle-Page App and pressEnter.

Usehttp://localhost:4200/login/callback for the Redirect URI and set the Logout Redirect URI tohttp://localhost:4200.

What does the Okta CLI do?
The Okta CLI will create an OIDC Single-Page App in your Okta Org. It will add the redirect URIs you specified and grant access to the Everyone group. It will also add a trusted origin forhttp://localhost:4200. You will see output like the following when it’s finished:
Okta application configuration:Issuer:    https://dev-133337.okta.com/oauth2/defaultClient ID: 0oab8eb55Kb9jdMIr5d6
Enter fullscreen modeExit fullscreen mode

NOTE: You can also use the Okta Admin Console to create your app. SeeCreate an Angular App for more information.

Make a note of theIssuer and theClient ID. You will need them in the following steps.

We can use the Okta-provided Angular SDK to connect to the OIDC client quickly. Add the two packages by running the following command.

npminstall @okta/okta-angular@4 @okta/okta-auth-js@5.8--save
Enter fullscreen modeExit fullscreen mode

Opensrcs/app/app.module.ts and create anOktaAuth instance by adding the following before theNgModule and replacing the placeholders with theIssuer andClient ID from earlier.

import{OKTA_CONFIG,OktaAuthModule}from'@okta/okta-angular';import{OktaAuth}from'@okta/okta-auth-js';constoktaAuth=newOktaAuth({issuer:'https://{yourOktaDomain}/oauth2/default',clientId:'{yourClientID}',redirectUri:window.location.origin+'/login/callback'});
Enter fullscreen modeExit fullscreen mode

Next, addOktaAuthModule to theimports array and configure the provider for theOKTA_CONFIG token, as shown below.

@NgModule({...imports:[...,OktaAuthModule],providers:[{provide:OKTA_CONFIG,useValue:{oktaAuth}}],...})
Enter fullscreen modeExit fullscreen mode

Okta has a component for the login callback, but we need to add the route. Opensrc/app/app-routing.module.ts and add the following to your routes array.

{path:'login/callback',component:OktaCallbackComponent}
Enter fullscreen modeExit fullscreen mode

We also want to guard theProtected component route to authenticated users. Okta has a guard we can use. Opensrc/app/protected/protected-routing.module.ts to add acanActivate guard to the default route. Your routes array will look like the code snippet below.

constroutes:Routes=[{path:'',component:ProtectedComponent,canActivate:[OktaAuthGuard]}];
Enter fullscreen modeExit fullscreen mode

For the actual logging in, opensrc/app/menu/menu.component.ts to add a menu with login and logout buttons. We'll use some Okta-provided code to log in, log out, and identify the authenticated state. Update the component code to match the code below.

@Component({selector:'app-menu',template:`      <button mat-icon-button aria-label="Button to open menu" [matMenuTriggerFor]="menu">        <mat-icon>menu</mat-icon>     </button>     <mat-menu #menu="matMenu">        <button mat-menu-item *ngIf="!isAuthenticated" (click)="login()">          <mat-icon>login</mat-icon> <span>Login</span>       </button>       <button mat-menu-item *ngIf="isAuthenticated" (click)="logout()">          <mat-icon>logout</mat-icon> <span>Logout</span>      </button>     </mat-menu>  `})exportclassMenuComponentimplementsOnInit,OnDestroy{publicisAuthenticated=false;private_destroySub$=newSubject<void>();constructor(private_oktaAuth:OktaAuth,private_authStateService:OktaAuthStateService,private_router:Router){}publicngOnInit():void{this._authStateService.authState$.pipe(filter((s:AuthState)=>!!s),map((s:AuthState)=>s.isAuthenticated??false),distinctUntilChanged(),takeUntil(this._destroySub$)).subscribe((authenticated:boolean)=>this.isAuthenticated=authenticated);}publicngOnDestroy():void{this._destroySub.next();}publicasynclogin():Promise<void>{awaitthis._oktaAuth.signInWithRedirect().then(_=>this._router.navigate(['/protected']));}publicasynclogout():Promise<void>{awaitthis._oktaAuth.signOut();}}
Enter fullscreen modeExit fullscreen mode

Now that you can log in let's get your name displayed in the toolbar. Opensrc/app/profile/profile.component.ts. Okta's auth state has user info. Notice it's also available through a claim. Replace theProfile component code with the following.

@Component({selector:'app-profile',template:`      <ng-container *ngIf="name$ | async as name ">      <span>{{name}}</span>      </ng-container> `})exportclassProfileComponent{publicname$:Observable<string>=this._authStateService.authState$.pipe(filter((s:AuthState)=>!!s&&!!s.isAuthenticated),map((s:AuthState)=>s.idToken?.claims.name??''));constructor(private_authStateService:OktaAuthStateService){}}
Enter fullscreen modeExit fullscreen mode

Now you see your name when you log in to the application.

Format the dynamic components

All the messages have data to display. The data always includes an URL plus other content that is unique to each component. Let's define the interface to support the message data. Run the following command to create the interface.

ng generate interface message
Enter fullscreen modeExit fullscreen mode

There'll be three different message formats, one for each dynamic component. So we'll define a message type, standardized data properties, and a class that holds the type and data properties. Opensrc/app/message.ts and replace the contents with the following code.

exporttypeMessageType='Pawesome'|'Clawesome'|'Smiley';exportinterfaceMessageData{url:string;content?:any;}exportclassMessageItem{constructor(publictype:MessageType,publicdata:MessageData){}}
Enter fullscreen modeExit fullscreen mode

When we created the dynamic components earlier, they all implemented the same base interface,DynamicComponent. Since all the dynamic components have some data, we need to update theDynamicComponent interface to reflect this shared property that all the components will implement.

Opensrc/app/protected/department/dynamic.component.ts and add a property nameddata of typeMessageData to it. The interface now looks like the following.

exportinterfaceDynamicComponent{data:MessageData;}
Enter fullscreen modeExit fullscreen mode

Because we have separate components specifically designed to handle each message type, we keep the logic very straightforward. We'll apply template updates to each component and implement all the interface members by adding an input property to accept the data.

Start by openingsrc/app/protected/department/clawesome.component.ts. This component's data has an URL to an image and string content. Update the component to the following.

@Component({selector:'app-clawesome',template:`      <mat-card>      <img mat-card-image src="{{data.url}}" alt="Photo of a clawesome creature" >        <mat-card-content>        <p>{{data.content}}</p>        </mat-card-content>    </mat-card>  `,styles:[` .card { max-width: 300px; } `]})exportclassClawesomeComponentimplementsDynamicComponent{@Input()data!:MessageData;}
Enter fullscreen modeExit fullscreen mode

Each component is a Material card control with an image to display. Next opensrc/app/protected/department/pawesome.component.ts. In addition to the URL, content contains the propertiesname andabout. Update the component to the following.

@Component({selector:'app-pawesome',template:`      <mat-card>      <mat-card-header>        <mat-card-title>{{data.content.name}}</mat-card-title>          <mat-card-subtitle>Good doggo</mat-card-subtitle>      </mat-card-header>      <img mat-card-image src="{{data.url}}" alt="Photo of a pawesome creature" >        <mat-card-content>        <p> {{data.content.about}} </p>      </mat-card-content>    </mat-card>  `,styles:[` .card { max-width: 300px; } `]})exportclassPawesomeComponentimplementsDynamicComponent{@Input()data!:MessageData;}
Enter fullscreen modeExit fullscreen mode

Finally, opensrc/app/protected/department/smiley.component.ts. The only data in this message type is the URL. Update the component to the following.

@Component({selector:'app-smiley',template:`      <mat-card>      <img mat-card-image src="{{data.url}}" alt="Photo of a smiley creature" >        <mat-card-content>        <p>SMILE!</p>      </mat-card-content>    </mat-card> `,styles:[` .card { max-width: 300px; } `]})exportclassSmileyComponentimplementsDynamicComponent{@Input()publicdata!:MessageData;}
Enter fullscreen modeExit fullscreen mode

The rotating components look a little goofy now and you'll see a lot of errors in the console, but we'll get it fixed up soon.

Add claim in Okta dashboard

Next, we need to add the claim. We'll assign a department to the user and create a claim that pulls in the department value. We can do this in the Okta dashboard. Navigate toDirectory >People and click the user you'll log in with.

Navigate to theProfile tab and pressEdit. Scroll down toDepartment and enter the number1. We'll label the department names as either1 or2 for ease of coding.

Since we see different content by user claim, it's helpful to have more than one user. If you have a second user, update their department name to2, but you can also edit the department value between logging in.

For the claim, navigate toSecurity >API and select your Authorization Server to edit in the Okta dashboard. If you are using a Developer Account, you'll have one named "default". In your "default" Authorization Server, navigate to theClaims tab. Press theAdd Claim button to create a new claim. Name your claim "department", always include it in the "ID Token", and set its value touser.profile.department. Your inputs should look like the image below.

add claim form inputs

The ID token now contains the new claim with the value of the user's department.

new claimed named 'department' in claims list within Okta dashboard

Use claims in the application

The dynamic components are cool, but they don't do anything yet. The value of dynamic components is that the component contents change, and we can show whatever data in the component we want. Let's add the code to see how flexible dynamic components are. We need a service that emulates a server response to return messages. Run the following command to create the service.

ng generate service message
Enter fullscreen modeExit fullscreen mode

TheMessageService returns messages the company wants to display to its users. All messages have a URL, and some have additional content to display. Opensrc/app/message.service.ts and add the following code to fake out message responses as a private class property.

privatemessages:MessageItem[]=[{type:'Clawesome',data:{url:'https://images.pexels.com/photos/2558605/pexels-photo-2558605.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',content:'Cat ipsum dolor sit amet, meow for can opener to feed me',}},{type:'Clawesome',data:{url:'https://images.pexels.com/photos/1560424/pexels-photo-1560424.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',content:'Cat ipsum dolor sit amet, find dog bed and sleep all day',}},{type:'Clawesome',data:{url:'https://images.pexels.com/photos/3687957/pexels-photo-3687957.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',content:'Cat ipsum dolor sit amet, too cute for human to get mad'}},{type:'Pawesome',data:{url:'https://images.pexels.com/photos/97082/weimaraner-puppy-dog-snout-97082.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',content:{name:'Sammy',about:'Snuggly cloud borker'}}},{type:'Pawesome',data:{url:'https://images.pexels.com/photos/825949/pexels-photo-825949.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',content:{name:'Pittunia',about:'Maximum adorable shooberino'}}},{type:'Pawesome',data:{url:'https://images.pexels.com/photos/406014/pexels-photo-406014.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1',content:{name:'Bay',about:'Long snoot for pats'}}},{type:'Smiley',data:{url:'https://images.pexels.com/photos/2168831/pexels-photo-2168831.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940'}},{type:'Smiley',data:{url:'https://cdn.pixabay.com/photo/2017/06/17/13/11/axolotl-2412189_960_720.jpg'}}];
Enter fullscreen modeExit fullscreen mode

There's one more property to add. A real server call should only return the messages relevant to the department of the caller. Users of department "1" shouldn't see the messages for department "2" by inspecting the network response. We'll handle this within the service by creating a mapping for message types by the department. Add thedepartmentMapping property and update the constructor for the service as shown below.

privatedepartmentMapping:Map<number,MessageType[]>=newMap<number,MessageType[]>();constructor(){this.departmentMapping.set(1,['Smiley']);this.departmentMapping.set(2,['Pawesome','Clawesome']);}
Enter fullscreen modeExit fullscreen mode

Now let's add a method to get the messages. Add the following code to filter the hard-coded messages.

publicgetMessages(department:number):MessageItem[]{constmessageTypes=this.departmentMapping.get(department)??[];returnthis.messages.filter(m=>messageTypes.includes(m.type));}
Enter fullscreen modeExit fullscreen mode

Next, we need to call this service method. To do so, we'll read the claims values of the authenticated user and pass in the department to thegetMessages method. We'll access the claim through the ID token from Okta's auth state subject. Even though we're in a guarded route, we'll still add the safety measures to verify the user authentication, and to return a default value if the claim isn't on the ID token for some reason. Opensrc/app/protected/protected.component.ts and update to the following code.

exportclassProtectedComponentimplementsOnInit{// ... task list property here don't deletepublicmessages:MessageItem[]=[];constructor(private_authStateService:OktaAuthStateService,private_messageService:MessageService){}publicngOnInit():void{this._authStateService.authState$.pipe(filter((s:AuthState)=>!!s&&!!s.isAuthenticated),map((s:AuthState)=>+s.idToken?.claims['department']??0),take(1)).subscribe((d:number)=>this.messages=this._messageService.getMessages(d));}}
Enter fullscreen modeExit fullscreen mode

We now have a list of messages to display to the user. We'll pass this to the dynamic component's container component to action. In the inline template forProtectedComponent, update the<app-department> element to pass inmessages as an input property. You'll see an error in the IDE since we haven't created the input property in the Department component yet. The element in the inline template will look like the code below.

<app-department[messages]="messages"></app-department>
Enter fullscreen modeExit fullscreen mode

You can probably see where this is going. Now we'll update the Department component to support the input property. Opensrc/app/protected/department/department.component.ts. Replace the hardcoded privatemessages property into a public input property like the code snippet below.

@Input()publicmessages:MessageItem[]=[];
Enter fullscreen modeExit fullscreen mode

You'll have a new error in the app because our code in theloadComponent method expects a component type. We'll add a factory method to return the component type to create by matching theMessageType to the component type like the following example.

privatecomponentTypeFactory(type:MessageType):Type<DynamicComponent>{letcomp:Type<DynamicComponent>;if(type==='Pawesome'){comp=PawesomeComponent;}elseif(type==='Clawesome'){comp=ClawesomeComponent;}else{comp=SmileyComponent;}returncomp;}
Enter fullscreen modeExit fullscreen mode

Then we can update theloadComponent method to use the factory method. We also have message data to pass into the components, although the dynamic components can't support the input property. Update the code and add the new line of code to pass data to the components like the code block below.

constcomponentRef=viewContainerRef.createComponent<DynamicComponent>(this.componentTypeFactory(message.type));componentRef.instance.data=message.data;
Enter fullscreen modeExit fullscreen mode

Now everything should compile, and you should have a working app that displays a rotating set of cute animals images to help you power through working on that task list. Try logging in as a user with a different department (or change the department value for yourself in the Okta dashboard) to see the various dynamic components at work.

You can find the code forthis project on GitHub.

GitHub logo oktadev / okta-angular-dynamic-components-example

Loading Components Dynamically in an Angular App

We did a lot in this tutorial and had a lot of dependencies on Material and Okta libraries. Please check outthis commit to see the changes required to run tests and create mocks of the Okta services.

Learn more

We covered a lot in this post, tried out a new API, and explored some cool concepts. If you liked this post, check out the following.

Don't forget to follow us onTwitter and subscribe to ourYouTube channel for more exciting content. We also want to hear from you on what tutorials you want to see and how you use dynamic components. Leave us a comment below.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Authenticate, manage, and secure users in any application within minutes.

More fromOkta

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp