Thursday, October 31, 2019

EventEmitter, Promise, Observable, Subject, BehaviorSubject difference

In Angular project, notification information can be passed from sender's web component to receiver's web component through eventEmitter, Observable and Subject. Depends on the requirement, different type should be selected.

Assuming in sender's web component, when a button is clicked, a new stock price should be updated and received by receiver's web component. The logic of managing the notification message is implemented in a separate service component

1. using EventEmitter

The messaging service should be registered in app.component.ts as a service provider
import { EventEmitter } from '@angular/core';
export class MessagingService {
    stockEventEmitter = new EventEmitter<number>();
}
sender.ts
import { MessagingService } from '../messaging/messaging.service';
export class Cmp1Component implements OnInit {
  stockPrice = 100;
constructor(public activatedRoute: ActivatedRoute, public router: Router, private messaging: MessagingService  ) 
{ }

onUpdateStockPrice(priceChange: number) {
    this.stockPrice = this.stockPrice + priceChange;
    this.messaging.stockEventEmitter.emit(this.stockPrice);
  }
}


Receiver.ts
import { MessagingService } from '../messaging/messaging.service';
export class AppComponent implements OnInit, OnDestroy {
  stockPrice: number;
  constructor(private router: Router,
              public activatedRoute: ActivatedRoute,
              private messaging: MessagingService) {
  }
  ngOnInit() {
    this.messaging.stockEventEmitter.subscribe( price => {
      this.stockPrice = price;
    });
  }
For eventEmitter, the event source is shared by all subscribers, all subscribers are passive listeners, and the event source decides when to send the updated event data to the listeners.
In addition, eventEmitter can only emits the same data type defined in emitter constructor, and does not provide ways to allow sender tells receiver if error happens, or if this messaging operation is ended.  This issue can be solved using Angular Subscribe.
2. Promise

Similar to event, promise pushes data to listener when it is ready, so listener does not need pull the data by itself. However, a promise instance cannot be shared by multiple listeners, each promise object can only server one listener through the registered callback method, it works like a regular asynchronous function call. 
Besides returning the normal successful response, promise can also return error result to receiver.

    const p = new Promise((resolve, reject) => {
      console.log('Promise operation starts');
    
      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');
     }
  } );

 p.then(r => {
        console.log('home build success with ' + r + ' rooms');
 })
 .catch((e) => {
        console.log(e);
 });
The promise constructor method starts when creating the promise object, even if there is no next method registered to get the result. 
3. Observable
Observable is similar to promise, but the observer can get multiple result set from observable. Actually all promise object can be converted to observable. The observable provides next, error and complete method for sender and receiver to handle the success, error and complete status. 
Although multiple observers can subscribe to the same observable instance, each observer will create its own observable instance by calling the obserable costructor, so unlike event emitter, there is not a shared data source to serve multiple subscribers.
Unlike promise, an observable will only start its life cycle when subscriber subscribes to it. At that moment, observable will execute its constructor method to start emitting data. Its life ends when subscriber calls unsubscribe method, or when observable emits error or complete data.
When used as a messaging service, both the observable object for pushing data out, and the subscriber for receiving data need to be exposed to public. It would be better to if a single object is exposed, which can be used by both sending and receiving the sender and receivers to broadcast and receive messages. This can be done by Angular Subject object.
messaging.service.ts
import { EventEmitter } from '@angular/core';
import { Observable, Subscriber } from 'rxjs';
export class MessagingService {
    stockObservable: Observable<number>; // for receiver to subscribe the notification
    stockSubscriber: Subscriber<number>; // for sender to send notification
    constructor() {
        this.stockObservable = new Observable( (subscriber: Subscriber<number>) => {
            this.stockSubscriber = subscriber;
        });
    }
}

sender.ts 
import { MessagingService } from '../messaging/messaging.service';
import { Subscriber } from 'rxjs';
export class Cmp1Component implements OnInit {
  constructor(public activatedRoute: ActivatedRoute, public router: Router, private messaging: MessagingService  ) { }

  onUpdateStockPriceByObservable(priceChange: number) {
    this.stockPrice = this.stockPrice + priceChange;
    this.messaging.stockSubscriber.next(this.stockPrice);
  }
  onErrorStockPriceByObservable() {
    this.messaging.stockSubscriber.error('error happened in stock price update');
  }
  onCompleteStockPriceByObservable() {
    this.messaging.stockSubscriber.complete();
  }
}
receiver.ts
import { intervalSubscription } from 'rxjs';
import { MessagingService } from '../messaging/messaging.service';

