-
Notifications
You must be signed in to change notification settings - Fork 6
Bang Async
ngx-bang/async
provides better DX with asynchronous tasks like RxJS and Promises.
To support the argument of keeping
ngx-bang
simple,ngx-bang/async
was created to be dependent on RxJS to provide some DX improvements to users. The fact thatngx-bang/async
is a secondary-entry point means that it is optional.
Let's get back to our CounterComponent
and expand our state
.
If you're not familiar with
CounterComponent
, please check out ngx-bang Usages
Assuming we have a new requirement that is to move the "Seconds have passed since last count changed" to a property in our state
// the value of interval() needs to become our "state" value now
// instead of logging to the console
effect(this.state, ['count'], () => {
const sub = interval(1000)
.pipe(map(tick => tick + 1))
.subscribe((tick) => {
console.log(`It has been ${tick}s since the last time you changed "count"`);
});
return () => {
sub.unsubscribe();
}
});
Let's start by adjusting our CounterComponent
state to include secondsPassed
interface CounterState {
count: number;
incrementCount: number;
decrementCount: number;
secondsPassed: number;
}
@Component({
template: `
<ng-container *stateful="state; let snapshot">
<button (click)="onDecrement()">-</button>
<p>{{snapshot.count}}</p>
<button (click)="onIncrement()">+</button>
<p>You have clicked increment: {{snapshot.incrementCount}}</p>
<p>You have clicked decrement: {{snapshot.decrementCount}}</p>
<p>Seconds since last "count" changed": {{snapshot.secondsPassed}}s</p>
</ng-container>
`
})
export class CounterComponent implements OnInit {
state = state<CounterState>({
count: 0,
incrementCount: 0,
decrementCount: 0,
secondsPassed: 0
});
ngOnInit() {
// remove the effect. We'll replace it with something else
}
onIncrement() {
/* ... */
}
onDecrement() {
/* ... */
}
}
Now instead of effect
, we'll use connect
which is imported from ngx-bang/async
import { connect } from 'ngx-bang/async';
interface CounterState {
count: number;
incrementCount: number;
decrementCount: number;
secondsPassed: number;
}
@Component({
template: `
<!-- the template -->
`
})
export class CounterComponent implements OnInit {
state = state<CounterState>({
count: 0,
incrementCount: 0,
decrementCount: 0,
secondsPassed: 0
});
ngOnInit() {
connect(
// π connect to the Proxy
this.state,
// π for this key
'secondsPassed',
// π update with value from this stream
interval(1000).pipe(map(tick => tick + 1))
);
}
onIncrement() {
/* ... */
}
onDecrement() {
/* ... */
}
}
connect()
subscribes to the connector
(eg: interval()
) and will automatically clean up on Component's destroy as well. Everytime the connector
emits new value, state.secondsPassed
gets updated with that value.
If you save and go to the template, you'll see that secondsPassed
increments every second now.
However, we miss one part of the requirement: "when count changes". Right now, secondsPassed
just keeps incrementing every second and will not reset when we change count
. Let's fix that
import { connect } from 'ngx-bang/async';
interface CounterState {
count: number;
incrementCount: number;
decrementCount: number;
secondsPassed: number;
}
@Component({
template: `
<!-- the template -->
`
})
export class CounterComponent implements OnInit {
state = state<CounterState>({
count: 0,
incrementCount: 0,
decrementCount: 0,
secondsPassed: 0
});
ngOnInit() {
connect(
this.state,
'secondsPassed',
// π we can pass in a tuple [connector, deps]
[
interval(1000).pipe(map(tick => tick + 1)),
// π deps is an array of keys from "state"
// π so we can observe multiple different values from "state"
['count']
]
);
}
onIncrement() {
/* ... */
}
onDecrement() {
/* ... */
}
}
That's it! Now the interval
that is used to update secondsPassed
will be re-new whenever count
changes.