Angular and Sass with Bootstrap 3 style support

Integrating Sass and Bootstrap 3 to angular project

If you want to customize default bootstrap 3 CSS theme, you will need to change it. But to be able to do that you will need to install CSS extension language Sass and bootstrap-sass definitions and then compile custom bootstrap CSS within your angular application.  This is officially supported by angular-cli tools.

Setup angular project for Sass

If you create new angular project you can add option “–styles=scss” to “new” command. Yo can also change existing project style settings with “set” command:

$ ng new my_app --style=scss
or
$ ng set defaults.styleExt scss 

To compile scss files to css files we need node-sass compiler :

npm install node-sass --save-dev

Next we install bootstrap-sass in our project :

npm install bootstrap-sass --save

Writing CSS with Sass

First we create “scss” subfolder under the “assets” folder and create main.scss file in it.

 src
 !--- assets
      !--- scss
             main.scss

Angular project expect your main.scss file defined in the .angular-cli.json file:

"styles": [
        "assets/scss/main.scss"
      ],

Start the project and angular-cli will compile SCSS and include result CSS into your application.

Test Sass compiler and angular integration

Add some simple scss definition to main.scss:

$default-font: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
$default-font-size: 16px;
$default-title-size: 200%;
$default-title-weight: bold;

.mytitle {
   font: $default-title-weight $default-title-size $default-font;
}

Now add a paragraph with “mytitle” class to your index.html :

...
<body>
  <p class="mytitle">This is a test</p>
...

If everything works  as expected you will see a test paragraph with correct font and weight as defined by scss:

Scss compiler create proper CSS definition and angular with webpack create styles.bundle.js file where we can find our “mytitle” class defined as :

// module
exports.push([module.i, ".mytitle {\n  font: bold 200% \"Segoe UI\", Tahoma, Geneva, Verdana, sans-serif; }\n", ""]);

Import Bootstrap Sass definitions

I am changing my existing seed project so you can get source code of this project from github repository “ANGULARMYAPP” .

Immediately after changing style definition to SCSS in our project, we lost all bootstrap CSS formating and the page looks like :

As we see from the screen above, there is no bootstrap formatting anymore, buttons are missing images (font_awesome as part of bootstrap also missing).

Now we will add bootstrap-scss and compile our own version of bootstrap css.

First we create new custom variables file and copy  all bootstrap theme variables to it. That way we will be able to customize any bootstrap setting simply by changing our copy of variable.

Create new file “_custom_variables.scss” and copy content of the bootstrap _variables.scss file into it or just copy a file into your scss folder. All files named with underscore are meant to be imported in another scss file by the Sass standard. Files without underscore are compiled to CSS format.

$cp node_modules/bootstrap-sass/assets/stylesheets/bootstrap/_variables.scss src/assets/scss/_custom_variables.scss

Now we import bootstrap and you variables file into main.scss file:

// Sass compiler need information where icons/fonts are with absolute path definition, tilde character (~) works from any project folder  
$icon-font-path: '~bootstrap-sass/assets/fonts/bootstrap/';

@import "_custom_variables.scss";
@import "~bootstrap-sass/assets/stylesheets/_bootstrap.scss";

If everything works as expected we will get our original bootstrap design back and we can start modifying it, for example we will change primary button color to red. Change variable  $brand-primary in the file _custom_variables.scss to red color:

And the result should be visible immediately on the screen :

The button defined in the currency-list component with the class “btn-primary”  is now red as expected.

 

 

 

 

 

 

Angular – create currency edit component

You can get source code of this project from github repository “ANGULARMYAPP” .

Edit form

What I wish to accomplish is to enable edit function on the currency list component.  Button “Add” will open editing panel, after saving changes the  editing panel will close.

The Pencil open source product is used for GUI prototyping.

To create new component we use angular command line interface and generate new component skeleton.

# ng g c currency-edit

After that we add new component to the currency-list.component template.

Some design considerations

New component will contain edit form with two buttons (save and cancel). After adding or editing record the form will be closed automatically. After some changes will be made, the list component will refresh it’s data source and the table will refresh it on the screen.

After we look at actions needed in the workflow, it’s obvious that the best way is to develop component with two events: save and cancel. The component will work only with events, that way no service interaction will be needed. All interaction will remain in the list component where we already have back-end service injected at the constructor.

Edit component template file