export class AppComponent implements OnInit, OnDestroy {
  private stockObservableSub: Subscription;
  constructor(private router: Router,
              public activatedRoute: ActivatedRoute,
              private messaging: MessagingService) {
  }
  ngOnInit() {
    this.stockObservableSub = this.messaging.stockObservable.subscribe( price => {
      this.stockPrice = price;
    }, error => {
      this.stockPrice = -1;
      this.stockPriceError = error;
    }, () => {
      this.stockPrice = -1;
      this.stockPriceError = 'observable completed!';
    }
    );
  }
  ngOnDestroy(): void {
    this.sub.unsubscribe();
    this.eventSub.unsubscribe();
    this.stockObservableSub.unsubscribe();
  }
}

3. Subject
Subject is a special type of observable, and can broadcast multiple data set to multiple subscribers. This is similar to event emitter. 
Subject is both functions to allow multiple subscribers to subscribe to it, and function to emit new data.
One feature missed in regular Subject object is it is not sticky. So after a listener registers a Subject, it will not automatically get the last value sent by the Subject, until the next time when Subject sends new message to listeners. This may not work in all scenarios, for example, as a stock price listener, when a receiver registers to get the price for a stock, it should immediately receive the last price broadcast by sender, without having to wait to get any value until the next stock price update. In this case, BehaviorSubject can be used to receive the initial default value sent by sender.

messging.service.ts
import { EventEmitter } from '@angular/core';
import { Observable, Subscriber, Subject } from 'rxjs';
export class MessagingService {
    stockSubject = new Subject<number>();
}
sender.ts
import { Component, OnInit } from '@angular/core';
import { MessagingService } from '../messaging/messaging.service';
import { Subscriber } from 'rxjs';
export class Cmp1Component implements OnInit {
  constructor(public activatedRoute: ActivatedRoute, public router: Router, private messaging: MessagingService  ) { }
  onUpdateStockPriceBySubject(priceChange: number) {
    this.stockPrice = this.stockPrice + priceChange;
    this.messaging.stockSubject.next(this.stockPrice);
  }
  onErrorStockPriceBySubject() {
    this.messaging.stockSubject.error('error happened in stock price subject');
  }
  onCompleteStockPriceBySubject() {
    this.messaging.stockSubject.complete();
  }
}
receiver.ts
import { Component, ViewEncapsulation, OnInit, OnDestroy } from '@angular/core';
import { interval, Subscription } from 'rxjs';
import { MessagingService } from '../messaging/messaging.service';
export class AppComponent implements OnInit, OnDestroy {
  private stockSubjectSub: Subscription;
  stockPrice: number;
  stockPriceError: string;
  constructor(private router: Router,
    public activatedRoute: ActivatedRoute,
    private messaging: MessagingService) {
  }
  ngOnInit() {
    this.stockSubjectSub = this.messaging.stockSubject.subscribe(price => {
      this.stockPrice = price;
    }, error => {
      this.stockPrice = -1;
      this.stockPriceError = error;
    }, () => {
      this.stockPrice = -1;
      this.stockPriceError = 'subject completed!';
    }
    );
  }
}
4. BehaviorSubject, ReplaySubject
BehaviorSubject is a special Subject, it allows to set an initial value in constructor, so there is always a value to be sent to subscriber when subscribing to a behaviorSubject. If next method is called on subscriber later, then the new value will be read by the observer's next method.
messaging.ts
import { EventEmitter } from '@angular/core';
import { Observable, Subscriber, Subject, BehaviorSubject } from 'rxjs';
export class MessagingService {
    stockBehaviorSubject = new BehaviorSubject<number>(1000);
}

ReplaySubject enables subscriber to specify how many old value, or how long of time should be cached and replayed to the new subscriber.

AsyncSubject is a subject where only the last value of the Observable is sent to its observers, and only when the execution completes.

Tuesday, October 29, 2019

Manage multiple git accounts for github authentication on Windows 10 / Mac

By default, Windows 10 manages git authentication username and password automatically without you to configure the username and password from git configuration. The credential is saved in Windows credential store, and can be accessed from Control Panel->User Account->Manage Your Credentials->Windows Credentials.

As a result, when checking the username and password information from git configuration by running
git config --list
or
git config --global --list
You will not see the git username and password information there.

One problem is, Windows only allows to store a single username and password for your github credential, if you have more than one github accounts, then in order to switch to different git account, you will have to update the username and password from Windows Control panel's Manage your credentials page. (open from: Control Panel -> Credential Manager ->Windows Credentials)

