• ApplicationRef - applicationRef.tick ?

  • ChangeDetectorRef - change detectection

    @ViewChildren(AuthMessageComponent) authMessageComponent: QueryList<AuthMessageComponent>

    constructor(private cd: ChangeDetectorRef) {}

    ngAfterViewInit(): void {
    console.log(this.email);

    if (this.authMessageComponent) {
    this.authMessageComponent.forEach((message) => {
    message.days = 30;
    });

    this.cd.detectChanges();
    }
    }
  • ComponentRef

    @ViewChild('entry', { read : ViewContainerRef }) entry: ViewContainerRef

    authComponent: ComponentRef<AuthFormComponent>;

    constructor(private resolver: ComponentFactoryResolver) {
    }

    ngAfterContentInit(): void {
    const authFormFactory = this.resolver.resolveComponentFactory(AuthFormComponent);
    this.authComponent = this.entry.createComponent(authFormFactory);
    this.authComponent.instance.title = 'Create account';
    this.authComponent.instance.submitted.subscribe(this.loginUser);
    }

    loginUser(user: User) {
    console.log('Login', user);
    }

    destoryComponent() {
    console.log(this.authComponent);
    }
  • ElementRef - html element, Test

    @ViewChild('#email') email: ElementRef;
    @ViewChildren('noTrackBy') heroesNoTrackBy: QueryList<ElementRef>;

    // custom directive
    constructor(el: ElementRef) {
    el.nativeElement.style.backgroundColor = 'powderblue';
    }

    import { Directive, ElementRef, HostListener, Input } from '@angular/core';

    @Directive({
    selector: '[appHighlight]'
    })
    export class HighlightDirective {

    @Input('appHighlight') highlightColor: string;

    private el: HTMLElement;

    constructor(el: ElementRef) {
    this.el = el.nativeElement;
    }

    @HostListener('mouseenter') onMouseEnter() {
    this.highlight(this.highlightColor || 'cyan');
    }

    @HostListener('mouseleave') onMouseLeave() {
    this.highlight(null);
    }

    private highlight(color: string) {
    this.el.style.backgroundColor = color;
    }
    }
  • EmbeddedViewRef

  • forwardRef - DI in Action

  • TemplateRef - acquire the contents, structual-directives

    <ng-template #tmpl>
    Tott Motto: England UK
    </ng-template>
    <div #entry></div>
@ViewChild('entry', { read : ViewContainerRef }) entry: ViewContainerRef
@ViewChild('tmpl') tmpl: TemplateRef<any>;

this.entry.createEmbeddedView(this.tmpl);

this.viewContainerRef.createEmbeddedView(this.templateRef);
  • NgModuleRef

  • PlatformRef - Test

  • resolveForwardRef

  • ViewRef

  • ViewContainerRef - dynamic component

    let viewContainerRef = this.adHost.viewContainerRef;
    viewContainerRef.clear();

    let componentRef = viewContainerRef.createComponent(componentFactory);
    (<AdComponent>componentRef.instance).data = adItem.data;


    import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, AfterContentInit } from '@angular/core';
    import { User } from './auth-form/auth-form.interface';
    import { AuthFormComponent } from './auth-form/auth-form.component';

    @Component({
    selector: 'app-root',
    template: `
    <div>
    <div #entry></div>
    </div>
    `
    })
    export class AppComponent implements AfterContentInit {
    @ViewChild('entry', { read : ViewContainerRef }) entry: ViewContainerRef

    constructor(private resolver: ComponentFactoryResolver) {
    }

    ngAfterContentInit(): void {
    const authFormFactory = this.resolver.resolveComponentFactory(AuthFormComponent);
    const component = this.entry.createComponent(authFormFactory);
    }

    loginUser(user: User) {
    console.log('Login', user);
    }
    }

Injector

A service like HeroService is just a class in Angular until you register it with an Angular dependency injector.

An Angular injector is responsible for creating service instances and injecting them into classes like the HeroListComponent.

You rarely create an Angular injector yourself. Angular creates injectors for you as it executes the app, starting with the root injector that it creates during the bootstrap process.
Injector
Provider ————-> Service Instance

Angular doesn’t automatically know how you want to create instances of your services or the injector to create your service. You must configure it by specifying providers for every service.