<div class="panel panel-default" style="padding: 4px">
  <div class="row">
    <div class="col-xs-12">
      <form id="currency-edit" (ngSubmit)="onSubmit(f.value)" #f="ngForm">
        <div class="row">
          <div class="col-sm-2 form-group">
            <label for="code">Code</label>
            <input type="text" class="form-control" id="code" [(ngModel)]="currency.code" name="code" #codeCtrl="ngModel" required>
          </div>
          <div class="col-sm-10 form-group">
            <label for="abreviation">Name</label>
            <input type="text" class="form-control" id="abbreviation" [(ngModel)]="currency.abbreviation" name="abbreviation" #abbreviationCtrl="ngModel"
              required>
          </div>
        </div>
        <div class="form-group">
          <label for="description">Description</label>
          <input type="text" class="form-control" id="description" [(ngModel)]="currency.description" name="description" #descriptionCtrl="ngModel">
        </div>
        <div class="row">
          <div class="col-xs-12">
            <button class="btn btn-success" type="submit">Save</button>
            <button class="btn btn-default" type="button" (click)="onCancel()">Cancel</button>
          </div>
        </div>
      </form>
    </div>
  </div>
</div>

Edit component class file

The component is really simple one. I am surprised how little is needed to create a pretty component with binding and events in the angular framework !

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { NgModel, NgForm} from '@angular/forms';
import { Currency } from "app/currency/currency";

@Component({
  selector: 'app-currency-edit',
  templateUrl: './currency-edit.component.html'
})
export class CurrencyEditComponent {
  @Input()  currency: Currency;
  @Output() cancel: EventEmitter<boolean> = new EventEmitter<boolean>(); 
  @Output() save: EventEmitter<Currency> = new EventEmitter<Currency>(); 

  onCancel() {
    this.cancel.emit(true);
  }

  onSubmit(value: Currency) {
    this.save.emit(value);
  }
}

The component is included in the list component with component tag just above the table component and under the “Add” button :

...
    <app-currency-edit 
      *ngIf="isEdit"
      (cancel)="onCancel($event)" 
      (save)="onSave($event)"
      [currency]=currency>
    </app-currency-edit>  
...

In the list component we intercept two events (cancel and save) and call to service layer for back-end actions. With the currency variable we push the selected row to the edit component (this is one way binding).

After each change in the database we refresh the table with changed data.

List component class

import { Component, OnInit } from '@angular/core';
import { Currency } from "app/currency/currency";
import { CurrencyService } from "app/currency/currency.service";
import { Observable } from "rxjs/Observable";

@Component({
  selector: 'app-currency-list',
  templateUrl: './currency-list.component.html',
  providers: [ CurrencyService ]
})
export class CurrencyListComponent implements OnInit {
  title: string = 'Currency List';
  errorMessage: string;
  currencies: Currency[];
  currency: Currency;
  isEdit: boolean;

  constructor(private currencyService: CurrencyService) { }

  ngOnInit() {
    this.isEdit = false;
    this.refreshList();
  }

  refreshList() {
    this.currencyService.getCurrencies().subscribe(
      currencies => this.currencies = currencies, 
      error => this.onError(error)
    );
  }

  readCurrency(rowId: number) {
    this.currencyService.getCurrency(rowId).subscribe(
      newValue => {
        this.currency = newValue;
        this.isEdit = true;
      },
      error => this.onError(error)
    );
  }

  onCancel(value: boolean) {
    this.isEdit = !value; 
  }

  onAdd() {
    this.currency = new Currency();
    this.isEdit = true;
  }

  onSave(value: Currency) {
    this.currencyService.updateCurrency(value).subscribe(
      success => {
        this.isEdit = false;     
        this.refreshList();
      },
      error => this.onError(error)
    );
  }

  onEdit(event: Event, rowId: number) {
    this.readCurrency(rowId);
  }

  onDelete(event: Event, rowId: number) {
    var ret: boolean;
    ret = confirm("Are you sure to delete record ?");
    if(ret == true) {
      this.currencyService.deleteCurrency(rowId).subscribe(
        success => this.refreshList(),
        error => this.onError(error)    
      )
    }
  }

  onError(errorMsg: string) {
    this.errorMessage = errorMsg;
  }
}

And the final template for currency-list component

<div class='panel panel-default'>
  <div class='panel-heading'>
    <span class='panel-title'>
      {{title}}
    </span>
  </div>
  <div style="padding: 4px">
    <div style="padding-bottom: 4px">
      <button class="btn btn-sm btn-primary" (click)="onAdd()" *ngIf="!isEdit">
        <span class="glyphicon glyphicon-plus"></span>
        Add</button>
    </div>

    <div>
      <span *ngIf="errorMessage!=''" >
        {{errorMessage}}
      </span>
    </div>

    <app-currency-edit 
      *ngIf="isEdit"
      (cancel)="onCancel($event)" 
      (save)="onSave($event)"
      [currency]=currency>
    </app-currency-edit>  
    
    <table class='table table-responsive table-striped'>
      <thead>
        <tr>
          <th>Currency</th>
          <th>Name</th>
          <th>Description</th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor='let currency of currencies'>
          <td>{{currency.code}}</td>
          <td>{{currency.abbreviation}}</td>
          <td>{{currency.description}}</td>
          <td align="right">
            <div class="btn-group">
            <button class="btn btn-xs btn-default" (click)="onEdit($event, currency.rowId)">
               <span class="glyphicon glyphicon glyphicon-pencil" aria-label="edit"></span>
            </button>
            <button class="btn btn-xs btn-default" (click)="onDelete($event, currency.rowId)">
               <span class="glyphicon glyphicon-remove" aria-label="remove"></span>
            </button>
            </div>
          </td>
        </tr>
      </tbody>
    </table>

  </div>
