Tuesday, September 17, 2019

Javascript callback, promise, await and observable

In traditional language, parallel calculation is handled by multithread. Basically, the application creates multiple threads and the operation system is responsible to manage and schedule time slice for each thread to run.

For javascript (and typescript), there is only one thread allow to run in a application, so multiple thread is not an option. Instead, async method is used to support parallel calculation, so that multiple tasks can run at the same time. Callback method, promise, async-await, and observor are some common ways for async method call. A simple way to distinguish sync and async method is for sync method, the next line after calling the sync method will not be called until the actual result is returned, while for async method, the next line will be called immediately no matter how long it will take to actually get the result.

1. callback is simplest way for executing async call, it passes the callback method to handle the result later, so there is no need to block the current call stack.
The drawback is when executing an async method, the onsuccess or onerror callback methods needs to be passed into the async method as parameters. If an async method needs to call another async method, then all the callback methods needs to be chained together, which causes the code not easy to read or maintain.

The below is a sample of using callback for async method to first build world, and then build a home.

 
  buildTheWorld(onsuccess, onerror) {
    // tslint:disable-next-line: only-arrow-functions
    setTimeout( () => {
      const r = Math.random();
      if (r > 0.5){
        console.log('build world successful');
        onsuccess(r);
      } else {
        console.log('build world failed');
        onerror();
      }
    }, 3000);
  }
  buildTheHome(r, onsuccess, onerror){
    // tslint:disable-next-line: only-arrow-functions
    setTimeout( function() {
      const s = Math.random();
      if (s > 0.5) {
        const room = r * 10;
        console.log('build home successful');
        onsuccess(room);
      } else {
        console.log('build home failed');
        onerror();
      }
    }, 3000);
  }
//not easy to read the caller code
 onClickMe() {
    const that = this;
    this.buildTheWorld(
      (r) => {
        that.buildTheHome(r, 
          // tslint:disable-next-line: only-arrow-functions
          (room) => {
            console.log('My new home has ' + room + ' rooms');
          },
          () => {
            console.log('Homeless again');
          }
        );
      },
      () => {
            console.log('No home without a world');
      }
    );
}
2. Promise can be used to simplify the javascript callback syntax, where, caller sets the success or error callback method on the returned promise object.

Theoretically, when implementing an async method, the logic should not need to care about the callback method information, such as how the result will be processed by caller, that information should be managed by caller, and should not pass into the async method. This is how promise handles the javascript async method call.

Basically, the async method will not accept onsuccess and onerror callback, instead, it returns an promise object to caller. Caller can set callback method to handle the promise result using Promise.then method. The creator of the promise calls resolve or reject to send the async result to the caller for consumption. Once the async result is available, the caller's callback method will be called. In this way, the callback method is limited in caller's scope and never need to be passed into async method as parameters. Then method will return immediately, but it will call the actual result handler until after the result is available in future.

Another benefit of using promise is chaining promise result. A promise handle can return a new promise, and so the then or catch method can be chained to sequentially handle the async request and result. Similar to try/catch, when an error or exception happens, all resolve handler will be skipped until it gets the first error/catch handler to handle the error.

The below sample is the function implemented using promise
  promiseTest() {
    this.buildTheWorldPromise()
    .then(result => {
        console.log('world is built');
        return this.buildTheHomePromise(result);
    })
    .then(r => {
        console.log('home build success with ' + r + ' rooms');
    })
    .catch((e) => {
        console.log(e); // get here if failed to build world or build home
    });
  }
  buildTheWorldPromise(): Promise<any> {
    const p = new Promise((resolve, reject) => {
      setTimeout( () => {
        const r = Math.random();
        if (r > 0.5) {
          console.log('build world successful');
          resolve(r);
        } else {
          console.log('build world failed');
          reject('world is falling');
        }
      }, 3000);
    });
    return p;
  }
  buildTheHomePromise(r): Promise<any> {
    const p = new Promise((resolve, reject) => {
      setTimeout( () => {
        const s = Math.random();
        if (s > 0.5) {
          const room = r * 10;
          console.log('build home successful');
          resolve(room);
        } else {
          console.log('build home failed');
          reject('home is falling');
        }
      }, 3000);
    });
    return p;
  }

