1. Installing Carbon

Starting with the Carbon Angular, there are two ways to begin working with Carbon components. By the end, these two methods will produce the same result.

Preview

A preview of what you will build:

Prerequisites

Fork, clone and branch

This tutorial has an accompanying GitHub repository called carbon-tutorial-angular that we’ll use as a starting point for each step.

Fork

To begin, fork carbon-tutorial-angular using your GitHub account.

Clone

Go to your forked repository, copy the SSH or HTTPS URL and in your terminal run the two commands to get the repository in your local file system and enter that directory.

git clone [your fork SSH/HTTPS]
cd carbon-tutorial-angular

Add upstream remote

Add a remote called upstream so we can eventually submit a pull request once you have completed this tutorial step.

SSH:
git remote add upstream git@github.com:carbon-design-system/carbon-tutorial-angular.git

Or, if you prefer to use HTTPS instead of SSH with your remotes:

HTTPS:
git remote add upstream https://github.com/carbon-design-system/carbon-tutorial-angular.git

Verify that your forked repository remotes are correct:

git remote -v

Your terminal should output something like this:

origin [your forked repo] (fetch)
origin [your forked repo] (push)
upstream git@github.com:carbon-design-system/carbon-tutorial-angular.git (fetch)
upstream git@github.com:carbon-design-system/carbon-tutorial-angular.git (push)

Branch

Now that we have our repository set up, let’s check out the branch for this tutorial step’s starting point. Run the two commands:

git fetch upstream
git checkout -b angular-step-1 upstream/angular-step-1

Add starter remote

Add a temporary remote called starter so we can clone the carbon-angular-starter.

SSH:
git remote add starter git@github.com:carbon-design-system/carbon-angular-starter.git

Or, if you prefer to use HTTPS instead of SSH with your remotes:

HTTPS:
git remote add starter https://github.com/carbon-design-system/carbon-angular-starter.git

Pull starter code

Now that we a have starter remote, we can rebase our branch to match whatever is in the starter app. Run the two commands:

git fetch starter
git pull --allow-unrelated-histories starter master

Remove starter remote

Remove the temporary remote called starter since we have no use for it anymore.

git remote remove starter

Build and start

We have the repository forked to your GitHub account, cloned down to your machine, and the starting branch checked out. Next, install the Carbon app’s dependencies with:

npm install

After the dependencies are installed, you can start the app with:

npm run start

Your app should now be running with the message: ** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

That’s it! Your browser should now resemble the preview above. You can skip create your own and jump straight to submit pull request

Create your own

Install Angular CLI

Since we are starting from scratch, we need to first install Angular CLI

npm install -g @angular/cli

Create an Angular App

Now that we have our environment set up, starting a new Angular app is easy! We will be using the Angular CLI to create and generate our components. It can also generate services, router, components, and directives.

To create a new Angular project with Angular CLI, just run:

ng new carbon-angular-tutorial --directory ./

When you get prompted, enter the following.

? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS

This command will install the Angular app with all the configurations needed. Currently, the src directory should have the structure:

src
├── app
│   ├── app-routing.module.ts
│   ├── app.component.html
│   ├── app.component.scss
│   ├── app.component.spec.ts
│   ├── app.component.ts
│   └── app.module.ts
├── assets
├── environments
│   ├── environment.prod.ts
│   └── environment.ts
├── favicon.ico
├── index.html
├── main.ts
├── polyfills.ts
├── styles.scss
└── test.ts

Install Carbon

Even though we installed some dependencies while creating the new app, we’ve yet to install the Carbon packages.

  • carbon-components - component styles
  • carbon-components-angular - Angular components
  • @carbon/icons-angular - Angular icons
npm install carbon-components carbon-components-angular @carbon/icons-angular

Import carbon-component styles

In styles/styles.scss, import the Carbon styles by adding the following to the top of the file:

src/styles.scss
@import '~carbon-components/scss/globals/scss/styles';

Run the app

Now we can run our app for a quick preview inside the browser.

npm run start

Your app should now be running with the message: ** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **

