Create Simple Chips In Angular Without Material Design

In this post, we will create a simple chips feature in Angular 8 application without material design. We will use simple html elements, css and typescript code to implement chips design.

Introduction


We can create chips in Angular using Material design very easily. There are lots of sample codes available in this context. I was thinking about creating the chips feature without using Material design so, that we can avoid the extra library and dependency of Material. I have achieved this goal with some simple steps and here, I am demonstrating the entire process.
 
Below is the screenshot of my working application.
 
 
 
To showcase the chips feature, we will create an Angular 8 application with default app component, and we will add two input boxes for capturing username and technical skills set of that user. After entering each skill set, user can hit enter key to create a skillset chip. We will use very simple logic for this. Though, this is a very simple demonstration, we have handled keypress events and curser focus to various HTML controls through “ViewChild” and “nativeElement.focus” method. We can see all these actions step by step.
 

Create a new Angular 8 application using CLI


If you have not installed Angular CLI, please install using npm command. (Please make sure, you could install latest node.js in your machine.)
 
npm i -g @angular/cli
 
After installing the CLI, we can create new Angular application using below command.
 
ng new AngularChips
 
After few minutes, entire node modules will be created successfully. Now we can add below packages to the project.
 
cd AngularChips (relocate to new project folder)
 
npm i bootstrap
npm i font-awesome
 
We must import both these libraries in styles.css file. So, that we can access these libraries globally in entire application, without further reference.
 
styles.css
  1. @import "~bootstrap/dist/css/bootstrap.css";      
  2. @import "~font-awesome/css/font-awesome.css";   
We are capturing the username and skills set in this application. Hence, we must create an interface for User data.
 
We can add required properties to this interface.
 
ng g i User
 
user.ts
  1. export interface User {  
  2.     userName: string;  
  3.     skillsSets: string[];  
  4. }  
We are using template driven forms to capture user data from screen. Hence, we must imports “FormsModule” in app.module.ts file.
 
app.module.ts
  1. import { BrowserModule } from '@angular/platform-browser';  
  2. import { NgModule } from '@angular/core';  
  3. import { FormsModule } from '@angular/forms';  
  4.   
  5. import { AppComponent } from './app.component';  
  6.   
  7. @NgModule({  
  8.   declarations: [  
  9.     AppComponent  
  10.   ],  
  11.   imports: [  
  12.     BrowserModule,  
  13.     FormsModule,  
  14.   ],  
  15.   providers: [],  
  16.   bootstrap: [AppComponent]  
  17. })  
  18. export class AppModule { }  
We can add required html items in app component template file.
 
app.component.html
  1. <div style="margin: 10px;" class="row">  
  2.   <div class="col-md-6">  
  3.       <div class="row">  
  4.           <button class="btn btn-success mr-2" style="width:120px;" type="submit" (click)="newOrCancel()">  
  5.             New User  
  6.           </button>  
  7.         </div>  
  8.     <div class="form-group row mb-2">  
  9.       <label class="col-md-4 col-form-label" for="userName">User Name</label>  
  10.       <div class="col-md-8">  
  11.         <input class="form-control" type="text" id="userName" placeholder="User Name" [(ngModel)]="userName"   
  12.         [disabled]="!isInsert" autofocus (keydown.enter)="skillsSetFocus()" #userNameRef/>  
  13.       </div>  
  14.     </div>  
  15.     <div class="form-group row mb-2">  
  16.       <label class="col-md-4 col-form-label" for="skillsSet">Skills Sets</label>  
  17.       <div class="col-md-8">  
  18.         <input class="form-control" type="text" id="skillsSet" placeholder="Enter each Skills Set and hit 'Enter' Key."   
  19.         [(ngModel)]="skillsSet" (keydown.enter)="onSkillsSetKeydown()" #skillsSetRef/>  
  20.       </div>  
  21.     </div>  
  22.     <div class="row mb-2">  
  23.       <div class="col-md-4"></div>  
  24.       <div class="col-md-8">  
  25.         <div *ngFor="let skill of skills; let i = index" class="roundedcorner">  
  26.           {{skill}}   
  27.           <i class="fa fa-times-circle" style="font-size:24px;color:red;" (click)="dropSkill(i)"></i>  
  28.         </div>  
  29.       </div>  
  30.     </div>  
  31.     <div class="offset-md-4">  
  32.       <button class="btn btn-primary mr-2" style="width:80px;" type="submit" [disabled]="!isInsert || skills.length==0 || userName==null || userName==''" (click)="saveUser()">  
  33.         Save  
  34.       </button>  
  35.       <button class="btn btn-warning mr-2" style="width:80px;" type="submit" (click)="newOrCancel()">  
  36.           Cancel  
  37.         </button>  
  38.     </div>  
  39.   </div>  
  40.   <div class="col-md-3">  
  41.     <div class="card">  
  42.       <div class="card-header">  
  43.         Users List  
  44.       </div>  
  45.       <div class="card-body">  
  46.         <div *ngFor="let user of users">  
  47.           <a href="#" (click)="selectUser(user)">{{user.userName}}</a>  
  48.         </div>  
  49.       </div>  
  50.     </div>  
  51.   </div>  
  52. </div>  