3. Await can be used to simplify the code and write synchronous method with promise.
Basically, when a function definition includes async keyword, it tells this method will return a promise, instead of data type specified by the method body's return statement. For example, if an async method returns an integer, then the actual return type to the caller is a promise which wraps a integer data. When the actual data (like a string or integer) is resolved by the promise, that is, when the async method returns from the function, the actual return data will be resolved by promise, and then return to the caller in its then method. 
Within an async function, for any method call that returns a promise, or any async method, add await keyword before the call indicates the call in the current method will be blocked and wait until the promise is fulfilled with actual returned value, either resolved or rejected, before the next line of code gets executed to handle the returned data. As a result, the returned value after await is no longer the promise type returned by calling method, instead, await will get the actual data type fulfilled by the promise. This effectively allow developers to write synchronous code when calling asynchronous method. 
The above sample can be simplified as below using await method
  async awaitTest() {
    try {
     // returned r is actual data, not a promise
      const r = await this.buildTheWorldPromise();
      console.log('world is built: ' + r);
      const h = await this.buildTheHomePromise(r);
      console.log('home build success with ' + h + ' rooms');
    } catch (e) {
      console.log(e);
    }
  }
4. Observable
One issue with promise is it can only handle a single result. If an async operation returns multiple result to caller, observable can be used to handle the async result. 
Observable is implemented by RsJx library as a common pattern. 
Similar to promise, the creator of the observable can call observable.next to send success result to caller, or call observable.error to send failed result to caller, in addition, caller can also call observable.complete to tell caller the async operation is finished. 
Unlike promise, the observable creator can call observable.next multiple times to send multiple result to caller to process. Although it can only call observable.error or observable.complete once.


Monday, August 26, 2019

Update old version Angular package to avoid Angular CLI error

When downloading angular package from git, running 
ng build
on the project 
or 
ng server 
on the demo app may get an error of 

Angular CLI Errorof "The serve command requires to be run in an Angular project, but a project definition could not be found". The reason of the error is the project was created using an old version of Angular CLI with the file name of .angular-cli.json.


To fix the issue, from the folder of .angular-cli.json, run the below command
ng update --all --force 

Thursday, August 22, 2019

javascript sort method does not change order if the return value is not integer

When calling sort method on array with object element, a compare method is passed as parameter to compare each element.

customComparator: (i1, i2)=>{
let ret = i1[fieldToCompare]<i2[fieldToCompare]; //return ret; //this does not work!!! return ret? -1: 1; }

myArray.sort(customCompaartor);

Many languages allow the compare method to return boolean value or integer value. However, for javascript and typescript, the compare method must return an integer, so 

If the result is negative i1 is sorted before i2.
If the result is positive i2 is sorted before i1.
If the result is 0 no changes are done with the sort order of the two values

If a boolean value is returned, no matter it is true or false, the order of the array will not be changed.

Wednesday, August 21, 2019

Chrome javascript debug tip (For Windows platform)

1. To find a javascript or typescript file in the whole source file tree, use
ctrl+p
or 
ctrl+o
and then type part of the file name

2. To search a particular key word in the whole project's js and html source tree, use
ctrl+ shift + f

3. (less useful) run a command
ctrl + shift + p

4. open javascript debugger
F12

5. check the return value of a method
When breaking in a line of code as 
return callSomeFunction(...);
click step over icon, then the return value will show in the debugger's local panel as below


Tuesday, August 20, 2019

Debug Angular library project's typescript code from root application project

Option 1:

The below steps can be used to debug typescript in Angular library project with Angular 8.*

1. Create Angular app
ng new myappwithlib

2. Create library project
ng generate library ui-uploadfile-lib

3. be sure both library and app project build
ng build ui-uploadfile-lib
ng serve

4. update library project to expose some function from library typescript code
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'lib-ui-uploadfile-lib',
template: `
<p>
Load library: {{this.libraryTitle}}
</p>
`,
styles: []
})
export class UiUploadfileLibComponent implements OnInit {
libraryTitle = "my ibrary title";
constructor() { }
ngOnInit() {
}
}
5. import library module to app module file
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import {UiUploadfileLibModule} from 'ui-uploadfile-lib'
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
UiUploadfileLibModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