Before we start adding components we want to start with an empty project, so delete everything in app.component.html except for the router-outler. We will also have to delete the test that was associated with this code. So in app.component.spec.ts, delete the should render title test.

Creating Pages

Next, we’re going to create an Angular component called StarterHome using Angular CLI. We will also need a module and routing files.

ng g module starter-home --routing
ng g component starter-home/starter-home

You should now have the following folder structure:

src/app/starter-home
├── starter-home
│   ├── starter-home.component.html
│   ├── starter-home.component.scss
│   ├── starter-home.component.spec.ts
│   └── starter-home.component.ts
├── starter-home-routing.module.ts
└── starter-home.module.ts

Code style

Next, we will edit the code style to match Carbon angular recommendations. Through the conventions listed in our tslint.js, the app will look clean and easier to read and maintain. So replace your code in tslint.json with the code below.

tslint.json
{
"rulesDirectory": [
"node_modules/codelyzer"
],
"rules": {
"callable-types": true,
"class-name": true,
"comment-format": [
true,
"check-space"
],
"curly": true,
"eofline": true,
"forin": true,
"import-blacklist": [true],
"import-spacing": true,
"indent": [
true,
"tabs",
2
],
"interface-over-type-literal": true,
"label-position": true,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"public-static-field",
"protected-static-field",
"private-static-field",
"public-static-method",
"protected-static-method",
"private-static-method",
"public-instance-field",
"protected-instance-field",
"private-instance-field",
"public-constructor",
"protected-constructor",
"private-constructor",
"public-instance-method",
"protected-instance-method",
"private-instance-method"
]
}
],
"no-arg": true,
"no-bitwise": true,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-construct": true,
"no-debugger": true,
"no-duplicate-variable": true,
"no-empty": false,
"no-empty-interface": true,
"no-eval": true,
"no-inferrable-types": [true, "ignore-params"],
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [
true,
"check-open-brace",
"check-catch",
"check-else",
"check-whitespace"
],
"prefer-const": true,
"quotemark": [
true,
"double"
],
"radix": true,
"semicolon": [
true,
"always"
],
"triple-equals": [
true,
"allow-null-check",
"allow-undefined-check"
],
"typedef-whitespace": [
true,
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}
],
"typeof-compare": true,
"unified-signatures": true,
"variable-name": [
true,
"ban-keywords",
"check-format",
"allow-leading-underscore"
],
"whitespace": [
true,
"check-branch",
"check-decl",
"check-operator",
"check-module",
"check-separator",
"check-rest-spread",
"check-type",
"check-preblock"
],
"directive-selector": [true, "attribute", ["app", "test"], "camelCase"],
"component-selector": [true, "element", ["app", "test"], "kebab-case"],
"no-inputs-metadata-property": true,
"no-outputs-metadata-property": true,
"no-host-metadata-property": true,
"no-attribute-decorator": true,
"no-input-rename": true,
"no-output-rename": true,
"no-forward-ref": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"templates-use-public": true,
"invoke-injectable": true
}
}

Now that our guidelines are defined, we need to apply them to our app.

ng lint --fix

All files should now pass linting. Finally, to help the editor indent lines properly in our project and to keep code styles consistent, we need to edit .editorconfig.

.editorconfig
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = tab
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

Note: This only works with editors that support editorconfigs

Adding starter content

The StarterHome component will hold the content of the starter app. We will add the grid, list, tabs and tiles Carbon components. Now, let’s import these Carbon components from our carbon-components-angular package library.

src/app/starter-home/starter-home.module.ts
import {
GridModule,
ListModule,
TabsModule,
TilesModule,
} from 'carbon-components-angular';
imports: [
CommonModule,
StarterHomeRoutingModule,
GridModule,
ListModule,
TabsModule,
TilesModule
],
declarations: [StarterHomeComponent]

Note: Make sure to add these imports to starter-home.component.spec.ts to ensure our tests pass.

Next, let’s add some markup and styling to match the starter app.