Providers tell the injector how to create the service. Without a provider, the injector would not know that it is responsible for injecting the service nor be able to create the service.

There are many ways to register a service provider with an injector. This section shows the most common ways of configuring a provider for your services.

Register Provider:

@Injectable providers

@Injectable configure a provider for those services.

import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root',
})
export class HeroService {
constructor() { }
}

providedIn tells Angular that the root injector is responsible for creating an instance of the HeroService (by invoking its constructor) and making it available across the application.

In the following excerpt, the @Injectable decorator is used to configure a provider that will be available in any injector that includes the HeroModule.

import { Injectable } from '@angular/core';
import { HeroModule } from './hero.module';
import { HEROES } from './mock-heroes';

@Injectable({
// we declare that this service should be created
// by any injector that includes HeroModule.

providedIn: HeroModule,
})
export class HeroService {
getHeroes() { return HEROES; }
}

@NgModule providers

providers: [
UserService,
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
],

The first entry registers the UserService class (not shown) under the UserService injection token. The second registers a value (HERO_DI_CONFIG) under the APP_CONFIG injection token.

@Component providers

In addition to providing the service application-wide or within a particular @NgModule, services can also be provided in specific components. Services provided in component-level is only available within that component injector or in any of its child components.

import { Component } from '@angular/core';
import { HeroService } from './hero.service';

@Component({
selector: 'app-heroes',
providers: [ HeroService ],
template: `
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
`
})
export class HeroesComponent { }

@Injectable, @NgModule or @Component

  • @Injectable
    Tree shakable

  • @NgModule.providers
    registered with the application’s root injector. Angular can inject the corresponding services in any class it creates. Once created, a service instance lives for the life of the app and Angular injects this one service instance in every class that needs it.

You’re likely to inject the UserService in many places throughout the app and will want to inject the same service instance every time. Providing the UserService with an Angular module is a good choice if an @Injectable provider is not an option..

To be precise, Angular module providers are registered with the root injector unless the module is lazy loaded. In this sample, all modules are eagerly loaded when the application starts, so all module providers are registered with the app’s root injector.

  • @Component.providers

A component’s providers (@Component.providers) are registered with each component instance’s own injector.

Angular can only inject the corresponding services in that component instance or one of its descendant component instances. Angular cannot inject the same service instance anywhere else.

Providers

  • The provide object literal
providers: [Logger]

shorthand expressioin using a provider literal with two properties:

[{ provide: Logger, useClasss: Logger }]
  • Alternative class providers
[{ provide: Logger, useClass: BetterLogger }]
@Injectable()
export class EvenBetterLogger extends Logger {
  constructor(private userService: UserService) { super(); }

  log(message: string) {
    let name = this.userService.user.name;
    super.log(`Message to ${name}: ${message}`);
  }
}
[ 
    UserService,
    { provide: Logger, useClass: EvenBetterLogger }
]
  • Aliased class providers
    [ NewLogger,
      // Not aliased! Creates two instances of `NewLogger`
      { provide: OldLogger, useClass: NewLogger}]
[ NewLogger,
  // Alias OldLogger w/ reference to NewLogger
  { provide: OldLogger, useExisting: NewLogger}]
  • Value providers
export function SilentLoggerFn() {}

const silentLogger = {
  logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
  log: SilentLoggerFn
};
[{ provide: Logger, useValue: silentLogger }]
  • Factory providers
    hero.service.ts
    constructor(
      private logger: Logger,
      private isAuthorized: boolean) { }
    
    getHeroes() {
      let auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
      this.logger.log(`Getting heroes for ${auth} user.`);
      return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
    }

hero.service.provider.ts

let heroServiceFactory = (logger: Logger, userService: UserService) => {
  return new HeroService(logger, userService.user.isAuthorized);
};

export let heroServiceProvider =
  { provide: HeroService,
    useFactory: heroServiceFactory,
    deps: [Logger, UserService]
  };
  • Creating Tree-shakeable providers
    @Injectable({
      providedIn: 'root',
      useFactory: () => new Service('dependency'),
    })
    export class Service {
      constructor(private dep: string) {
      }
    }

Injection Token

import { InjectionToken } from '@angular/core';
// export const TOKEN = new InjectionToken('desc');

export const TOKEN = 
  new InjectionToken('desc', { providedIn: 'root', factory: () => new AppConfig(), })

