@actyx-contrib/ng-pond 中文文档教程
ng-Pond
使用 Actyx Pond 框架集成为您的服务角度应用。 使用 ActyxPondService
扩展您的工具链以观察整个应用程序中的鱼并加快您的 UI 项目并在几个小时内编写分布式应用程序。
Installation
ng-pond 可作为 npm 包使用。
npm install @actyx-contrib/ng-pond
Quick start
ActyxPondService
将 ActyxPondService
作为单例实例添加到您的根模块,并保持池塘内部缓存的优势。
Example:
文件:app.module.ts
import { AppComponent } from './app.component';
import { ActyxPondService } from '@actyx-contrib/ng-pond'
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [ActyxPondService],
bootstrap: [AppComponent]
})
export class AppModule {}
Use the pond api
在您的组件中使用简单的 pond api 作为回调或 rxjs observables。 这将使您有机会在代码中使用您的鱼状态或使用 async
管道来构建响应式和最先进的用户界面。
注意:强烈建议在关注点分离 (SoC) 中构建应用程序。 直接在组件中使用
PondService
会使维护项目和为组件编写 e2e 和单元测试变得更加困难。
Example:
Logic:
文件:app.component.ts
import { Component } from '@angular/core';
import { ActyxPondService } from '@actyx-contrib/ng-pond'
import { MachineFish, State } from '../fish/MachineFish';
import { Observable } from 'rxjs';
import { ConnectivityStatus } from '@actyx/pond';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
machine$: Observable<State>
connectivity$: Observable<ConnectivityStatus>
constructor(private pondService: ActyxPondService) {
this.machine$ = this.pondService.observe$(MachineFish.of('Machine1'))
this.connectivity$ = this.pondService.getNodeConnectivity$()
}
async start() {
const pond = await this.pondService.getPond()
MachineFish.emitProdStartedEvent(pond, 'Machine1', 'order1')
}
async stop() {
const pond = await this.pondService.getPond()
MachineFish.emitProdStoppedEvent(pond, 'Machine1', 'order1')
}
}
Template:
文件:app.component.html
<h1>Angular - Actyx-Pond - Machine control</h1>
<div *ngIf="connectivity$ | async as connectivity">
<h2>Connectivity: {{connectivity.status | json}}</h2>
</div>
<div *ngIf="machine$ | async as machine; else loading">
<div>
<h2>Machine {{machine.machineId}}</h2>
<dl>
<dt>state:</dt>
<dd>{{machine.state}}</dd>
</dl>
</div>
<button *ngIf="machine.state==='stopped'" (click)="start()">start</button>
<button *ngIf="machine.state==='started'" (click)="stop()">stop</button>
</div>
<ng-template #loading>Loading machine data...</ng-template>
Fish
文件:MachineFish.ts
import { Fish, FishId, Pond, Tag } from '@actyx/pond'
export type State =
| { state: 'idle', machineId: string }
| { state: 'inProduction', machineId: string, orderId: string }
export type Event =
| { eventType: 'prodStarted', machineId: string, orderId: string }
| { eventType: 'prodStopped', machineId: string, orderId: string }
const machineTag = Tag<Event>('machine')
export const MachineFish = {
tags: { machineTag },
of: (machineId: string): Fish<State, Event> => ({
fishId: FishId.of('machineFish', machineId, 0),
initialState: { state: 'idle', machineId },
where: machineTag.withId(machineId),
onEvent: (state, event) => {
switch (event.eventType) {
case 'prodStarted':
return {
state: 'inProduction',
machineId: state.machineId,
orderId: event.orderId,
}
case 'prodStopped':
return {
state: 'idle',
machineId: state.machineId,
}
}
return state
},
}),
emitProdStoppedEvent: (pond: Pond, machineId: string, orderId: string) =>
pond.emit(
machineTag.withId(machineId),
{ eventType: 'prodStopped', machineId, orderId }
),
emitProdStartedEvent: (pond: Pond, machineId: string, orderId: string) =>
pond.emit(
machineTag.withId(machineId),
{ eventType: 'prodStarted', machineId, orderId }
),
}
Registry fish
池中有两种创建方式注册鱼。 observeAll
和 observe
注册表鱼并将实体鱼映射为第二步。 observeAll
非常有策略,下面是注册表鱼的示例。
Example:
注意:这个例子建立在上面的
Use the pond api
例子之上。
Logic:
文件:app.component.ts
// [..]
allMachines$: Observable<ReadonlyArray<State>>
constructor(private pondService: ActyxPondService) {
this.machine$ = this.pondService.observe$(MachineFish.of('Machine1'))
this.allMachine$ = this.pondService.observeRegistry$(MachineFish.registry(), s => s, MachineFish.of)
this.connectivity$ = this.pondService.getNodeConnectivity$()
}
// [..]
}
Template:
文件:app.component.html
<!-- [..] -->
<div *ngFor="let machine of (allMachines$ | async)">
<dl>
<dt>Name:</dt>
<dd>{{machine.machineId}}</dd>
<dt>State:</dt>
<dd>{{machine.state}}</dd>
</dl>
</div>
<!-- [..] -->
Fish
文件:MachineFish.ts
export const MachineFish = {
// [..]
registry: (): Fish<string[], Event> => ({
fishId: FishId.of('machineRegFish', 'reg', 0),
initialState: [],
where: machineTag,
onEvent: (state, event) => {
if (!state.includes(event.machineId)) {
state.push(event.machineId)
}
return state
},
}),
// [..]
Service overview
查看关于 Actyx-Pond 的文档获取更多详细信息 https://developer.actyx.com/docs/pond/introduction/
您将在包的定义文件中找到详细的 api 文档。
TS/JS Promise interface:
- getPond()
- emit(tags, event)
- observe(fish, onStateChanged)
- observeRegistry(registryFish, mapToProperty, makeEntityFish, onStateChanged)
- observeAll(seedEventsSelector, makeFish, opts, onStateChanged)
- observeOne(seedEventSelector, makeFish, onStateChanged, stoppedByError)
- getPondState(callback)
- pondInfo()
- run(fish, fn)
- keepRunning(fish, fn, autoCancel)
RxJs integration:
注意:并非每个函数都有 RxJs 包装器。 在这种情况下,请使用上面的那个。
- getRxPond()
- observeRegistry$(registryFish, mapToProperty, makeEntityFish)
- observe$(fish)
- observeAll$(seedEventsSelector, makeFish, opts)
- observeOne$(seedEventsSelector, makeFish)
- getPondState$()
- getNodeConnectivity$()
- waitForSwarmSync$()
- run$(fish, fn)
ng-Pond
Use the Actyx Pond framework integrated as a service in your angular application. Expand your toolchain with the ActyxPondService
to observe fish all over your application and speed up your UI projects and write distributed apps in a couple of hours.
???? Installation
ng-pond is available as a npm package.
npm install @actyx-contrib/ng-pond
???? Quick start
???? ActyxPondService
Add the ActyxPondService
to your root module as singleton instance and keep the advantage of the pond's internal caching.
???? Example:
File: app.module.ts
import { AppComponent } from './app.component';
import { ActyxPondService } from '@actyx-contrib/ng-pond'
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [ActyxPondService],
bootstrap: [AppComponent]
})
export class AppModule {}
???? Use the pond api
Use the simple pond api in your components as with callbacks or with rxjs observables. This will give you the opportunity to use your fish states in the code or use async
pipelines to build reactive and state of the art user interfaces.
Note: It is highly recommended to build applications in separation of concerns (SoC). Using the
PondService
directly in the components makes it harder to maintain your project and write e2e and unit tests for your components.
???? Example:
Logic:
File: app.component.ts
import { Component } from '@angular/core';
import { ActyxPondService } from '@actyx-contrib/ng-pond'
import { MachineFish, State } from '../fish/MachineFish';
import { Observable } from 'rxjs';
import { ConnectivityStatus } from '@actyx/pond';
@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
machine$: Observable<State>
connectivity$: Observable<ConnectivityStatus>
constructor(private pondService: ActyxPondService) {
this.machine$ = this.pondService.observe$(MachineFish.of('Machine1'))
this.connectivity$ = this.pondService.getNodeConnectivity$()
}
async start() {
const pond = await this.pondService.getPond()
MachineFish.emitProdStartedEvent(pond, 'Machine1', 'order1')
}
async stop() {
const pond = await this.pondService.getPond()
MachineFish.emitProdStoppedEvent(pond, 'Machine1', 'order1')
}
}
Template:
File: app.component.html
<h1>Angular - Actyx-Pond - Machine control</h1>
<div *ngIf="connectivity$ | async as connectivity">
<h2>Connectivity: {{connectivity.status | json}}</h2>
</div>
<div *ngIf="machine$ | async as machine; else loading">
<div>
<h2>Machine {{machine.machineId}}</h2>
<dl>
<dt>state:</dt>
<dd>{{machine.state}}</dd>
</dl>
</div>
<button *ngIf="machine.state==='stopped'" (click)="start()">start</button>
<button *ngIf="machine.state==='started'" (click)="stop()">stop</button>
</div>
<ng-template #loading>Loading machine data...</ng-template>
???? Fish
File: MachineFish.ts
import { Fish, FishId, Pond, Tag } from '@actyx/pond'
export type State =
| { state: 'idle', machineId: string }
| { state: 'inProduction', machineId: string, orderId: string }
export type Event =
| { eventType: 'prodStarted', machineId: string, orderId: string }
| { eventType: 'prodStopped', machineId: string, orderId: string }
const machineTag = Tag<Event>('machine')
export const MachineFish = {
tags: { machineTag },
of: (machineId: string): Fish<State, Event> => ({
fishId: FishId.of('machineFish', machineId, 0),
initialState: { state: 'idle', machineId },
where: machineTag.withId(machineId),
onEvent: (state, event) => {
switch (event.eventType) {
case 'prodStarted':
return {
state: 'inProduction',
machineId: state.machineId,
orderId: event.orderId,
}
case 'prodStopped':
return {
state: 'idle',
machineId: state.machineId,
}
}
return state
},
}),
emitProdStoppedEvent: (pond: Pond, machineId: string, orderId: string) =>
pond.emit(
machineTag.withId(machineId),
{ eventType: 'prodStopped', machineId, orderId }
),
emitProdStartedEvent: (pond: Pond, machineId: string, orderId: string) =>
pond.emit(
machineTag.withId(machineId),
{ eventType: 'prodStarted', machineId, orderId }
),
}
Registry fish
In the pond, there are two ways to create registry fish. observeAll
and observe
a registry fish and map the entity fish as a second step. In the matter that observeAll
is pretty strate forward, here is an example for the registry fish.
???? Example:
Note: This example is build on top of the
Use the pond api
example above.
Logic:
File: app.component.ts
// [..]
allMachines$: Observable<ReadonlyArray<State>>
constructor(private pondService: ActyxPondService) {
this.machine$ = this.pondService.observe$(MachineFish.of('Machine1'))
this.allMachine$ = this.pondService.observeRegistry$(MachineFish.registry(), s => s, MachineFish.of)
this.connectivity$ = this.pondService.getNodeConnectivity$()
}
// [..]
}
Template:
File: app.component.html
<!-- [..] -->
<div *ngFor="let machine of (allMachines$ | async)">
<dl>
<dt>Name:</dt>
<dd>{{machine.machineId}}</dd>
<dt>State:</dt>
<dd>{{machine.state}}</dd>
</dl>
</div>
<!-- [..] -->
???? Fish
File: MachineFish.ts
export const MachineFish = {
// [..]
registry: (): Fish<string[], Event> => ({
fishId: FishId.of('machineRegFish', 'reg', 0),
initialState: [],
where: machineTag,
onEvent: (state, event) => {
if (!state.includes(event.machineId)) {
state.push(event.machineId)
}
return state
},
}),
// [..]
???? Service overview
Check out the documentation about the Actyx-Pond to get more detailed information https://developer.actyx.com/docs/pond/introduction/
You are going to find a detailed api documentation in the definition file of the package.
TS/JS Promise interface:
- getPond()
- emit(tags, event)
- observe(fish, onStateChanged)
- observeRegistry(registryFish, mapToProperty, makeEntityFish, onStateChanged)
- observeAll(seedEventsSelector, makeFish, opts, onStateChanged)
- observeOne(seedEventSelector, makeFish, onStateChanged, stoppedByError)
- getPondState(callback)
- pondInfo()
- run(fish, fn)
- keepRunning(fish, fn, autoCancel)
RxJs integration:
Note: Not every function has a RxJs wrapper. In this case, please use the one from above.
- getRxPond()
- observeRegistry$(registryFish, mapToProperty, makeEntityFish)
- observe$(fish)
- observeAll$(seedEventsSelector, makeFish, opts)
- observeOne$(seedEventsSelector, makeFish)
- getPondState$()
- getNodeConnectivity$()
- waitForSwarmSync$()
- run$(fish, fn)