src/app/starter-home/starter-home/starter-home.component.html
<div class="starter-home" ibmGrid>
<h1 class="title">Congratulations, <br />you made it!</h1>
<p style="margin-top: 2rem;">
You have offically built your first Carbon Angular app
</p>
<div class="body-text" ibmRow>
<div ibmCol [columnNumbers]="{'lg': 6, 'md': 12, 'sm': 12}">
<p>
This app is to get you quickly started with our Carbon Angular
libraries. It currently includes the grid, list, tab and tile
components. Feel free to add or remove any components you desire. To
help you get started we've also set up:
</p>
</div>
<div ibmCol [columnNumbers]="{'lg': 6, 'md': 12, 'sm': 12}">
<ul ibmList class="list">
<li ibmListItem>Angular-cli</li>
<li ibmListItem>Build Process</li>
<li ibmListItem>Code styles <br />& editor configs</li>
<li ibmListItem>Folder structure</li>
<li ibmListItem>Lazy loading</li>
<li ibmListItem>Routing</li>
<li ibmListItem>Service workers</li>
<li ibmListItem>Test framework</li>
</ul>
</div>
</div>
<ibm-tabs isNavigation="false">
<ibm-tab heading="carbon-components-angular">
<p>Description</p>
<br />
<p style="margin-bottom: 2rem;">
A component library that gives developers a collection of re-usable
angular components that they can use for building websites and user
interfaces
</p>
<p>Other useful resources</p>
<br />
<div ibmRow>
<div ibmCol [columnNumbers]="{'md': 2, 'sm': 12}">
<ibm-clickable-tile
href="https://github.com/IBM/carbon-components-angular"
target="_blank"
>Repository</ibm-clickable-tile
>
</div>
<div ibmCol [columnNumbers]="{'md': 2, 'sm': 12}">
<ibm-clickable-tile
href="https://angular.carbondesignsystem.com/"
target="_blank"
>Component Library</ibm-clickable-tile
>
</div>
<div ibmCol [columnNumbers]="{'md': 2, 'sm': 12}">
<ibm-clickable-tile
href="https://angular.carbondesignsystem.com/documentation/"
target="_blank"
>Documentation</ibm-clickable-tile
>
</div>
<div ibmCol [columnNumbers]="{'md': 2, 'sm': 12}">
<ibm-clickable-tile
href="https://www.npmjs.com/package/carbon-components-angular"
target="_blank"
>NPM</ibm-clickable-tile
>
</div>
</div>
</ibm-tab>
<ibm-tab heading="carbon-charts">
<p>Description</p>
<br />
<p style="margin-bottom: 2rem;">
Carbon Charts is an open-source JavaScript data visualization library
that will be used as a functional component of IBM's Carbon Design
System.
</p>
<p>Other useful resources</p>
<br />
<div ibmRow>
<div ibmCol [columnNumbers]="{'md': 2, 'sm': 12}">
<ibm-clickable-tile
href="https://github.com/IBM/carbon-charts"
target="_blank"
>Repository</ibm-clickable-tile
>
</div>
<div ibmCol [columnNumbers]="{'md': 2, 'sm': 12}">
<ibm-clickable-tile
href="https://carbon-design-system.github.io/carbon-charts/"
target="_blank"
>Demo</ibm-clickable-tile
>
</div>
<div ibmCol [columnNumbers]="{'md': 2, 'sm': 12}">
<ibm-clickable-tile
href="https://carbon-design-system.github.io/carbon-charts/documentation/"
target="_blank"
>Documentation</ibm-clickable-tile
>
</div>
<div ibmCol [columnNumbers]="{'md': 2, 'sm': 12}">
<ibm-clickable-tile
href="https://www.npmjs.com/package/@carbon/charts"
target="_blank"
>NPM</ibm-clickable-tile
>
</div>
</div>
</ibm-tab>
</ibm-tabs>
</div>
src/app/starter-home/starter-home/starter-home.component.scss
.starter-home {
.title {
font-size: 4rem;
font-weight: 400;
}
.body-text {
margin-top: 2rem;
margin-bottom: 4rem;
}
.list {
max-width: 500px;
padding-left: 50px;
column-count: 2;
@media only screen and (max-width: 1055px) {
margin-top: 3rem;
}
li {
font-weight: bold;
}
}
}