We have used very simple logic to create chips for skills set. We will store these skills in a “skills” variable and using “ngFor” we have populated in div element.
  1.      <div class="col-md-8">  
  2.         <div *ngFor="let skill of skills; let i = index" class="roundedcorner">  
  3.           {{skill}}   
  4.           <i class="fa fa-times-circle" style="font-size:24px;color:red;" (click)="dropSkill(i)"></i>  
  5.         </div>  
  6.       </div>  
After saving the data, each user information (username and skills set array) will be added to another array variable “users” and populated in another div element in the right side of the application.
 
We can add business logic in the app component file.
 
app.component.ts
  1. import { Component, OnInit, ViewChild, ElementRef, HostListener } from '@angular/core';  
  2. import { User } from './user';  
  3.   
  4. @Component({  
  5.   selector: 'app-root',  
  6.   templateUrl: './app.component.html',  
  7.   styleUrls: ['./app.component.css']  
  8. })  
  9. export class AppComponent implements OnInit {  
  10.   @ViewChild('userNameRef', { staticfalse }) userNameRefElement: ElementRef;  
  11.   @ViewChild('skillsSetRef', { staticfalse }) skillsSetRefElement: ElementRef;  
  12.   
  13.   skillsSet: any;  
  14.   skills: string[] = [];  
  15.   users: User[] = [];  
  16.   userName: any;  
  17.   isInsert: boolean;  
  18.   
  19.   onSkillsSetKeydown() {  
  20.     if (this.skillsSet == "" || this.skillsSet == nullreturn;  
  21.     this.skills.push(this.skillsSet);  
  22.     this.skillsSet = "";  
  23.   }  
  24.   
  25.   dropSkill(index: any) {  
  26.     this.skills.splice(index, 1);  
  27.   }  
  28.   
  29.   ngOnInit() {  
  30.     this.isInsert = true;  
  31.   }  
  32.   
  33.   selectUser(user: User) {  
  34.     this.isInsert = false;  
  35.     this.userName = user.userName;  
  36.     this.skills = user.skillsSets;  
  37.     this.skillsSet = "";  
  38.   }  
  39.   
  40.   newOrCancel() {  
  41.     this.isInsert = true;  
  42.     this.userName = "";  
  43.     this.skillsSet = "";  
  44.     this.skills = [];  
  45.     this.userNameRefElement.nativeElement.focus();  
  46.   }  
  47.   
  48.   saveUser() {  
  49.     var user: User = {  
  50.       userName: "",  
  51.       skillsSets: []  
  52.     };  
  53.     user.userName = this.userName;  
  54.     user.skillsSets = this.skills;  
  55.     this.users.push(user);  
  56.     this.newOrCancel();  
  57.   }  
  58.   
  59.   skillsSetFocus() {  
  60.     this.skillsSetRefElement.nativeElement.focus();  
  61.   }  
  62.   
  63.   @HostListener('window:keydown', ['$event'])  
  64.   onKeyPress($event: KeyboardEvent) {  
  65.     if (($event.ctrlKey || $event.metaKey) && $event.keyCode == 83) {  
  66.       $event.preventDefault();  
  67.       if (this.skills.length > 0 && this.userName != null && this.userName != '' && this.isInsert == true) {  
  68.         this.saveUser();  
  69.       }  
  70.     }  
  71.   
  72.   }  
  73. }  
We have added a method “onSkillsSetKeydown” to add skill to “skills” variable.
  1. onSkillsSetKeydown() {  
  2.     if (this.skillsSet == "" || this.skillsSet == nullreturn;  
  3.     this.skills.push(this.skillsSet);  
  4.     this.skillsSet = "";  
  5.   }  
We have added two “ViewChild” variables to create references for two input elements username and skills set.
 
This ViewChild variables will be used to set cursor focus to respective elements in run time.
  1. skillsSetFocus() {  
  2.     this.skillsSetRefElement.nativeElement.focus();  
  3.   }  
In addition to chips feature, we have created a “HostListener” to listen key actions. We can press “Ctrl + S” key combinations to save the user data. We have handled these actions in code.
  1. @HostListener('window:keydown', ['$event'])  
  2.   onKeyPress($event: KeyboardEvent) {  
  3.     if (($event.ctrlKey || $event.metaKey) && $event.keyCode == 83) {  
  4.       $event.preventDefault();  
  5.       if (this.skills.length > 0 && this.userName != null && this.userName != '' && this.isInsert == true) {  
  6.         this.saveUser();  
  7.       }  
  8.     }  
  9.   }  
We must add a small style class to handle the chips behaviour in app component style file.
 
app.component.css
  1. .roundedcorner {  
  2.     margin5px;  
  3.     padding5px;  
  4.     floatleft;  
  5.     border-radius: 25px;  
  6.     border2px solid #73AD21;  
  7. }  
We have completed entire coding. We can run the application now.
 
 
 
 
 
We can add multiple skills for a user and display as chips. We can add “n” number of users and their skills set and it will be added to the memory list. Please note, this will not persist after page refresh as we have stored it as in-memory.
 
 
 

Conclusion


In this post, we have created an Angular 8 application using CLI command and then we added an attractive chips feature with help of simple typescript and few HTML elements. We have also used HostListener to get key events and using that, we have saved the user data with simple “Ctrl + S” command. Hope, you have understood clearly about the chips feature in Angular without material design. Please feel free to give your comments and feedback.