</div>



Because every client/server interaction is now asynchronous, the code in the list view component become less readable and little harder to understand. If you need to add any code after the call to the server, you need to add it to the subscribe success part of the observable execution path.

Don’t forget, you can always set a callback function as a subscribed success call, that way you retain readability even in more complex scenarios.

I just open the curly brackets after the fat arrow function and add multiple statements for this simple example.

The final result

If you click on the “Add” button, the edit form will open and you will be able to add new record to the list.

Edit form will show only input fields enabled for edit (no “Id” field for example).

If you click on the pencil button on a specific row, you open the edit form with the selected data. You can then save or cancel changes. After the action, the edit form is closed (removed from the DOM because of *ngIf directive), and the data are refreshed automatically.

Of course this is only a “TODO” like application and intentionally I didn’t put router into it to remain as simple as possible.

There is always more what can be done, for example edit and delete icons are always enabled, even after the edit form is open. At the edit form, there are no real validation rules before save button can be clicked, there is no error message event and messages don’t flow up in the case of validation errors in the edit component etc. However, that are all interesting challenges for later.

Angular – create currency list view component

You can get source code of this project from github repository “ANGULARMYAPP” .

Build components

In previous blog article I describe angular environment and generate base angular skeleton application. In this article I will create my first list component and connect client app to server REST service.

Create new component

When you create new component, you will need to name it first. You can use dash in the middle of the name, but it is not required. The dash will not be used by generator, the name will be converted by pascal convention (camel case). But your file names and folder names will remain readability.

Use angular cli interface and create skeleton for a component:

$ ng generate component currency-list
  or
$ ng g c currency-list

Result of the command in the terminal window will look something like this:

The app.module file is updated automatically and new component is registered to angular application:

import { CurrencyListComponent } from './currency-list/currency-list.component';

The result in the project folder hierarchy shown new sub-folder “currency-list” with all corresponding  files:

Open app.component.html and replace generated content with new component tag like:

<app-currency-list></app-currency-list>

Now start serving angular application locally :

ng serve

And navigate to http://localhost:4200 :

Yeah, that was easy.

Proxy to java embedded server

To be able to call to the java spring boot server application we need to add proxy configuration as described here. After proxy configuration we should  add “start” command  (“start”: “ng serve –proxy-config proxy-conf.json”) to “package.json” and use “npm start” instead of “ng serve”.

Create currency interface

Create file currency.ts and add currency definition as is returned from server :

export interface ICurrency {
    rowId: number;
    code: string;
    abbreviation: string;
    description: string;
}

Add “HttpModule” to application

We will request data from REST server with this module, therefore we need to add it to the app.module.ts (as imports) :

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { CurrencyListComponent } from './currency-list/currency-list.component';