We will also add some styling in src/styles.scss for resets, font styles and the different themes that you can play around with.

src/styles.scss
$feature-flags: (
css--body: false,
// we're providing our own body styles
css--reset: false // prevent thousands of resets being included...,,
);
body {
// To change the theme, uncomment the below import and whichever carbon theme you want to use
// @import "~carbon-components/scss/globals/scss/vendor/@carbon/elements/scss/themes/theme-maps";
// $carbon--theme: $carbon--theme--g100;
// $carbon--theme: $carbon--theme--g90;
// $carbon--theme: $carbon--theme--g10;
@import '~carbon-components/scss/globals/scss/styles';
color: $text-01;
background-color: $ui-background;
@import '~carbon-components/scss/globals/scss/css--reset';
@include reset;
position: absolute;
top: 0;
left: 0;
margin: 0;
min-width: 100vw;
min-height: 100vh;
font-family: 'IBM Plex Sans', Arial, sans-serif;
p {
@include type-style('expressive-paragraph-01');
max-width: 800px;
}
li {
@include type-style('body-long-02');
}
}

Add routing

Currently, we you will not be able to see anything in the browser since our routing is not set up yet. Angular CLI has already set up routing in app-routing.module.ts and starter-home-routing.module.ts, we just need to add the routes:

src/app/app-routing.module.ts
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./starter-home/starter-home.module').then(
m => m.StarterHomeModule
),
},
{
path: '',
redirectTo: '',
pathMatch: 'full',
},
];
src/starter-home/starter-home-routing.module.ts
import { StarterHomeComponent } from './starter-home/starter-home.component';
const routes: Routes = [
{
path: '',
component: StarterHomeComponent,
},
];

Progressive web app

Before we submit a pull request, let’s explore progressive web app, or PWA. PWA is a web application that has a set of capabilities (similar to native apps) which provide an app-like experience to users. Some other capabilities include full responsiveness and browser compatibility, connectivity independence and self-updates. In the src directory create a file called sw.js and paste the code below.

For more information on PWAs, check out the developer’s documentation.

src/sw.js
let log = console.log.bind(console);
let err = console.error.bind(console);
let version = '1';
let cacheName = 'pwa-client-v' + version;
let dataCacheName = 'pwa-client-data-v' + version;
let appShellFilesToCache = [
'./',
'./index.html',
// "./inline.bundle.js",
// "./polyfills.bundle.js",
// "./styles.bundle.js",
// "./vendor.bundle.js",
// "./main.bundle.js",
'./manifest.json',
];
self.addEventListener('install', ev => {
ev.waitUntil(self.skipWaiting());
log('Service Worker: Installed');
ev.waitUntil(
caches.open(cacheName).then(cache => {
log('Service Worker: Caching App Shell');
return cache.addAll(appShellFilesToCache);
})
);
});
self.addEventListener('activate', ev => {
ev.waitUntil(self.clients.claim());
log('Service Worker: Active');
ev.waitUntil(
caches.keys().then(keyList => {
return Promise.all(
keyList.map(key => {
if (key !== cacheName) {
log('Service Worker: Removing old cache', key);
return caches.delete(key);
}
})
);
})
);
});
self.addEventListener('fetch', e => {
log('Service Worker: Fetch URL ', e.request.url);
// Match requests for data and handle them separately
e.respondWith(
caches.match(e.request.clone()).then(response => {
return (
response ||
fetch(e.request.clone()).then(r2 => {
return caches.open(dataCacheName).then(cache => {
console.log('Service Worker: Fetched & Cached URL ', e.request.url);
cache.put(e.request.url, r2.clone());
return r2.clone();
});
})
);
})
);
});

Now we need to register our service worker and use it to cache files locally. In index.html add the below code in a script block using <script></script> under the app-root tag.