On Mac, the git account information is saved in keychain tool. To switch between different git user account, first open KeyChain tool, In Category section, select all items. Then input "github" in top right search text field, and select the matched item for githbu, and update the user name and password for the record.

Actually, there is simple way to store and use multiple git accounts on a single windows box by following the below steps:
1. run
git config --global credential.github.com.useHttpPath true
from command line
2. open Windows control panel->Credential Manager->Windows Credentials and delete the saved github.com credential if existing.


Note, Github is about to deprecate password authentication, and will replace the password with personal token. If you use gitbut's username/password to log, then you will need to replace the password with personal token in your saved github credential in windows or mac.
To do so, first go to github's "Settings->Developer settings->Personal access tokens" web page to generate a new personal access token, and then copy the generated token, and replace the saved  password with the personal token. The username/personal token can be used for authentication in the same way as username/password. 

Friday, October 18, 2019

Angular css style and view encapsulation

For angular project, the style defined in component's css file is encapsulated for the current component, so the style does not apply to the global scope, or the indirect children components contained within the current component.

For example, appComponent has the below html and css definition

html:
<p>The app component title</p>
<p myattribute>the app component content</p>
<div class="container" style="background-color:yellow;">
  <div class="row">
    <div class="col-sm" >
      <app-cmp1></app-cmp1>
    </div>
    <div class="col-sm">
      <app-cmp2></app-cmp2>
    </div>
  </div>
</div>
css:
p {
    background-color: green;
    font-weight: bold;
}
p[myattribute] {
    background-color: pink;
}
Then when the app is running on browser, the defined css style will automatically be added an attribute selector as 

p[_ngcontent-gio-c0] {
    background-color: green;
    font-weight: bold;
}

p[myattribute][_ngcontent-gio-c0] {
    background-color: pink;
}

where _gncontent_gio-c0 is the random attribute added to the elements defined in appComponent.htm. The runtime element of appComponent looks like as below:
Note every elements defined in appcomponent.html has the new ng attribute of _ngContent-gio-c0, so the defined css style in appcomponent.css can be applied to them.
Similarly, the child element of app-cmp1 element has _ngcontent_gio_c1 defined on it. as a result, any indirect html elements defined in app-cmp1 will not inherit the css style defined in the parent components of appcomponent, as they have a different attribute generated by angular..

Css style files added into compoent's styleURls will always has this ng style attribute selector added for it when inserting the styles into index.html. However, styles files added in component.html will be handled differently depending on whether full url or relative url are used to in style link's href url. If relative url (without http(s) scheme) is used, then the style will have ng attribute selector.

<link rel="stylesheet" href="assets/mystyles.css">

If absolute url (with http(s) scheme) is used, then the style will not have ng generated attribute, so the style will be applied globally, and may affect other elements by accident.
<link rel="stylesheet" href="http://localhost:4200/assets/mystyles.css">
There are two additional ways to add external css style files to angular project:
1. set global css style by adding the css style url in the project's styles.css file, as mentioned in its comment of  "/* You can add global styles to this file, and also import other style files */

2. in the web component's css file, add the external css file with @import statement as shown below:
@import "https://maxcdn.bootstrapcdn.com/bootstrap/4.3.0/css/bootstrap.min.css";

Now the question come, how can we set the style to the html elements of a web component from the holding html page of the web component. For example, how to set style of p element of app-cmp1 from root appComponent.html and its css file. The easy way to do so is applying ::ng-deep to styles. ::ng-deep will remove the ng attribute selector when adding css style at runtime, so it makes the style global available. Note ng-deep is marked as deprecated, although no alternative available for now.
::ng-deep p {
    background-color: pink;
}
Other than ::ng-deep, another option is using component module configuration's encapsulation settings. 
ViewEncapsulation.ShadowDom 
ViewEncapsulation.None
which will allow you to set the global css style from your component's css code. ShodowDom is a better option without the need to generate the random angular attribute id, but it is not supported by all browser, particularly by IE. When using ViewEncapsulation.None, be sure to limit the scope of the css style to be applied, so it will not affect other elements by accident.

One solution of using ViewEncapsulation.None without polluting the angular project's global css style is, using the regular css style for general web component css styles. But creating a separate web component with empty html element, and special css styles that need to be applied globally, so that only a very limited css styles are exposed by this dummy web component.