@NgModule({
  declarations: [
    AppComponent,
    CurrencyListComponent
  ],
  imports: [
    HttpModule,
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Create currency service

Well, without list of currencies from back end service there will not be a currency list component. To provide the list of currencies to the local component I need to create new angular client side service class and connect it to back end server.

If you wish generate to specific folder, you define folder as a relative path to app folder in the name of new service :

$ ng g service currency-list/currency

If you are in the root or the source folder of your angular application, you are perceived to be in the app folder.

The currency.service.ts and currency.service.spec.ts files are created in app/currency-list folder.

The service will provide a Observable of the list of currencies from the REST server application written in Java. You will find the code for the server app on this github repository.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';

import { ICurrency } from './currency';

@Injectable()
export class CurrencyService {
  currencyUrl: string = "/api/currency";

  constructor(private _http: Http) { }

  getCurrencies() : Observable<ICurrency[]> {
    return this._http.get(this.currencyUrl)
      .map((response: Response) => <ICurrency[]>response.json() )
      .do(data => console.log('Received ' + JSON.stringify(data)))
      .catch(this.handleError);
  } 

  handleError(error: Response) {
    console.error(error);
    return Observable.throw(error.json().error || 'Severe error');
  }

}

Provide new service

Don’t forget to provide new service otherwise you will not be able to use it. You need to import it and add it to the providers array.  If you add new service to the application itself you add into app.module.ts  file, but  you can also add it to the parent component, for example “app.component.ts”.  This way all components in the hierarchy from “app” component down will be able to use the service.

import { Component } from '@angular/core';
import { CurrencyService } from "./currency-list/currency.service";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [ CurrencyService ]
})
export class AppComponent {
  title = 'app';
}

Using the service

New service return “Observable” object, it means we will subscribe to it instead of call it. The method will not be triggered before data are really requested (by binding for example).  Unsubscribing should be automatic when using with angular components, otherwise we need to unsubscribe manually.

import { Component, OnInit } from '@angular/core';
import { ICurrency } from "app/currency-list/currency";
import { CurrencyService } from "app/currency-list/currency.service";
import { Observable } from "rxjs/Observable";

@Component({
  selector: 'app-currency-list',
  templateUrl: './currency-list.component.html',
  styleUrls: ['./currency-list.component.css']
})
export class CurrencyListComponent implements OnInit {
  title: string = 'Currency List';
  errorMessage: string;
  currencies: ICurrency[];

  constructor(private _currencyService: CurrencyService) { }

  ngOnInit() {
      this._currencyService.getCurrencies()
      .subscribe(currencies => this.currencies = currencies, 
                  error => this.errorMessage = <any>error);
  }

}

And binding to the array inside HTML template:

<div class='panel panel-default'>
  <div class='panel-heading'>
    <h3 class='panel-title'>
      {{title}}
    </h3>
  </div>

  <table class='table'>
    <thead>
      <tr>
        <th>Currency</th>
        <th>Name</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor='let currency of currencies'>
        <td>{{currency.code}}</td>
        <td>{{currency.description}}</td>
      </tr>
    </tbody>
  </table>

</div>

In the template we use for each directive “*ngFor” and loop over array of ICurrency objects received from java back-end service.

The result of this very simple angular list component is:

Angular and Git project setup

Angular-CLI, VSCode and Git/GitHub configuration

I will create Angular 4 client application with project setup needed to seamlessly develop and store source code to remote Github repository.

Projects folder structure

I will create client side SPA application as client to Java spring boot server application, created in previous blog article.

Both projects (server & client) are in separate folders, only at the deployment stage they will be combined under java server application. In the simplest micro-service deployment scenario, both applications are served from the same server instance. Embedded Tomcat server  will serve all files needed for angular client side application and to drive java REST services as application back-end.

myapp
!--server
!  !--SpringBootMyApp
!     
!--client
   !--AngularMyApp

Generate angular client project

After you have installed all prerequisites for angular you create a new empty application from command line. Command will create application subfolder and all required app files for simple angular app.

/myapp/client/$ ng new AngularMyApp

You can experiment with some command line switches to add additional generator options like generate routes file for example (–routing).

Dry-Run

If you do not want to actually execute commands then add –dry-run option (“-d”  for short) and inspect generated set of commands.

$ ng new app_name --routing 
$ ng new app_name --dry-run 
$ ng new app_name -r -d

Open project in Visual Studio Code

You can open project from command line (“code .”) or open folder from inside editor.

Integrate with Git  source control

After angular-cli create a project it will immediately initialize GIT folder and commit initial version of an application.

You can check log of git commits right after generating new app :

/AngularMyApp/$ git log

and result will be something like this :

Author: Angular CLI <angular-cli@angular.io>
Date:   Mon Jun 26 22:41:07 2017 +0200

chore: initial commit from @angular/cli

We see initial commit was done by @angular/cli tool.

Configure remote Git repository

If you wish to publish code to the Git-Hub server, you need to prepare local windows git to be able to push code to remote server without any username/password interaction.

git config --global credential.helper wincred
git config --global user.name "user_name"
git config --global user.email "email@example.com"

This type of user identification is used with HTTPS repository address for git communication.

You need to have github repository created (empty project!). When you create new repository, try to not create readme file, because you will not be able to push initial code up without “force” option. This option is not possible inside visual studio code. “Force push” is possible with command line or with github desktop client.

With remote repository created configure “git remote”  localy with correct “https” repository address :
/AngularMyApp/ $> git remote add AngularMyApp https://github.com/bisaga/AngularMyApp.git
Push code to Github

After adding remote repository to local git configuration you select “Push to…” inside visual studio code Git extension menu (…), select remote repository and push.

SSH key passphrase

You need to setup your ssh-agent correctly to work with remote git repository without constant interruptions with an question for ssh passphrase.

If you use bash terminal inside vscode and you work on windows, add the ssh-agent to the setup script in your home (~) folder (usually c:\Users\your_name\.bash_profile file.

env=~/.ssh/agent.env

agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; }

agent_start () {
    (umask 077; ssh-agent >| "$env")
    . "$env" >| /dev/null ; }

agent_load_env

# agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2= agent not running
agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?)

if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then
    agent_start
    ssh-add
elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then
    ssh-add
fi

unset env

You can check with the command

git fetch

If you get the question then you probably need to create proper ssh-agent setup.

 

 

Links

Initial code for AngularMyApp is on this github repostory.