How to Create a Basic Angular App
By: John Au-Yeung
Tags: Angular, Web Development

Using Angular to build a simple front end app is easy. With Bootstrap, building an Angular app is easier since we can make the elements looks good easily.

In this story, we will build a dictionary app using the Oxford dictionary API and ng2-bootstrap Bootstrap library for Angular. To get started, we start by install the Angular CLI. We run npm i -g @angular/cli .

Once Angular CLI is installed, we run ng new dictionaryApp to scaffold the dictionary app. This should build the folder structure for our app.

We have to install the ng2-bootstrap library ourselves. To do this, we run npm i ng2-bootstrap . This provides our app with Bootstrap widgets.

Now we can create the components. We add the following components. Run:

ng g component main

This should create a component called main with files called main.component.ts and main.component.html .

In main.component.ts , we add:



import { ActivatedRoute } from '

import { HttpClient } from ' import { Component, OnInit } from ' @angular/core ';import { ActivatedRoute } from ' @angular/router ';import { HttpClient } from ' @angular/common /http';

selector: 'app-main',

templateUrl: './main.component.html',

styleUrls: ['./main.component.scss']

})

export class MainComponent implements OnInit { @Component ({selector: 'app-main',templateUrl: './main.component.html',styleUrls: ['./main.component.scss']})export class MainComponent implements OnInit { query

sub: any;

dictionary: string;

dicts: {};

title = 'Longman Dictionary of Contemporary English (5th edition)';

results: any[] = [];

constructor(private http: HttpClient, private route: ActivatedRoute) { } ngOnInit() {

this.query = {

headword: '',

relatedwords: '',

synonyms: '',

phrasalverbs: '',

partsofspeech: '',

images: '',

audio: '',

search: '',

}; this.dicts = {

ldoce5: 'Longman Dictionary of Contemporary English (5th edition)',

lasde: 'Longman Active Study Dictionary',

ldec: 'Longman English-Chinese Dictionary of 100,000 Words (New 2nd Edition)',

wordwise: 'Longman Wordwise Dictionary',

laesd: 'Longman Afrikaans to English',

leasd: 'Longman English to Afrikaans',

laad3: 'Longman Advanced American Dictionary',

laes: ' English to Latin American Spanish',

lase: 'Latin American Spanish to English',

brep: 'English to Brazilian Portuguese',

brpe: 'Brazilian Portuguese to English'

} this.sub = this.route.params.subscribe(params => {

this.dictionary = params['dict'];

if (this.dictionary) {

this.title = this.dicts[this.dictionary];

}

}); } ngOnDestroy() {

this.sub.unsubscribe();

} getWords() {

let params = new URLSearchParams();

params.set('headword', this.query.headword);

if (this.query.relatedwords) {

params.set('related_words', this.query.relatedwords);

}

if (this.query.synonyms) {

params.set('synonyms', this.query.synonyms);

}

if (this.query.phrasalvebs) {

params.set('phrasal_verbs', this.query.phrasalvebs);

}

if (this.query.partsofspeech) {

params.set('part_of_speech', this.query.partsofspeech);

}

if (this.query.images) {

params.set('images', this.query.images);

}

if (this.query.audio) {

params.set('audio', this.query.audio);

}

if (this.query.search) {

params.set('search', this.query.search);

}

let dict = (this.dictionary) ? this.dictionary : '';

.subscribe(

dictresults => {

this.results = (dictresults as any).results;

},

error => console.log(error)

)

} this.http.get(` https://api.pearson.com/v2/dictionaries/${dict}/entries?${params.toString()}`) .subscribe(dictresults => {this.results = (dictresults as any).results;},error => console.log(error) }

This is the main page for our dictionary app with the search logic. It uses the HTTP client which we will import in the code later into our main app module.

this.http.get is a observable, which means that whenever something feeds into it, it will get the latest data.

This block:



.subscribe(

dictresults => {

this.results = (dictresults as any).results;

},

error => console.log(error)

) this.http.get(` https://api.pearson.com/v2/dictionaries/${dict}/entries?${params.toString()}`) .subscribe(dictresults => {this.results = (dictresults as any).results;},error => console.log(error)

is where we make the HTTP request to the Pearson server to get the dictionary entries.

In main.component.html , we add:

<h1>Dictionary Search - {{title}}</h1>

<div class='row'>

<div class='col-md-12'>

<form #dictionaryForm="ngForm" novalidate (ngSubmit)="getWords(dictionaryForm)">

<div class="form-group">

<label>Word</label>

<input type='text' class="form-control" required #headword="ngModel" [(ngModel)]='query.headword' name="headword" pattern="[a-zA-Z]+">

<div *ngIf="headword.errors && (headword.dirty || headword.touched)">

<p><small [hidden]="!headword.errors.required" class="form-text text-muted">Word is required</small></p>

<p><small [hidden]="!headword.invalid" class="form-text text-muted">Please enter a word</small></p>

</div>

</div>

<p *ngIf="advanced" (click)="advanced = !advanced"><i class="fa fa-chevron-up" aria-hidden="true"></i> Advanced Search</p>

<p *ngIf="!advanced" (click)="advanced = !advanced"><i class="fa fa-chevron-down" aria-hidden="true"></i> Advanced Search</p>

<div *ngIf="advanced">

<div class="form-group">

<label>Related Words</label>

<input type='text' class="form-control" #relatedwords="ngModel" [(ngModel)]='query.relatedwords' name="relatedwords" pattern="[a-zA-Z]+">

<div *ngIf="relatedwords.errors && (relatedwords.dirty || relatedwords.touched)">

<p><small [hidden]="!relatedwords.invalid" class="form-text text-muted">Please enter a related word</small></p>

</div>

</div>

<div class="form-group">

<label>Synonyms</label>

<input type='text' class="form-control" #synonyms="ngModel" [(ngModel)]='query.synonyms' name="synonyms" pattern="[a-zA-Z]+">

<div *ngIf="synonyms.errors && (synonyms.dirty || synonyms.touched)">

<p><small [hidden]="!synonyms.invalid" class="form-text text-muted">Please enter a synonym</small></p>

</div>

</div>

<div class="form-group">

<label>Phrasel Verb</label>

<input type='text' class="form-control" #phrasalverb="ngModel" [(ngModel)]='query.phrasalverb' name="phrasalverb" pattern="[a-zA-Z]+">

<div *ngIf="phrasalverb.errors && (phrasalverb.dirty || phrasalverb.touched)">

<p><small [hidden]="!phrasalverb.invalid" class="form-text text-muted">Please enter a phrasal verb</small></p>

</div>

</div>

<div class="form-group">

<label>Parts of Speech</label>

<select class='form-control' #partsofspeech='ngModel' [(ngModel)]='query.partsofspeech' name='partsofspeech' pattern="[a-zA-Z]+">

<option [ngValue]="noun">Noun</option>

<option [ngValue]="verb">Verb</option>

</select>

<div *ngIf="partsofspeech.errors && (partsofspeech.dirty || partsofspeech.touched)">

<p><small [hidden]="!partsofspeech.invalid" class="form-text text-muted">Please select parts of speech</small></p>

</div>

</div>

<div class="form-group">

<label>Images</label>

<select class='form-control' #images='ngModel' [(ngModel)]='query.images' name='images' pattern="[a-zA-Z]+">

<option [ngValue]="picture">Picture</option>

</select>

<div *ngIf="images.errors && (images.dirty || images.touched)">

<p><small [hidden]="!images.invalid" class="form-text text-muted">Please select image</small></p>

</div>

</div>

<div class="form-group">

<label>Audio</label>

<input type='text' class="form-control" #audio="ngModel" [(ngModel)]='query.audio' name="audio" pattern="[a-zA-Z]+">

<div *ngIf="audio.errors && (audio.dirty || audio.touched)">

<p><small [hidden]="!audio.invalid" class="form-text text-muted">Please enter audio type.</small></p>

</div>

</div>

<div class="form-group">

<label>Search</label>

<input type='text' class="form-control" #search="ngModel" [(ngModel)]='query.search' name="search" pattern="[a-zA-Z]+">

<div *ngIf="search.errors && (search.dirty || search.touched)">

<p><small [hidden]="!search.invalid" class="form-text text-muted">Please enter search term.</small></p>

</div>

</div>

</div>

<button class='btn btn-primary'>Search</button>

</form>

</div>

</div>

<div class='row'>

<div class='col-md-12'>

<br>

<div *ngIf='results'>

<div *ngFor='let r of results' class='panel panel-default'>

<div class='panel-heading'>

<h3>{{r.headword}}</h3>

</div>

<div class='panel-body'>

<div class='col-md-12' *ngIf="r.part_of_speech">

<p><b>Parts of Speech:</b> {{r.part_of_speech}}</p>

</div>

<div class='col-md-12' *ngIf="r.pronunciations">

<p><b>Pronunciations</b></p>

<div *ngFor='let p of r.pronunciations' class='col-md-12'>

<span>IPA: {{p.ipa}} <span *ngIf="p.lang">({{p.lang}})</span></span>

</div>

</div>

<div *ngFor='let s of r.senses' class='col-md-12'>

<p *ngIf="s.definition"><b>Definition:</b> {{s.definition}}</p>

<p *ngIf="s.translation"><b>Translation:</b> {{s.translation}}</p>

<div *ngIf="s.translations">

<b>Translations:</b>

<div *ngFor='let tr of s.translations' class='col-md-12'>

<p *ngIf='tr.examples'>

Examples: <span *ngFor='let ex of tr.examples'>{{ex}}</span>

</p>

</div>

</div>

</div>

</div>

</div>

</div>

</div>

</div>

Note that the [( in [(ngModel)] denotes 2 way data binding between the component or directive and the current component and [ denotes one way binding from the current component to the directive or component. The this variable in our component is shared between our templates and TypeScript component code. For example, query.audio is the same as this.query.audio in main.component.html . *ngFor is for repeatedly displaying our items. *ngIf displays the item only if it’s truthy. [ngValue] directive allows for values other than strings in our drop downs.

Notice that we have form validation in our forms. We have the pattern attribute where we provide the regular expression for Angular to to validation with the FormsModule . We have #audio=”ngModel" [(ngModel)]=’query.audio’ name=”audio” to do 2 way binding and create the form field which Angular FormsModule will use to validate the form field. The other fields are the same. (ngSubmit)=”getWords()” is where the submit handler for this form is. It is called when the button with type='submit' is pressed.

In main.component.scss , we put:

.form-control, .panel, .panel-default{

max-width: 850px

}

In app.component.html , we put:

<div class='box'>

<nav class="navbar navbar-default">

<div class="container-fluid">

<!-- Brand and toggle get grouped for better mobile display -->

<div class="navbar-header">

<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"

aria-expanded="false">

<span class="sr-only">Toggle navigation</span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

</button>

<a class="navbar-brand" routerLink="/">Dictionary App</a>

</div> <!-- Collect the nav links, forms, and other content for toggling -->

<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">

<ul class="nav navbar-nav">

<li><a routerLink="/">Home</a></li>

<li class="dropdown">

<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dictionaries <span class="caret"></span></a>

<ul class="dropdown-menu">

<li><a routerLink="/dictionary/ldoce5">Longman Dictionary of Contemporary English (5th edition)</a></li>

<li><a routerLink="/dictionary/lasde">Longman Active Study Dictionary</a></li>

<li><a routerLink="/dictionary/ldec">Longman English-Chinese Dictionary of 100,000 Words (New 2nd Edition)</a></li>

<li><a routerLink="/dictionary/wordwise">Longman Wordwise Dictionary</a></li>

<li><a routerLink="/dictionary/laesd">Longman Afrikaans to English</a></li>

<li><a routerLink="/dictionary/leasd">Longman English to Afrikaans</a></li>

<li><a routerLink="/dictionary/laad3">Longman Advanced American Dictionary</a></li>

<li><a routerLink="/dictionary/laes">English to Latin American Spanish</a></li>

<li><a routerLink="/dictionary/lase">Latin American Spanish to English</a></li>

<li><a routerLink="/dictionary/brep">English to Brazilian Portuguese</a></li>

<li><a routerLink="/dictionary/brpe">Brazilian Portuguese to English</a></li>

</ul>

</li>

</ul>

</div>

<!-- /.navbar-collapse -->

</div>

<!-- /.container-fluid -->

</nav>

<div class='container'>

<router-outlet></router-outlet>

</div>

</div>

so that we have a navigation we use at the top of our app. routerLink directive allows us to direct our to the routes we want in an a tag.

In app.module.ts , we put:



import { NgModule } from '

import { FormsModule } from '

import { HttpClientModule } from '

import { AppComponent } from './app.component';

import { RouterModule } from '

import { MainComponent } from './main/main.component'; import { BrowserModule } from ' @angular/platform-browser ';import { NgModule } from ' @angular/core ';import { FormsModule } from ' @angular/forms ';import { HttpClientModule } from ' @angular/common /http';import { AppComponent } from './app.component';import { RouterModule } from ' @angular/router ';import { MainComponent } from './main/main.component';

declarations: [

AppComponent,

MainComponent

],

imports: [

BrowserModule,

FormsModule,

HttpClientModule,

RouterModule.forRoot([

{

path: '',

component: MainComponent

},

{

path: 'dictionary/:dict',

component: MainComponent

}

])

],

providers: [],

bootstrap: [AppComponent]

})

export class AppModule { } @NgModule ({declarations: [AppComponent,MainComponent],imports: [BrowserModule,FormsModule,HttpClientModule,RouterModule.forRoot([path: '',component: MainComponent},path: 'dictionary/:dict',component: MainComponent])],providers: [],bootstrap: [AppComponent]})export class AppModule { }

The code above includes the FormsModule and the components we created in the app.

This block:

RouterModule.forRoot([

{

path: '',

component: MainComponent

},

{

path: 'dictionary/:dict',

component: MainComponent

}

])

defines the routes which will be reached with user enters these URLs.

In index.html , we add:

<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

<link href="//fonts.googleapis.com/css?family=Roboto" rel="stylesheet">

<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

<script src="//code.jquery.com/jquery-1.12.4.min.js" ></script>

<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" ></script>

This includes the Bootstrap styles so that the Bootstrap widgets display properly.

At the end, we get:

Subscribe to my email list now at http://jauyeung.net/subscribe/ to get more tutorials.

Follow me on Twitter at https://twitter.com/AuMayeung