const TOKEN = 
  new InjectionToken('tree-shakeable token', 
    { providedIn: 'root', factory: () => 
        new AppConfig(inject(Parameter1), inject(Paremeter2)), });
        
export const HERO_DI_CONFIG: AppConfig = {
  apiEndpoint: 'api.heroes.com',
  title: 'Dependency Injection'
};
constructor(@Inject(TOKEN));

CQS

command query separation

a method should only have been responsible for executing a command

against an object or it should be responsible for retrieving information from real objects query

you should never do both.

CQRS

command query responsibility separation or segregation

you should have a separate object for your commands

and a separate object for your queries

 COMMANDS <- CLIENT <------------
    |                           |
    V                           |
DISPATCHER                      |
    |                           |
    V                         VIEW
 HANDLER                        ^
    |                           |
    V                         READ
  DOMAIN                    DATABASE
    |                           ^
    V                           |
REPOSITORY                  PROJECTION  
    |                           ^
    V                           |
  EVENTS  -> EVENT STORE -> PROJECTION
                             MANAGER
```
ADMIN PATIENT (Command)
      |
      V

———-DISPATCHER————-
| | |
| | |
V V V
ADMIT TRANSFER DISCHARGE
HANDLER HANDLER HANDLER



ADMIN HANDLER
|
V ——————————————
ENCOUNTER |AGGREGATE |
^ |Events + 1 |—————————————|
|Events| |LIST VERSION APPLY |
| V ——————————————
REPOSITORY



 PATIENT
 ADMITTED
    |
    V
PROJECTION  <----- CHECKPOINT
 MANAGER    ----->
    |
    V
 WARD VIEW
PROJECTION
    |
    V
 WARD VIEW

```

EVENTUAL CONSISTENCY

Things need to do

  1. Validing command is correct.

https://www.youtube.com/watch?v=8JKjvY4etTY
https://www.youtube.com/watch?v=holjbuSbv3k

Function Declarations vs. Function Expressions

Question 1:

function foo(){
function bar() {
return 3;
}
return bar();
function bar() {
return 8;
}
}

console.log(foo()); // 8

Question 1 uses function declarations which means they get hoisted.

“Function declarations and function variables are always moved (‘hoisted’) to the top of their JavaScript scope by the JavaScript interpreter.”

When a function declaration is hoisted the entire function body is lifted with it, so after the interpreter has finished with the code in Question 1 it runs more like this:

//**Simulated processing sequence for Question 1**
function foo(){
//define bar once
function bar() {
return 3;
}
//redefine it
function bar() {
return 8;
}
//return its invocation
return bar(); //8
}
alert(foo());

Question 2

function foo(){
var bar = function() {
return 3;
};
return bar();
var bar = function() {
return 8;
};
}
alert(foo());

Do Function Expressions get Hoisted too?

That depends on the expression.

var bar = function() {
return 3;
};

The left hand side (var bar) is a Variable Declaration. Variable Declarations get hoisted but their Assignment Expressions don’t. So when bar is hoisted the interpreter initially sets var bar = undefined. The function definition itself is not hoisted.

Thus the code in Question 2 runs in a more intuitive sequence:

//**Simulated processing sequence for Question 2**
function foo(){
//a declaration for each function expression
var bar = undefined;
var bar = undefined;
//first Function Expression is executed
bar = function() {
return 3;
};
// Function created by first Function Expression is invoked
return bar();
// second Function Expression unreachable
}
console.log(foo()); //3

Question 3

console.log(foo());
function foo(){
var bar = function() {
return 3;
};
return bar();
var bar = function() {
return 8;
};
}
function foo(){
//a declaration for each function expression
var bar = undefined;
var bar = undefined;
//first Function Expression is executed
bar = function() {
return 3;
};
// Function created by first Function Expression is invoked
return bar();
// second Function Expression unreachable
}
alert(foo());

Question 4

function foo(){
return bar();
var bar = function() {
return 3;
};
var bar = function() {
return 8;
};
}
alert(foo());
//**Simulated processing sequence for Question 4**
function foo(){
//a declaration for each function expression
var bar = undefined;
var bar = undefined;
return bar(); //TypeError: "bar not defined"
//neither Function Expression is reached
}
alert(foo());

RxJS 入门指引和初步应用

