Javascript

Angular 2 and Typescript Conference Browser Application

September 6th, 2016 | By Jscrambler | 11 min read

Today, we present a tutorial to build a conference browsing application using Typescript and the Angular 2 CLI.

Angular 1 has been a phenomenal success in the Single-Page Applications (SPA) world.

This was in part due to its simplicity and easy-to-understand syntax. It was also aided by the fact that it is a Google-backed product, which instills some confidence in developers. After the team at Google decided to come up with version two of the popular framework, they decided to change things drastically.

Angular 2 takes a different approach to building single-page applications. Let this not stop you from diving into it because Angular 2 is geared towards using some of the best tools out there in the industry from some of the most brilliant companies.

For example, Angular 2 fully supports typescript as a first-class citizen. Even though typescript is not required to use the framework, I must admit that the framework feels more intuitive when used with typescript. In addition to that, you tend to write less code.

Also, many examples and blog posts are written in typescript, so that is a reason to stick to using it when just starting. Another adoption is the new angular-cli, based on the already brilliant ember-cli.

The command line helps with rapid development and shields you from having to make many small decisions upfront.

In light of that, we will use typescript and angular-cli to build a conference browsing application. The data for the conference will come from an Express.js backend application, which I have prepared upfront with some mocked data.

We will connect it using the Angular 2 HTTP module to the Express.js backend to get a list of speakers to browse them in detail and see their respective talks and times.

A simple application, but it will touch on many aspects of Angular 2, such as components, the router, component scoped styles, data-binding, helpers or pipes, and much more, so let’s dive in now and get started.

Bootstrap

Install angular-cli

First of all, make sure you have node.js and npm installed.

Head over to your terminal and install angular-cli using the following command:

npm install - g angular - cli@ latest


Now we should have a new ng command available to us. Just type ng and a whole bunch of commands should show up. If so, then you have successfully installed the tool.

Setup new angular conference application

As of this article the latest version of angular is 2.0.0-rc.4. Create a new application using the command:

ng new angular2 - conference

You should see that ng created a new project for you and installed the node dependencies.

The backend

Next, let’s set up our backend. Clone the basic setup. Install the dependencies using npm install, and start the backend server using the node server. You should now have a service running at localhost:3000.

Navigate to the URL localhost:3000/speakers. You should see a list of speakers' data in json format in your browser. If that is the case, our backend setup is done. Leave the service running in the background as it will be needed soon.

Application structure

Let’s go through the important parts of our newly bootstrapped application.

The folder we are concerned with in this article is the src folder as it will contain all our components, services, styles, templates, and typescript class files.

The angular-cli knows to look for files in there. Speaking of the cli, let's start the development server by navigating to our application folder (angular2-conference) and using the:

ng server


This should start a server that watches the files in our application and automatically refreshes the browser when there are any changes. If all goes well, you should see some text on your browser when you visit localhost:4200 which is the client’s default development URL.

In terms of our application, we will have three components. One is the main component which was automatically created for us when we bootstrapped a new application.

The second one is the speakers component, which will list out the speaker names along with how many talks they have.

The third one will be a speaker-detail-component which will show more details for a particular speaker and show his list of talks along with further details as well.

Now that we understand our app structure a bit more, let’s create our first component.

Speakers component

Creates the speakers-component using:

ng generate component speakers


This should create a folder called speakers in the src/app folder. It will have a bunch of files there but the ones to be concerned with are speakers.component.ts, speakers.component.css, and speakers.component.html. Let’s see how we can query for speakers from the backend and list them.

In the project root directory create a service folder for speakers using

mkdir src / app / speakers / services


Next, create the actual service class using

ng generate class speakers / services / speaker - service


In the new file speakers/services/speaker-service.ts put the following content:

import {
    Injectable
}
from '@angular/core';
import {
    Http, Response
}
from '@angular/http';
import {
    Speaker
}
from '../models/speaker';
import {
    Talk
}
from '../models/talk';

@
Injectable()
export class SpeakerService {
    speakers: Speaker[];
    talks: Talk[];
    constructor(private http: Http) {}

    public getAll() {
        return this.http.get("http://localhost:3000/speakers")
            .toPromise().then((response) => {
                return this.mapGetAll(response);
            });
    }