src/index.html
// Uncomment the following lines to activate the service worker // if
(navigator.serviceWorker) { // navigator.serviceWorker.register("sw.js").then(()
=> { // console.log("Service worker installed") // }, err => { //
console.error("Service worker error:", err); // }); // }

Next, we need to add the web app manifest. The manifest is a file in json format that provides the name, description, icon locations and other information required for an app to be considered a PWA. It lets users install the web app on the home screen just like native apps without going through an app store. Create a manifest.json in the src directory the paste the following code.

src/manifest.json
{
"name": "Carbon Angular Starter",
"short_name": "Starter",
"icons": [
{
"src": "/assets/icons/favicon-32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/assets/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/icons/icon-144x144.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/icons/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/assets/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"background_color": "#ff00ff",
"theme_color": "#ffff00",
"display": "standalone",
"start_url": "/"
}

We will also need to add sw.js and manifest.json into our asset entities (build & test) in angular.json and reference the manifest.json and theme-color in the index.html <head> tags. The theme color tells the browser with what color to tint UI elements such as the address bar.

angular.json
"assets": [
"src/favicon.ico",
"src/assets",
"src/sw.js",
"src/manifest.json"
],
src/index.html
<link rel="manifest" href="manifest.json" />
<meta name="theme-color" content="#ffff00" />

Finally, let’s download the different sizes of icons to the src/assests/icons directory by running the following commands:

mkdir src/assets/icons
curl -o src/assets/icons/android-chrome-192x192.png https://raw.githubusercontent.com/carbon-design-system/carbon-angular-starter/master/src/assets/icons/android-chrome-192x192.png
curl -o src/assets/icons/icon-144x144.png https://raw.githubusercontent.com/carbon-design-system/carbon-angular-starter/master/src/assets/icons/icon-144x144.png
curl -o src/assets/icons/icon-192x192.png https://raw.githubusercontent.com/carbon-design-system/carbon-angular-starter/master/src/assets/icons/icon-192x192.png
curl -o src/assets/icons/icon-256x256.png https://raw.githubusercontent.com/carbon-design-system/carbon-angular-starter/master/src/assets/icons/icon-256x256.png
curl -o src/assets/icons/icon-384x384.png https://raw.githubusercontent.com/carbon-design-system/carbon-angular-starter/master/src/assets/icons/icon-384x384.png
curl -o src/assets/icons/icon-512x512.png https://raw.githubusercontent.com/carbon-design-system/carbon-angular-starter/master/src/assets/icons/icon-512x512.png

Submit pull request

We’re going to submit a pull request to verify completion of this tutorial step and demonstrate a couple related concepts.

Continuous integration (CI) check

Note: Before you run any tests, make sure that you are using ChromeHeadless in karma.conf.js instead of Chrome.

We have lint and test scripts defined in package.json that verify file formatting for files that have been touched since the last Git commit. You’d typically also have that script run your test suite as part of your CI build. Go ahead and make sure everything looks good with:

npm run lint && npm test

Note: If this gives an error, it’s likely that some of your source files are not properly formatted.

Git commit and push

Before we can create a pull request, we need to stage and commit all of our changes:

git add --all && git commit -m "feat(tutorial): complete step 1"

Then, push to your repository:

git push origin angular-step-1

Note: If your Git remote protocol is HTTPS instead of SSH, you may be prompted to authenticate with GitHub when you push changes. If your GitHub account has two-factor authentication enabled, we recommend that you follow these instructions to create a personal access token for the command line. That lets you use your token instead of password when performing Git operations over HTTPS.

Note: If you receive a non-fast-forward error, it’s likely that your forked repository is behind the original repository and needs updated. This can happen if the tutorial was updated after you began working on it. To fix, run git pull upstream angular-step-1 to merge the changes into your branch, then you can try pushing again. Or, you can manually merge in the upstream changes.

Pull request (PR)

Finally, visit carbon-tutorial-angular to “Compare & pull request”. In doing so, make sure that you are comparing to angular-step-1 into base: angular-step-1. Take notice of the Netlify bot that deploys a preview of your PR every time that you push new commits. These previews can be shared and viewed by anybody to assist the PR review process.

Note: Expect your tutorial step PRs to be reviewed by the Carbon team but not merged. We’ll close your PR so we can keep the repository’s remote branches pristine and ready for the next person!