6. (Important) in application's root folder, update angular.json file's application serve setting (not library project's build settings) as below
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"sourceMap": {
"scripts": true,
"styles": true,
"vendor": true
},
"browserTarget": "myappwithlib:build"
},
"configurations": {
"production": {
"browserTarget": "myappwithlib:build:production"
}
}
},
7. start app by executing 
ng serve

In browser's javascript debugger page as shown below. Depends on project settings, the typescript code may also show up in localhost:4200/ng: section, so be sure to search the source tree to try to locate the typescript code.



6. Options 2
If you only need to debug some javascript/typescript code from a third party npm package, and do not need to maintain the code, then there is also another easy and quick way to do so. Just copy the source code from the third party package into the src folder of your angular project, and then search and replace all import statement in your js/ts files so as to import the third party package's classes and modules from the local copy of the source files. 

You can leave the project's package.json as it is, the downloaded package will just be ignored.

Monday, August 19, 2019

Bootstrap grid layout note

1.  The column with definition is automatically inherited from small class to large class, so if col-sm is defined, md, lg and xml will automatically inherit the column size setting
  • .col- (phone - screen width less than 576px)
  • .col-sm- (phone in landscape - screen width equal to or greater than 576px)
  • .col-md- (Tablet - screen width equal to or greater than 768px)
  • .col-lg- (Laptop - screen width equal to or greater than 992px)
  • .col-xl- (Desktop - screen width equal to or greater than 1200px)
2. If row is embedded within col, then the col content will be the first row. and the new row will start from second line. The new row width is the width of the parent column width.
3. row can only contain col as sub items.
4. if more than 12 cols are added in a row, it will wrap to a new line after the first 12 cols
5. col with number will automatically share the row width equally
6. col-auto will take up the remaining width
7. if col is  not defined for col or col-xs, then each column will automatically stacked horizontally.

Tuesday, August 13, 2019

Data binding in Angular

There are several ways to handle data binding in Angular framework:

1. string interpolation
Using {{ }} to include a component property or expression in html markup, Angular will convert the property into a string, and show it in the html page. Note string interpolation only works in normal html text, not in element attribute.
<p>The sum of 1 + 1 is not {{myProperty}}.</p>

2. element property binding
Using [] with an html element attribute name to dynamically binding the html element attribute to a component's property, so that the value is come from the component's property at runtime. For example, in the below example, the image src is set to Angular component's itemImageUrl property's value.

<img [src]="itemImageUrl2"> 

Comparing to this, if the html attribute is not surrounded by [], then it means the attribute value is set to the hardcoded value of the string. For example
<img src="https://www.gstatic.com/webp/gallery3/1.png">    

Angular can recognize [] in the element attribute, so then handle anything included in "" as typescript code.


3. element event binding
Using () to include a html element event name, and set it to value to a method call in quotation mark.
 <button (click)="onButtonClicked()">Delete</button>
If the event takes any event parameter, add $event as parameter in calling method.

4. two way binding with ngModel
NgModel directive attribute depends on FormsModule for Angular template based form handling.

when nModel is used for a standalone control, it allows one way (using [] syntax) or two way binding ([()] syntax) for html input element  Only input element types (Input, select, textarea) or custom element implemented ControlValueAccessor interface can use ngModel for two way binding, as the property is binding to the html element's value property as default.
In addition to binding to ts property, [(ngModel)] can also bind to item or item's property got from ngFor loop.

To use two way binding, must import FormsModule in the app module as below.