    public getOne(id) {
        return this.http.get("http://localhost:3000/speakers/" + id)
            .toPromise().then((response) => {
                return this.mapGetOne(response);
            });
    }

    public mapGetAll(resp) {
        let speakers = resp.json().speakers.map((speaker) => {
            let talks = this.prepareSpeakerTalks(speaker, 
                 resp.json().talks);
            return new Speaker(speaker.id, speaker.firstName, 
                      speaker.lastName, talks, speaker.summary);
        });

        return speakers;
    }

    public mapGetOne(resp) {
        let speaker = resp.json().speaker;
        let talks = this.prepareSpeakerTalks(speaker, resp.json().talks);

        speaker = new Speaker(speaker.id, speaker.firstName, 
                       speaker.lastName, talks, speaker.summary);

        return speaker;
    }

    public prepareSpeakerTalks(speaker, talks) {
        let speakerTalks = [];

        talks.forEach((talk) => {
            if (speaker.id === talk.speaker) {
                speakerTalks.push(new Talk(talk.id, talk.title, 
                  talk.date, talk.summary));
            }
        });

        return speakerTalks;
    }
}


We have two methods called getAll and getOne. The first one gets a list of speakers while the second retrieves a single speaker by id.

The other functions are helper functions for mapping the data.
Inspecting these two methods, we see that we are calling the toPromise function on the HTTP method return.

This is because, in Angular 2, HTTP calls return observables instead of promises so we need to make that call to convert them into promises. Observables are useful but using promises will do just fine in our application.

We are also using the Speaker and Talk models which we imported above but are yet to create. Let’s do that now.

Create the Speaker model using the following commands:

mkdir src / app / speakers / models


and

ng generate class speakers / models / speaker


Generate the talk model using:

ng generate class speakers / models / talk


In the speaker model class speakers/models/speaker.ts paste there:

export class Speaker {
    id: number;
    firstName: string;
    lastName: string;
    talks: number[];
    summary: string;

    constructor(id, firstName, lastName, talks, summary) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.talks = talks;
        this.summary = summary;
    }
}


and paste it into the talk model:

import {
    Speaker
}
from './speaker';
export class Talk {
    id: number;
    title: string;
    date: string;
    summary: string;

    constructor(id, title, date, summary) {
        this.id = id;
        this.title = title;
        this.date = date;
        this.summary = summary;
    }
}


These two models are simply plain typescript classes with constructors that accept properties.

Modify the speakers class file speakers.component.ts with the following content:

import {
    Component, OnInit
}
from '@angular/core';
import {
    Speaker
}
from './models/speaker';
import {
    SpeakerService
}
from './services/speaker-service';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';

@
Component({
    moduleId: module.id,
    selector: 'speakers',
    templateUrl: 'speakers.component.html',
    styleUrls: ['speakers.component.css']
})
export class SpeakersComponent implements OnInit {
    public speakers: Speaker[];
    constructor(private speakerService: SpeakerService) {}

    ngOnInit() {
        this.speakerService.getAll().then(speakers => {
            this.speakers = speakers
        });
    }

}


This calls the speaker service and gets the list of speakers that have been prepared as instances of speaker model classes and assigns it to a speaker property of the component.

The ngOnInit is a way of running code when the component has been initiated.

Modify the template file speakers.component.html with the following content:

<div>
    <h2>Speakers</h2>
    <div>
        <p *ngFor="let speaker of speakers">
         <a class="speaker">{{ speaker.firstName + " " 
                   + speaker.lastName }}</a>
         <span class="talks-counter">{{speaker.talks.length}} talk(s)
         </span>
        </p>
    </div>
</div>

This loops over the speaker's array and shows their firstName, lastName, and number of talks they have. We plan to convert these into links that we can click to see more detailed information about that particular speaker.

Finally, to use our speakers component, we need to tell the main app component about it and do some other setup work. Let’s make sure the HTTP service and speaker services are available throughout our application and easily injectable.

Do that by modifying main.ts so

import {
    bootstrap
}
from '@angular/platform-browser-dynamic';
import {
    enableProdMode
}
from '@angular/core';
import {
    AppComponent, environment
}
from './app/';
import {
    HTTP_PROVIDERS
}
from '@angular/http';
import {
    SpeakerService
}
from './app/speakers/services/speaker-service';

if (environment.production) {
    enableProdMode();
}

