Building Slack in Angular with Covalent
Simple steps to prototype an chat application in Angular Material
What we’re building
We will quickly build a responsive layout with all the main UI elements and interactions of the Slack desktop app using Angular components from Angular Material and Covalent. Using atomic design we will build this layout all with existing UI elements and zero custom SCSS.
Check out the final prototype in plunker
What’s Covalent?
At Teradata we created Covalent to fill the need for a rapid prototyping tool to serve a global organization and quickly flip mock applications to enterprise products.
Change the app color theme
Changing the entire application color scheme is simple with Angular Material’s theme. Edit /src/theme.scss and change the $primary and $accent colors (referring to the material color palette) to the class purple & teal Slack theme. (need more info? check out my theme tutorial)
$primary: mat-palette($mat-purple, 700);
$accent: mat-palette($mat-teal, A700, A100, A400);
Choosing a layout
Although Angular Material comes with a sidenav and toolbar, there is no premade layouts. Covalent has us covered with 4 responsive layouts following the Material Design spec.
Nav List
The nav-list layout features a persistent left nav that will be perfect for Slack channels.
<td-layout #layout>
<td-navigation-drawer sidenavTitle="Angular Slack" name="Firstname Lastname" email="firstname.lastname@company.com">
</td-navigation-drawer>
<td-layout-nav-list>
content
</td-layout-nav-list>
</td-layout>
Here we have 3 components:
- td-layout
- td-navigation-drawer
- td-layout-nav-list
Toolbar content
Next let’s override the top sticky toolbars in the left nav and main content area to mimic Slack’s toolbars.
While we could use toolbarTitle=”Team Name” for a toolbar title, we want to mimic Slack’s team & notification options. For that we’ll use an override with a md-menu dropdown and an arrow_drop_down md-icon. Notice we’re using a flexbox layout as well.
<td-layout-nav-list>
<div td-sidenav-toolbar-content layout="row" flex>
<div>
<button md-button [mdMenuTriggerFor]="menu">Team Name <md-icon>arrow_drop_down</md-icon></button>
<md-menu #menu="mdMenu">
<button md-menu-item>Set a status</button>
...
</md-menu>
</div>
</div>
...
</td-layout-nav-list>
Notification settings menu
Now we’ll add another md-menu to the right w/ a notification icon & dropdown. Notice we’re using a md-icon-button instead of md-button and on the md-menu we added a x-position=”before” so the menu opens to the left.
<button md-icon-button [mdMenuTriggerFor]="notifyMenu"><md-icon>notifications_off</md-icon></button><md-menu #notifyMenu="mdMenu" x-position="before">
<button md-menu-item>Do not disturb ON</button>
...
<md-divider></md-divider>
<button md-menu-item>Settings for #channel</button>
<button md-menu-item>Notifications preferences...</button>
</md-menu>
Channel & DM Navigation
The channel & user left nav is populated using md-nav-list with md-list-item and md-list-icon. Notice the user of ng-template w/ an array for easy prototyping:
<md-nav-list td-sidenav-content>
<a md-list-item><md-icon md-list-icon>sort</md-icon> All Unreads</a
<a md-list-item><md-icon md-list-icon>chat</md-icon> All Threads</a
<md-divider></md-divider>
<h3 md-subheader>STARRED</h3>
<ng-template let-item let-i="index" let-last="last" ngFor [ngForOf]="[0,1]">
<a md-list-item><md-icon md-list-icon>#</md-icon> starred-channel-{{i}}</a>
</ng-template>
<md-divider></md-divider>
<h3 md-subheader>CHANNELS (5)</h3>
<ng-template let-item let-i="index" let-last="last" ngFor [ngForOf]="[0,1,2,3]">
<a md-list-item><md-icon md-list-icon>#</md-icon> channel-name-{{i}}</a>
</ng-template>
<md-divider></md-divider>
<h3 md-subheader>DIRECT MESSAGES</h3>
<ng-template let-item let-i="index" let-last="last" ngFor [ngForOf]="[0,1,2,3,4,5]">
<a md-list-item><md-icon color="accent" md-list-icon>lens</md-icon> username{{i}}</a>
</ng-template>
</md-nav-list>
Toolbar
For the main toolbar we’re going to use a combo of Angular-Material and Covalent components with a slew of utility classes included with Covalent.
- md-menu, md-icon, md-icon-button, mdTooltip
- td-search-box
- typography classes and margin (push) utility styles
<div td-toolbar-content layout="row" layout-align="start center" flex><div>
<div [mdMenuTriggerFor]="channelMenu" class="cursor-hover">#channel</div>
<md-menu #channelMenu="mdMenu">
...
</md-menu> <div layout="row" layout-align="start center">
<md-icon class="md-caption">star</md-icon>
<md-icon class="md-caption push-left-sm">person</md-icon>
<span class="md-caption">94</span>
<md-icon class="md-caption push-left-sm">note</md-icon>
<span class="md-caption">12</span>
<span class="md-caption push-left-sm">channel description</span>
</div>
</div> <span flex></span> <td-search-box placeholder="Search" [showUnderline]="false"></td-search-box> <button md-icon-button mdTooltip="Call"><md-icon>phone</md-icon></button>
<button md-icon-button mdTooltip="Conversation settings"><md-icon>settings</md-icon></button>
<button md-icon-button mdTooltip="Show conversation details"><md-icon>chrome_reader_mode</md-icon></button>
<button md-icon-button mdTooltip="Show activity"><md-icon>@</md-icon></button>
<button md-icon-button mdTooltip="Show starred items"><md-icon>star_border</md-icon></button>
<button md-icon-button mdTooltip="More items"><md-icon>more_vert</md-icon></button></div>
Chat Content
Now onto the main content area! We’ll use a md-card to hold a md-list and repeat some md-list-items with avatars and dummy text. Notice how we use the Covalent media query to add some margin to the md-card on browsers greater than xtra-small.
<md-card tdMediaToggle="gt-xs" [mediaClasses]="['push-sm']"> <md-list>
<ng-template let-item let-i="index" let-last="last" ngFor [ngForOf]="[0,1,2,3,4,5,6,7,8,9]">
<md-list-item>
<img md-list-avatar src="http://lorempixel.com/40/40/people/{{i}}" />
<h3 md-line class="cursor-pointer"> Firstname Lastname </h3>
<p md-line>
<span class="text-wrap">
Farm-to-table poke distillery...
</span>
</p>
</md-list-item>
<md-divider *ngIf="!last" md-inset></md-divider>
</ng-template>
</md-list></md-card>
Message bar
Now for the message bar! We’ll use a fun blend of items to achieve a quasi-realistic Slack messaging toolbar, ranging from a textarea w/ mdInput to a td-menu component wrapper for md-menu.
<td-layout-footer-inner>
<div layout="row" layout-align="start center">
<div> <button md-icon-button mdTooltip="Add content" mdTooltipPosition="after" [mdMenuTriggerFor]="chatMenu"><md-icon>add</md-icon></button> <md-menu #chatMenu="mdMenu">
<button md-menu-item>Code or text snippet</button>
...
</md-menu>
</div> <md-input-container class="push-left push-right" flex layout="row" floatPlaceholder="never">
<textarea flex mdInput placeholder="message"></textarea>
</md-input-container> <div> <button md-icon-button mdTooltip="Add emoji" mdTooltipPosition="before" [mdMenuTriggerFor]="emojiMenu"><md-icon>sentiment_very_satisfied</md-icon></button> <md-menu #emojiMenu="mdMenu" x-position="before">
<td-menu>
<div td-menu-header>
<md-button-toggle><md-icon>access_time</md-icon></md-button-toggle>
...
</div>
<div class="push">
<div layout="row" layout-wrap>
<button flex="20" md-icon-button><md-icon>mood</md-icon></button>
<button flex="20" md-icon-button><md-icon>mood_bad</md-icon></button>
...
</div>
</div>
<div td-menu-footer>
<div layout="row" layout-align="center center" class="push-sm">
<span class="md-subhead">Emoji Deluxe</span>
<span flex></span>
<span>:happy:</span>
<md-icon>sentiment_very_satisfied</md-icon>
</div>
</div>
</td-menu>
</md-menu> </div>
</div>
</td-layout-footer-inner>
Bonus: Add a right sidenav
In Slack you can toggle a right sidenav for channel info. We can easily achieve this with an Angular Material sidenav but wrapping our content area (toggle the sidenav with (click)=”channelInfo.toggle()”).
<md-sidenav-container fullscreen>
<md-sidenav #channelInfo mode="side" align="end">
sidenav content
</md-sidenav>
main content
</md-sidenav-container>
That was easy right?
With some powerful tools and a little bit of creativity, you’ve got yourself a material design Slack clone built on Angular v4 in a matter of minutes! Feel free to fork the code and have fun with Covalent!
Interested in a Part II?
Would you like to take this a step further and build into a functional chat app? Let us know in comments!