Note:
1. external css url (from remote cdn) starting with http or https  cannot be directly added into component's ts file's styleUrls list. If external css style should only be applied to a particular web component, then the recommended way is importing the whole npm package (like bootstrap or material design) into the project, and then add the css file from local relative path into the component's style files. For example, the below code in a webcomponent's css file import another css file from a different package (from @ng-select). In this way, when loading the css style file, it will have the angular generated attribute, so those css style will not be applied globally.
@import "~@ng-select/ng-select/themes/default.theme.css";
@media screen and (max-width: 769px) {
    .container-size{
        margin: 1.2em;
    }
}
2. when external css file loaded from web component's html file's as css link reference, the css styles are added into DOM tree without angular random id. The reason of why the style definition can still be applied to web component's html elements is, those styles are applied globally to all elements, no matter the elements have the angular generated attribute or not.

Friday, October 11, 2019

Notes about angular theme style

1. Can default npm bootstrap package work with angular project?
The default npm package published by https://getbootstrap.com/ can be installed by
npm install bootstrap

Or skip the npm installation and directly include the css style and javascript file in the html file as 
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> 
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script

The default npm package's package.json shows it depends on jquery library. The jQuery depended by  default bootstrap implementation has conflict with angular, as jquery is not compatible with angular project due to the different way to render the DOM element.

So default bootstrap npm package cannot be used by angular project directly. 

If the angular project only needs the bootstrap css style without jQuery dependency, then the angular project can just include the bootstrap in its css style, and the function works as expected. 

But if the angular project depends on bootstrap's javascript dependency, then it will need to use third party library to support bootstrap without jQuery's dependency, such as ngx-bootstrap.

2. What bootstrap components requires javascript library?
Please see https://getbootstrap.com/docs/4.0/getting-started/introduction/ for bootstrap components that requires javascript dependency, they include
  Alerts for dismissing
  Buttons for toggling states and checkbox/radio functionality
  Carousel for all slide behaviors, controls, and indicators
  Collapse for toggling visibility of content
  Dropdowns for displaying and positioning (also requires Popper.js)
  Modals for displaying, positioning, and scroll behavior
  Navbar for extending our Collapse plugin to implement responsive behavior
  Tooltips and popovers for displaying and positioning (also requires Popper.js)
  Scrollspy for scroll behavior and navigation updates

3. what is ng-bootstrap and ngx-bootstrap npm package?
ng-bootstrap and ngx-bootstrap npm package were created to use bootstrap function in angular project without depending on jQuery.
ngx-bootstrap is a new version of ng-bootstrap, so it should be used in the new angular project.
As ngx-bootstrap only replaces default bootstrap package's jquery part with its own javascipt library, so ngx-bootstrap still uses the same css style definition bt default bootstrap library. 

That is why when using ngx-bootstrap package, the CND for the css style file link is same as default bootstrap css CDN link.
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
as show in https://valor-software.com/ngx-bootstrap/#/documentation#getting-started 
4. Careful about bootstrop css version
When using bootstrap css style in angular project to render html elements, it is important to match the element style settings with the matched bootstrap versions. As different major version of bootstrap requires different style settings. The angular project will not render properly when using 4.x.x css style with 3.x.x html element settings.


5. Difference between bootstrap and material for angular library
Both bootstrap and material for angular are css styles for html ui component, and both of them can be used for angular project. 
Bootstrap is developed by Twitter, and has a bigger user base. 
Material for angular is developed by angular team and is better integrated with angular project. Bootstrap and Material for angular can be used in the same angular project.

Wednesday, October 2, 2019

Dependency setting in package.json for angular library project and app project

dependencies:
dependencies node defined in package.json will be installed in application's node_modules folder, and used by the library and application project at runtime. There is no difference between library project and application project.

devDependencies:
modules specified in devDependencies is mostly used for compiling and building the library project, so only developers of the app or library need them. For library project, the devDependencies node should only exist in root application's package.json, and should not exist in library project's package.json, so that those information will not be exposed to the library's output package.json, as users of the library do not need to know or care about those modules.

peerDependencies:
peerDependencies node is mostly used by library projects, the packages specified in peerDependencies will not be installed when running "npm package" on the library project, the information specified in peerDependencies node only indicates which version of dependencies packages are required by the library project at runtime, and the host application's package.json must include the related packages with compatible versions, otherwise an error will be reported for missing the required peerDependent packages.

whitelistedNonPeerDependencies:
To avoid developers to add a package in dependencies section by accident, (instead of adding it into peerDependencies section), angular build will fail by default if it finds a such package.
If developers are certain a dependent package should be added into dependencies section instead of peerDependencies section, then developers need to explicitly add the package into ng-package.json's whitelistNonPeerDependencies section to avoid the build failure. Adding a package in whitelistedNonPeerDependencies indicates the developers are certain that a separate copy of the package should be loaded only for the current library.