https://zhuanlan.zhihu.com/p/25383159

RxJS提供了各种API来创建数据流:

单值:of, empty, never
多值:from
定时:interval, timer
从事件创建:fromEvent
从Promise创建:fromPromise
自定义创建:create
创建出来的数据流是一种可观察的序列,可以被订阅,也可以被用来做一些转换操作,比如:

改变数据形态:map, mapTo, pluck
过滤一些值:filter, skip, first, last, take
时间轴上的操作:delay, timeout, throttle, debounce, audit, bufferTime
累加:reduce, scan
异常处理:throw, catch, retry, finally
条件执行:takeUntil, delayWhen, retryWhen, subscribeOn, ObserveOn
转接:switch
也可以对若干个数据流进行组合:

concat,保持原来的序列顺序连接两个数据流
merge,合并序列
race,预设条件为其中一个数据流完成
forkJoin,预设条件为所有数据流都完成
zip,取各来源数据流最后一个值合并为对象
combineLatest,取各来源数据流最后一个值合并为数组

在RxJS中,存在这么几种东西:

Observable 可观察序列,只出不进
Observer 观察者,只进不出
Subject 可出可进的可观察序列,可作为观察者
ReplaySubject 带回放
Subscription 订阅关系

前三种东西,根据它们数据进出的可能性,可以通俗地理解他们的连接方式,这也就是所谓管道的“形状”,一端密闭一端开头,还是两端开口,都可以用来辅助记忆。

上面提到的Subscription,则是订阅之后形成的一个订阅关系,可以用于取消订阅。

Observable.interval(1000).subscribe(() => {
  this.diff = moment(createAt).fromNow()
})
const timeA$ = Observable.interval(1000)
const timeB$ = timeA$.filter(num => {
    return (num % 2 != 0)
      && (num % 3 != 0)
      && (num % 5 != 0)
      && (num % 7 != 0)
  })

const timeC$ = timeB$.debounceTime(3000)
const timeD$ = timeC$.delay(2000)


A: 0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21
B:    1                             11    13          17    19
C:          1                                   13                19
D:                1                                   13
const 九阴真经 = '天之道,损有余而补不足'

const 黄蓉$ = new ReplaySubject(Number.MAX_VALUE)
const 郭靖$ = new ReplaySubject(3)

const 读书$ = Observable.from(九阴真经.split(''))

读书$.subscribe(黄蓉$)
读书$.subscribe(郭靖$)
const action$ = new Subject()
const reducer = (state, payload) => {
  // 把payload叠加到state上返回
}

const state$ = action$.scan(reducer)
  .startWith({})
const meAction$ = new Subject()
const meReducer = (state, payload) => {}

const articleAction$ = new Subject()
const articleReducer = (state, payload) => {}

const me$ = meAction$.scan(meReducer).startWith({})
const article$ = articleAction$.scan(articleReducer).startWith({})

const state$ = Observable
  .zip(
    me$,
    article$,
    (me, article) => {me, article}
  )
  
const editable$ = Observable.combineLatest(article$, me$)
  .map(arr => {
    let [article, me] = arr
    return me.isAdmin || article.author === me.id
  })

示例五:幸福人生

// 工资始终不涨
const salary$ = Observable.interval(100).mapTo(2)

// 挣钱是为了买房,买房是为了赚钱
const house$ = new Subject()

const houseCount$ = house$.scan((acc, num) => acc + num, 0).startWith(0)

const rent$ = Observable.interval(3000)
  .withLatestFrom(houseCount$)
  .map(arr => arr[1] * 5)
  
/*
解释一下上面这段代码:

房租由房租周期的定时器触发
然后到房子数量中取最后一个值,也就是当前有多少套房
然后,用房子数量乘以单套房的月租,假设是5
*/

// 一买了房,就没现金了……
const income$ = Observable.merge(salary$, rent$);

const cash$ = income$
  .scan((acc, num) => {
    const newSum = acc + num

    const newHouse = Math.floor(newSum / 100)
    if (newHouse > 0) {
      house$.next(newHouse)
    }

    return newSum % 100
  }, 0);
  
`
/*

累积之前的现金流与本次收入
假定房价100,先看看现金够买几套房,能买几套买几套
重新计算买完之后的现金
*/

Hello RxJS

https://zhuanlan.zhihu.com/p/23331432