bootstrap(AppComponent, [
    HTTP_PROVIDERS, SpeakerService
]);


Now let’s tell the app component about our new component. Modify app.component.ts to

import {
    Component
}
from '@angular/core';
import {
    SpeakersComponent
}
from './speakers';

@
Component({
    moduleId: module.id,
    selector: 'app-root',
    templateUrl: 'app.component.html',
    styleUrls: ['app.component.css'],
    directives: [SpeakersComponent]
})
export class AppComponent {
    title = 'test-angular2-conference works!';
}


The only change we’ve made is to add a directives property to the @component declaration and add the imported SpeakersComponent class.

Now let’s use the component in app.component.html. Modify the template file to

<div class="page">
    <speakers></speakers>
</div>


Looking at your browser window, you should now see a list of users along with their talk count, which by default is 2. If that is not the case and you are getting errors make sure to remove all the code on .spec.ts files as these are used for unit testing and we will not cover them in this article.

Now let’s move on to the speaker-details components.

Speaker Details Component

Create the component using:

ng generate component speaker - details


In the new class file speaker-details/speaker-details.component.ts, let’s make a call to the server for a single speaker, so modify it like so:

import {
    Component, OnInit
}
from '@angular/core';
import {
    ActivatedRoute
}
from '@angular/router';
import {
    SpeakerService
}
from '../speakers/services/speaker-service';
import {
    Speaker
}
from '../speakers/models/speaker';

@
Component({
    moduleId: module.id,
    selector: 'speaker-details',
    templateUrl: 'speaker-details.component.html',
    styleUrls: ['speaker-details.component.css']
})
export class SpeakerDetailsComponent implements OnInit {
    speaker: Speaker;
    constructor(
        private speakerService: SpeakerService,
        private route: ActivatedRoute
    ) {}

    ngOnInit() {
        this.route.params.subscribe(params => {
            let speakerId = params['id'];
            this.speakerService.getOne(speakerId).then(speaker => 
               this.speaker = speaker);
        });
    }

}


Here we are extracting the speaker ID from the URL parameters and making a call to the server for that speaker using the speaker service.

Next, in the template file for the component speaker-details.component.html, modify it to look like this:

<div>
    <h2>Speaker Details</h2>
    <div *ngIf="speaker" class="details">
        <p>Name: {{ speaker.firstName }} {{ speaker.lastName }}</p>
        <p>{{ speaker.summary }}</p>
    </div>
    <div *ngIf="speaker" class="talks">
        <h3>Talks</h3>
        <div *ngFor="let talk of speaker.talks" class="talk">
            <p><span>Title:</span> <span>{{talk.title}}</span>
            </p>
            <p>
              <span>Date:</span>
              <span>{{talk.date | date:"yMMMdjms"}}</span>
            </p>
            <p>{{ talk.summary }}</p>
        </div>
    </div>
</div>


This displays the details for the current speaker and lists out more details about each of their talks. In this template, we are making use of the *ngIf directives which are used to show or remove dom elements based on a property value in the templates component class.

Setup the angular 2 router

Next, let’s set up the router so we can easily navigate between pages. This will allow us to click on a speaker name on the homepage and we’ll be taken to the details page for that speaker.

Manually create a router file called routes.ts directly inside the app folder.

In there paste the following code:

import {
    provideRouter, RouterConfig
}
from '@angular/router';
import {
    SpeakersComponent
}
from './speakers/index';
import {
    SpeakerDetailsComponent
}
from './speaker-details/index';

const routes: RouterConfig = [{
    path: 'speakers',
    component: SpeakersComponent
}, {
    path: 'speakers/:id',
    component: SpeakerDetailsComponent
}, {
    path: '',
    redirectTo: '/speakers',
    pathMatch: 'full'
}];

export const APP_ROUTE_PROVIDERS = [
    provideRouter(routes)
];


Here we are setting up three pages. One for the homepage, one for the speakers page, and the last one being the speaker-details page.

When we hit the homepage, we have instructed the router to redirect us to the speakers list page and that will automatically load the speakers component.
When we click a specific speaker, we’ll be taken to the speaker-details page and the speaker-details-component will be automatically loaded to show the information for that speaker.

That’s all the setup we need for the router, however, we need to modify the speakers component so that it links to the speaker-details page.

Modify its class file by adding a directive property to its component declaration like so:

...More code
    ...
import 'rxjs/add/operator/toPromise';
import {
    ROUTER_DIRECTIVES
}
from '@angular/router';

@
Component({
        moduleId: module.id,
        selector: 'speakers',
        templateUrl: 'speakers.component.html',
        styleUrls: ['speakers.component.css'],
        directives: [ROUTER_DIRECTIVES]
    })
    ...
    ...More code


Don’t forget to import the ROUTER_DIRECTIVES first. Modify the template file to look like the following:

<div>
    <h2>Speakers</h2>
    <div>
        <p *ngFor="let speaker of speakers">
          <a class="speaker" href="#" [routerLink]="['/speakers', 
             speaker.id]">
            {{ speaker.firstName + " " + speaker.lastName }}</a> 
           <span class="talks-counter">
            {{speaker.talks.length}} talk(s)</span>
        </p>
    </div>
</div>


All we have added is the part:

[routerLink] = "['/speakers', speaker.id]"


This will evaluate the speaker model and create the link to the appropriate speaker using the route. Now import the route.ts and load it in src/main.ts. Modify the main to look like this:

import {
    bootstrap
}
from '@angular/platform-browser-dynamic';
import {
    enableProdMode
}
from '@angular/core';
import {
    AppComponent, environment
}
from './app/';
import {
    HTTP_PROVIDERS
}
from '@angular/http';
import {
    SpeakerService
}
from './app/speakers/services/speaker-service';
import {
    APP_ROUTE_PROVIDERS
}
from './app/routes';

if (environment.production) {
    enableProdMode();
}

bootstrap(AppComponent, [
    HTTP_PROVIDERS, SpeakerService, APP_ROUTE_PROVIDERS
]);


Finally, replace app.component.html with the following code:

<div class="page">
    <router-outlet></router-outlet>
</div>


and modify the app.component.ts file to look like this:

import {
    Component
}
from '@angular/core';
import {
    ROUTER_DIRECTIVES
}
from '@angular/router';

@
Component({
    moduleId: module.id,
    selector: 'app-root',
    templateUrl: 'app.component.html',
    styleUrls: ['app.component.css'],
    directives: [ROUTER_DIRECTIVES]
})
export class AppComponent {
    title = 'test-angular2-conference works!';
}


Now we have a list of speaker links on the homepage which when clicked will take you to the speaker-details page for that speaker.

Component Scoped Styling

Even though we have a functional application, it does not look pretty. Let’s add some styling to both the speaker list page and the speaker details page.

Angular 2 has component-level styles which only apply to elements in the component. You can still have global styles but we do not need them in this application. So with that in mind, let’s style the app component which has a class of pages in its template.

In the css file app.component.css add there

.page {
    max - width: 600 px;
    margin - left: auto;
    margin - right: auto;
}


This will center the page of our application. Next style the links for speakers. In the file speakers.component.css, add there:

.speaker {
    margin - bottom: 40 px;
    padding: 5 px 20 px;
    border - radius: 7 px;
    background - color: #0093ff;
	color: white;
}


The speaker links look much prettier now. Finally, navigate to any speaker's page by clicking on their name. In the file speaker-details.component.css add this:

.details {
    margin - bottom: 40 px;
    padding: 5 px 20 px;
    border - radius: 7 px;
    margin - bottom: 15 px;
    background - color: #0093ff;
	color: white;
}

.talk {
	padding: 5px 20px;
	border-radius: 7px;
	margin-bottom: 15px;
	background-color: # 0093 ff;
    color: white;
}


The details page should look a little better now.

Conclusion

That brings us to the end, where we touched on some Angular 2 concepts like using angular-cli, data-binding, components, dependency injection, scoped-styling, and directives.

Have you used Angular 2 before? Let us know what your thoughts are in the comments below.

You can download the code sample for this tutorial.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Web Development Tutorials

Getting Started with React Navigation v6 and TypeScript in React Native

In this tutorial, let's look at how you can set up and use React Navigation and TypeScript together in a React Native app.

June 3, 2022 | By Aman Mittal | 11 min read

Web Security

How Secure is your Web Browser?

Pedro Fortuna points out that if companies continue to solely focus on protecting the server, they will leave their front door open to attacks.

January 20, 2017 | By Pedro Fortuna | 3 min read

Section Divider

Subscribe to Our Newsletter