https://github.com/ngbox/ng-observe
Angular reactivity streamlined...
https://github.com/ngbox/ng-observe
angular angular2 observable observer reactive-programming rxjs streams subscription utility
Last synced: about 1 month ago
JSON representation
Angular reactivity streamlined...
- Host: GitHub
- URL: https://github.com/ngbox/ng-observe
- Owner: ngbox
- License: mit
- Created: 2021-03-08T07:12:17.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2022-08-18T08:36:29.000Z (almost 3 years ago)
- Last Synced: 2025-05-01T08:23:22.463Z (about 1 month ago)
- Topics: angular, angular2, observable, observer, reactive-programming, rxjs, streams, subscription, utility
- Language: TypeScript
- Homepage:
- Size: 378 KB
- Stars: 70
- Watchers: 7
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
![]()
ng-observe
Angular reactivity streamlined...
### Why?
- Unlike [AsyncPipe](https://angular.io/api/common/AsyncPipe), you can use it in component classes and even in directives.
- Feels more reactive than unsubscribing on destroy (be it handled by a decorator, triggered by a subject, or done by a direct call in the lifecycle hook).
- Reduces the complexity of working with streams.
- Works in zoneless apps. (v1.1.0+)### How it works
- Extracts emitted value from observables.
- Marks the component for change detection.
- Leaves no subscription behind.
- Clears old subscriptions and creates new ones at each execution if used in getters, setters or methods.### How to use
Install the package, and you are good to go. No module import is necessary.
```
npm install ng-observe
```...or...
```
yarn add ng-observe
```### Example
We can subscribe to a stream with the `AsyncPipe` in component templates, but we can't use it in component or directive classes.
```typescript
@Component({
template: '{{ fooBar$ | async }}',
})
class DemoComponent {
foo$ = of('foo');get fooBar$() {
return foo$.pipe(map(val => val + 'bar'));
}
}
```With ng-observe, we don't need to pipe the stream.
```typescript
import { OBSERVE, OBSERVE_PROVIDER, ObserveFn } from 'ng-observe';@Component({
template: '{{ fooBar }}',
providers: [OBSERVE_PROVIDER],
})
class DemoComponent {
foo = this.observe(of('foo'));get fooBar() {
return this.foo.value + 'bar';
}constructor(@Inject(OBSERVE) private observe: ObserveFn) {}
}
```You can see other examples at links below:
- [Basic example](https://stackblitz.com/edit/ng-observe?file=src%2Fapp%2Fapp.ts)
- [Using with Angular router](https://stackblitz.com/edit/ng-observe-router?file=src%2Fapp%2Fapp.ts)
- [Using with NgRx](https://stackblitz.com/edit/ng-observe-ngrx?file=src%2Fapp%2Fapp.ts)
- [Zoneless](https://stackblitz.com/edit/ng-observe-zoneless?file=src%2Fapp%2Fapp.ts)> **Important Note:** Do not destructure a collection created by the `ObserveFn`. Otherwise, the reactivity will be lost. Use `toValue` or `toValues` to convert elements of the collection to instances of `Observed` instead.
You can read [this Medium article](https://ozak.medium.com/angular-reactivity-streamlined-831754b60a11) to learn about what the motivation behind ng-observe is.
### API
#### OBSERVE_PROVIDER
To use ng-observe in your components and directives, add `OBSERVE_PROVIDER` to providers array in metadata.
#### ObserveFn
This function is used to extract a single stream's value. You can inject it via the `OBSERVE` injection token.
```typescript
import { OBSERVE, OBSERVE_PROVIDER, ObserveFn } from 'ng-observe';@Component({
template: '{{ foo.value }}',
providers: [OBSERVE_PROVIDER],
})
class Component {
foo = this.observe(of('foo'));constructor(@Inject(OBSERVE) private observe: ObserveFn) {}
}
```You can extract multiple streams' value too.
```typescript
import { OBSERVE, OBSERVE_PROVIDER, ObserveFn } from 'ng-observe';@Component({
template: '{{ state.foo }} {{ state.bar }}',
providers: [OBSERVE_PROVIDER],
})
class Component {
state = this.observe({ foo: of('foo'), bar: of('bar') });constructor(@Inject(OBSERVE) private observe: ObserveFn) {}
}
```It works with arrays as well.
```typescript
import { OBSERVE, OBSERVE_PROVIDER, ObserveFn } from 'ng-observe';@Component({
template: '{{ state[0] }} {{ state[1] }}',
providers: [OBSERVE_PROVIDER],
})
class Component {
state = this.observe([of('foo'), of('bar')]);constructor(@Inject(OBSERVE) private observe: ObserveFn) {}
}
```#### ObserveService
You can call `ObserveService`'s `value` and `collection` methods explicitly instead of `ObserveFn`. This offers a very slight (ignorable in most cases) performance improvement.
```typescript
import { ObserveService } from 'ng-observe';@Component({
template: '{{ foo.value }} {{ state[0] }} {{ state[1] }}',
providers: [ObserveService],
})
class Component {
foo = this.observe.value(of('foo'));state = this.observe.collection([of('foo'), of('bar')]);
constructor(private observe: ObserveService) {}
}
```#### Observed
`ObserveFn` infers types for you, but if you want to assign an observed value later, you can use `Observed` class for type annotation.
```typescript
import { OBSERVE, OBSERVE_PROVIDER, Observed } from 'ng-observe';@Component({
template: '{{ foo.value }}',
providers: [OBSERVE_PROVIDER],
})
class Component {
foo: Observed;constructor(@Inject(OBSERVE) private observe: ObserveFn) {
this.foo = this.observe(of('foo'));
}
}
```#### toValue
`toValue` converts an element in the collection to a reactive observed value. Returns an instance of the `Observed` class.
```typescript
import { OBSERVE, OBSERVE_PROVIDER, Observed, ObserveFn, toValue } from 'ng-observe';@Component({
template: '{{ foo.value }} {{ bar.value }}',
providers: [OBSERVE_PROVIDER],
})
class Component {
foo: Observed;bar: Observed;
constructor(@Inject(OBSERVE) private observe: ObserveFn) {
const state = this.observe({ foo: of('foo'), bar: of('bar') });
this.foo = toValue(state, 'foo');
this.bar = toValue(state, 'bar');
}
}
```#### toMappedValue
You can use `toMappedValue` to get a reactive observed value mapped from the collection. Returns an instance of the `Observed` class.
```typescript
import { OBSERVE, OBSERVE_PROVIDER, Observed, ObserveFn, toMappedValue } from 'ng-observe';@Component({
template: '{{ fooBar.value }}',
providers: [OBSERVE_PROVIDER],
})
class Component {
fooBar: Observed;constructor(@Inject(OBSERVE) private observe: ObserveFn) {
const state = this.observe({ foo: of('foo'), bar: of('bar') });
this.fooBar = toMappedValue(state, ({ foo, bar }) => `${foo} ${bar}`);
}
}
```#### toValues
`toValues` converts all elements in collection to reactive observed values. Returns an array/object the indices/keys of which will be the same with the input collection. Each element will be an instance of the `Observed` class.
```typescript
import { OBSERVE, OBSERVE_PROVIDER, Observed, ObserveFn, toValues } from 'ng-observe';@Component({
template: '{{ foo.value }} {{ bar.value }}',
providers: [OBSERVE_PROVIDER],
})
class Component {
foo: Observed;bar: Observed;
constructor(@Inject(OBSERVE) private observe: ObserveFn) {
const state = this.observe({ foo: of('foo'), bar: of('bar') });
const { foo, bar } = toValues(state);
this.foo = foo;
this.bar = bar;
}
}
```#### isCollection
Collections observed by ng-observe are plain arrays or objects, but you can detect them with `isCollection` function. It returns `true` when input is an observed collection, and `false` when not.
```typescript
import { isCollection, OBSERVE, OBSERVE_PROVIDER, ObserveFn } from 'ng-observe';@Component({
template: '',
providers: [OBSERVE_PROVIDER],
})
class Component {
constructor(@Inject(OBSERVE) private observe: ObserveFn) {
const state = this.observe({ foo: of('foo'), bar: of('bar') });
console.log(isCollection(state)); // true
}
}
```### Sponsors
[](https://volosoft.com/)
![]()