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 with Bootstrap

How to add Bootstrap to your angular application

Add dependency to your package.json file, this will install bootstrap to your local node_modules folder with “npm update” command.

"dependencies" : {
...
    "bootstrap": "^3.3.7",
    "font-awesome": "^4.7.0"
}, 

Add CSS files to angular cli file (.angular-cli.json)

"apps": [{
...
      "styles": [
        "../node_modules/bootstrap/dist/css/bootstrap.css",
        "../node_modules/font-awesome/css/font-awesome.css",
        "styles.css"
      ],
...
}]

Now you can start using bootstrap classes in HTML templates.

<div class='panel panel-primary'>
  <div class='panel-heading'>
    Title
  </div>
  <div class='panel-body'>
    <div class='row'>
       <div class='col-md-2'>Filter by:</div>
       <div class='col-md-4'>
         <input type='text'/>
       </div>
    </div>

...

 

Angular components

Create new component with CLI

To create new component run ng generate command. This command has ability to generate different entities of your angular application like components, modules, interfaces, services, etc.  If you generate component, your angular application will  be updated to include newly generated component automatically.

$ ng generate component component_name
$ ng g c component_name

 

 

 

Angular environment with java backend server app setup

Install Angular toolchain

Before you can start working with angular you need to install nodejs and npm. Your angular development will mostly rely on those two tools.

Typescript

You need to install typescript, in which your you will write code.

$ npm install -g typescript

Angular.io

Angular is a tool for building web & desktop oriented multi-platform client applications. Angular comprise of all development tools to build fully featured web application. To build standalone desktop applications with native functionalities running on Windows, iOS or  Linux you can integrate your angular app with Electron platform.

AngularCLI

After initial installation of npm you install angular cli (command line interface) to speed up development.

$ npm install -g @angular/cli@latest
Update CLI tools to latest version

You need to uninstall previous version and install latest version again.

$ npm uninstall -g @angular/cli 
$ npm cache clean
$ npm install -g @angular/cli@latest 
$ ng -v

On the end you can check installed version with “ng -v” command.

Visual studio code

Best code editor & debugger for angular/typescript/javascript.

Create new angular application scaffold

If you create client side and server side application it is wise to create single parent folder for both part of application and divide it with two folders (server and client for example).

To create new application structure for angular web application, go to “client” application folder and create new application:

$ ng new app_name

To generate new application with build in support for SCSS CSS styles support:

$ ng new app_name --style=scss

To change existing app to support SCSS styles:

$ ng set defaults.styleExt=scss

Serve application in development

Application will be compiled and local node server will serve it at http://localhost:4200 address.

$ ng serve

Java Spring boot server app and Angular client app setup

If you develop Java spring boot server application to serve JSON services on it, on embedded tomcat server for example, and running it at address localhost:8080,  but your client app is served on localhost:4200 address, than you will need some sort of proxy to overcome cross site request forgery limitations. In that case you define proxy on your angular client application. That way all your requests from angular application to the server addresses beginning with  localhost:4200/api suffix will be redirected to tomcat server running on 8080 port.

Setup proxy address

Create proxy-conf.json file like this (there is only one path redirection definition “/api” ) :

{
    "/api": {
        "target": "http://localhost:8080",
        "secure": false
    }
}

In your package.json file define new start command for npm:

...
"scripts": {
    "ng": "ng",
    "start": "ng serve --proxy-config proxy-conf.json",

...

Startup angular client app

Don’t forget to use “npm start” instead of “ng serve” to run angular application that way.

$ npm start

Now you can start debugging  process on your java server app directly from your angular client app.

For example your java service is named “greeting” and you publish it under “http://localhost:8080/api/greeting” path. When you load it from angular web page your address will be “http://localhost:4200/api/greeting” and because you use proxy redirection your request will arrive on tomcat server.

How to run arbitrary command with “npm”

To run an arbitrary command from package’s scripts object use “npm run”.

$npm run "command_from_scripts_section"

This is useful if you use IDE with terminal command window, you can run REST server directly from your angular project folder for example.

Add “java -jar project.jar” command to package.json:

  "scripts": {
    "ng": "ng",
    "start": "ng serve --proxy-config proxy-conf.json",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "server": "java -jar ../../server/SpringBootMyApp/target/myapp-0.0.1-SNAPSHOT.jar"
  },

Now if you run “server” command with npm run, you will start “java -jar …” command:

$ npm run server

Here I describe how to prepare your java spring boot project as executable jar.

 

 

 

Netbeans and Polymer

Polymer HTML code completion for Netbeans 8.2

Thanks to this wonderful extension I can get rid of annoying errors in HTML editor when I edit polymer components in html files.

Because I use maven project type and netbeans , there is no “.nbproject” folder. In that case the “customs.json” file must be present in src/main/webapp folder.

2016-12-10-11_08_11

Recreate new version of customs.json

Because not all attributes are recognized by customs.json I will recreate it as explained in project documentation.

After downloading master zip file, you need to create “dist” folder under src folder and then run index.js file.

igorb@Pavilion ~/PolymerForNB-master/src
$ mkdir dist

igorb@Pavilion ~/PolymerForNB-master/src
$ node index.js

New copy of customs.json will be created in dist folder, just copy it to the webapp folder and that’s it.

Merge changes back to project file

Don’t forget if you already used customization and added something, you need to merge old version with new one or you will lose your changes…

Well, it’s not so simple, looks like after you add few changes to customs.json with HTML editor, the file (customs.json) changed very  dramatically. Looks like netbeans  reorganize whole structure or something.