import { FormsModule } from '@angular/forms';

imports: [
BrowserModule,
FormsModule
],

Html template file:
<input [(ngModel)]="myname" id="example-ngModel">
Web component ts file:
export class FormComponent implements OnInit {
  myname= 'default name';

Note, when using ngModel for html element inside a form, it requires a name attribute, and ngForm will synchronize the html element with component class, so there is no need to setup oneway or two way binding.

If local reference variable is not assigned to value of 'ngModel', then when passing the local variable as a method parameter to ts code, the type of parameter in ts code is HtmlInputElement. If the local reference variable is assigned to the value of 'ngModel', then the type of the parameter in ts code is NgModel
  <div><input type="text" #local1></div>
  <div><input type="text" ngModel #local2="ngModel"></div>
  <ion-button (click)="onCreateOffer(local1, local2)">

ts code:
  onCreateOffer(l1: HTMLInputElement, l2: NgModel) {
    console.log("create offer place", typeof(l1), typeof(l2) );
  }

5. style binding
A element's style can  bind to a property or method's return value as shown below
<p [ngStyle]="{backgroundColor: getColor()}">servers works!</p>

In component typescript file, getColor method is defined as
getColor() {
return this._color;


6. Conditional style class binding
A element's style class can bind to a class conditionally based on property or method return value as shown below. If getColor() returns 'red', then the online style class will be applied to the button element.
<button [ngClass]="{online:getColor()=='red'}" [disabled]="!allowNewServer" class="btn btn-primary" (click)="onCreateServer()">my button</button>

The style class is defined as 
@Component({
selector: 'app-servers',
templateUrl: './servers.component.html',
styles: [`
.online {
color: pink;
}`]
})


7. local reference passed as template html method parameter
In html template, local reference variable can be used to pass the html element or Angular web component as parameter to method call. The below sample pass html input type and SecondComponent as parameter to a js method.

Note, template reference variable are only visible within the template definition scope, as ngIf, ngFor will create a separate template block, so template reference variables defined within ngIf or ngFor clause are not visible to js code outside of the ngIf or ngFor clause. In this case, ViewChild can be used as an alternative solution.

When reference input element

app component html template:
<input type="text" #local1>
<app-second #local2></app-second>
<button class="btn btn-primary" (click)="onMyClick(local1, local2)">button</button>

app Component typescript:
export class AppComponent {
title = 'local';


onMyClick(l1, l2) {
console.log('on click called');
console.log(l1.value);
console.log(l2.getSecondValue()); //getSecondValue is a method in SecondComponent angular web component
}
}


Second Component typescript:

export class SecondComponent implements OnInit {
constructor() { }
ngOnInit() {
}

getSecondValue(){
return 'value from second component';
}
}

8. @ChildView  
@ChildView can let typescript code directly reference the web components.

@ChildView method parameter must indicate which html element is associated with the typescript variable. The below are two types can be specified by @ViewChild query parameter

Html element: 
Single quotation mark is used around the local reference name when specifying a viewChild variable.

app component html file
<input type="text" class="form-control" #local1>
<app-second #local2></app-second>
<button class="btn btn-primary" (click)="onMyClick(local1, local2)">button</button>
<button class="btn btn-primary" (click)="onClick()">button2</button>

Web component typescript file
export class AppComponent {
@ViewChild('local1', {static: true}) local1;
@ViewChild('local2', {static: true}) local2;

onClick(){
console.log(this.local1 + ', ' + this.local1.nativeElement.value);
console.log(this.local2 + ', ' + this.local2.getSecondValue());
}
}

8.2 web component class name
html file
<input type="text" class="form-control" >
<app-second ></app-second>
<button class="btn btn-primary" (click)="onMyClick(local1, local2)">button</button>
<button class="btn btn-primary" (click)="onClick()">button2</button>

web component file
@ViewChild(SecondComponent, {static: true}) local2;


onClick(){
console.log(this.local2 + ', ' + this.local2.getSecondValue());
}