On chaining and lettable operators - rxjs5

I am undecided if this is the right place to ask this question, or StackOverflow might be best for this. So here i go.
My introductions to rxjs was through the excellent Angular framework and throughout every tutorial and in practice has been "import whatever you need from add/operator and then chain it. eg.
import { Observable } from 'rxjs/Observable';
import { empty } from 'rxjs/observable/empty';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
// Other rxjs import
import 'rxjs/add/observable/of';
Class SomeClass {
#Effect
search$ = this._action
.ofType<search.SearchCountry>(search.SEARCH_COUNTRY)
.map(action => {
return action.payload;
})
.switchMap(query => {
if (query === '') {
return empty();
}
const nextSearch$ = this._action.ofType(search.SEARCH_COUNTRY).skip(1);
return this._searchService
.search(query)
.do(query => console.log(query))
.takeUntil(nextSearch$)
.map((result: RestResponseInterface) => {
return new search.SearchCountryComplete(result);
});
})
.catch(error => {
return Observable.of(new search.SearchError('Undocumented API Error'));
});
}
As explained in lettable documentaion, and If I am reading this right, chaining is discouraged as way forward, and pipe is the recommended implementation. The same implementation should be written as follows
import { tap, take, takeUntil, skip, map, switchMap } from 'rxjs/operators'; // Import statements have changed
search$ = this._action
.pipe(
ofType<search.SearchCountry>(search.SEARCH_COUNTRY),
map(action => action.payload),
switchMap(query => {
if (query === '') {
return empty();
}
const nextSearch$ = this._action.ofType(search.SEARCH_COUNTRY).skip(1);
return this._searchService
.search(query)
.pipe(tap(qyery => console.log(query)), takeUntil(nextSearch$), map((result: RestResponseInterface) => new search.SearchCountryComplete(result)));
})
)
.catch(error => {
return Observable.of(new search.SearchError('Undocumented API Error'));
});
Because when i read the source, especially after https://github.com/ReactiveX/rxjs/blob/master/src/Rx.ts#L41 its seems that chaining is perfectly acceptable, however, there is a proposal https://github.com/ReactiveX/rxjs/issues/2913, which is discussing adding rxjs/add/* as a seperate package.
My question is, how will this effect chaining in future?

It seems as though RxJS v6 is focusing completely on lettable operators (and actually dropping support for chainable operators). All evidence points towards they having a proper migration guide and support (eg: the RxJS 5 - 6 migration guide) but it seems as though the days of chainable operators are numbered, even if there is short-term support for a while.

Related

Is It good to subscribe to an observable within another subscription handler?

I'm using Angular2 and I have a question about what is the best way to do if I have many observables.
Can I put subscriptions inside each other or put each one in a different method and put the results in class properties?
Example :
ngOnInit() {
this.route.params**.subscribe**(params => {
if (params['id']) {
this.load = true;
this.batchService.getPagesOfCurrentObject(params['id'], "10", "0")
**.subscribe**(result => {
this.stream = result;
if (this.stream.length > 0) {
this.stream.forEach(page => { this.batchService.getPageStreamById
(page.pageId)**.subscribe**(pageStream => {
let base64 = btoa(new Uint8Array(pageStream.data)
.reduce((data, byte)
=> data + String.fromCharCode(byte), ''));
this.pages.push(base64 );
})
return;
});
}
},
error => this.errorService.setError(<any>error),
() => this.load = false
);
}
});
try {
this.customer = this.sharedService.processSelect.subscription.customer;
} catch (err) {
return;
}
}
Having multiple observables is totally fine, this is what reactive programming is about :)
But here your problem is having too much subscribe. Keep in mind that subscribe is a way to create side effect. To have an easy to read code, you should try to use the least possible subscribe.
Your use case is the perfect use case for the mergeMap operator, that allows you to flatten nested observables.
Here what your code would look like
const response$ = this.route.params
.mergeMap(params => {
return this.batchService.getPagesOfCurrentObject(params['id'])
})
.mergeMap(stream => {
return Rx.Observable.merge(stream.map(page => this.batchService.getPageStreamById(page.pageId))
})
.map(pageStream => /* do your stuff with pageStream, base64 ... */)
response$.subscribe(pageStreamData => pages.push(pageStreamData))
See how there is a single subscription that triggers the side-effect that will modify your app's state
Note that I voluntarily simplified the code (removed error handling and checks) for you to get the idea of how to do that.
I hope it will help you thinking in reactive programming :)

What is analogous to Promise.resolve for an Observable?

Here's an example of some code I picked up for an Angular2 tutorial by Max Schwarzmueller on YouTube: https://www.youtube.com/playlist?list=PL55RiY5tL51olfU2IEqr455EYLkrhmh3n.
import {Injectable} from "angular2/core";
import {CONTACTS} from "./mock-contact";
#Injectable()
export class ContactService {
getContacts() {
return Promise.resolve(CONTACTS);
}
insertContact(contact: Contact) {
Promise.resolve(CONTACTS)
.then(
(contacts: Contact[]) => contacts.push(contact)
);
}
}
In this example, the CONTACTS object is static JSON. A promise isn't necessary here, but was used to show usage of a service in the real world.
I pretty much understand it, but I'm trying to migrate this idea into a service where I'm using an observable instead of a promise.
I want to make a change to the CONTACTS array, and then have the Observable emit .then again to tell all the observers to do their thing one more time.
On an observable, what would be analogous to Promise.resolve here? The documentation for RxJS observables is a lot to wade through.
If this is just a dumb idea, or there's a better way, please let me know.
With
getContacts() {
return Observable.of(CONTACTS);
}
the observable will complete after it emitted the CONTACTS.
See also http://paqmind.com/posts/rxjs-error-and-completed-events-demystified/
The following is functioning code that works as far as my original question goes. I've replaced the JSON formatted array from CONTACTS with data pulled from a local JSON file, and I'm dealing with products instead of contacts.
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Product } from './product';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ProductsService {
private _productsUrl = "./products.json";
private _productObservable: Observable<Product[]>;
private _products: Product[];
constructor(private _http: Http) {
}
getProducts() : Observable<Product[]> {
this._productObservable = this._http.get(this._productsUrl)
.map((response:Response) => {
if (this._products == null)
{
this._products = response.json();
}
return this._products;
})
.catch((error:any) => Observable.throw(error || 'Server error'));
return this._productObservable;
}
deleteProduct()
{
this._products.pop();
return Observable.of(this._products);
}
}

why Rx.Observer get difference when import from 'rxjs' and 'rx-lite'

Here's the code, and the console output is in comment:
import Rx from 'rxjs';
import { Observer } from 'rxjs/Observer';
import Rx2 from 'rx-lite';
// import Promise from 'bluebird';
import 'whatwg-fetch';
const componentsData = {};
const availableComponentsObservable = Rx.Observable.create(observer => {
console.log(Observer); // output: undefined
console.log(Rx.Observer); // output: undefined
console.log(Rx2.Observer); // output: function () {}
fetch('/static/component-list.json').then((res) => res.json())
.then((components) => { observer.onNext(components); })
.catch((err) => { observer.onError(err); });
});// .groupBy(component => component.group);
availableComponentsObservable.subscribe((data) => {
componentsData.availableComponents = data;
});
I dont know why when import from rxjs got undefined
rxjs is https://github.com/ReactiveX/RxJS the RxJS 5 implementation that Ben Lesh has lead to have a reference implementation of https://github.com/tc39/proposal-observable
It is a rewrite and there are a number of differences in the API. You can find the differences documented here: https://github.com/ReactiveX/rxjs/blob/master/MIGRATION.md
rx-lite is part of https://github.com/Reactive-Extensions/RxJS which is the RxJS 4 implementation.
Note that with modern build tools, and the way you are expected to import each operator/observable you use of RxJS, there is no need for a "lite" version of RxJS 5. Just don't use rx.all.js and use it as documented with build tools that only include the files you use in your final deliverable.

Angular2: How to make several GET requests at once

With a promise JS library (https://github.com/stackp/promisejs) I can make this:
promise.join([
promise.get('/settings'),
promise.get('/translations'),
promise.get('/main-data')
]).then(function(results) {
console.log(results);
});
Now I need to do this with Angular2. So I made a service with methods getSettings, getTranslations, etc. - but how do I join them in the component that uses this service ?
(And join them in such way that when and only when all requests finish - I'll run a functionality that uses all the responses?)
You can use the forkJoin operator for this
import {Observable} from 'rxjs/Rx';
//...some code
Observable.forkJoin(
this.http.get('/settings').map((res:Response) => res.json()),
this.http.get('/translations').map((res:Response) => res.json())
)
.subscribe(data => {
console.log(data[0], data[1]);
});
The answer didn't solved the problem, mostly because I didn't understand how to get the data in the component. But it still guided me very well :)
So, here is the final code:
// Service - "AppService.ts":
import {Observable} from 'rxjs/Rx';
export class AppService {
getAllData() {
return Observable
.forkJoin(
this._http.get('/settings').map(res => res.json()),
this._http.get('/translations').map(res => res.json())
)
}
}
// Component - "AppComponent.ts":
import {AppService} from './app.service';
export class AppComponent {
AllData = {};
appService
.getAllData()
.subscribe(
data => {
console.log('Responses from AJAX:');
console.log(data);
this.AllData = JSON.stringify(data)
},
error => alert(error)
);
}

General: asynchonious validation in angular2

Since couple evening I've played with form validation in augular2.
All basic cases were easy to implement and they works fine but I stick with asynchronous validation. I have created a very tiny example http://plnkr.co/edit/Xo8xwJjhlHkXrunzS8ZE and it didn't work.
According to test "should fire an event after the status has been updated to pending" from model_spec.ts Registration via creation of control group suppose to work in a way
builder.group({login: ["",Validators.required,validationFuctionWhichReturnsPromise]
I spent a full evening to discovered that this code has been released in alfa-46 (and I used alfa-45) and after update depencies the async validation started to work. The feature is very fresh and is not fully documented but
(for those who haven't tried it yet) Basically async validator is a function which have a Control argument and return a promise which validation result. There are two ways to register a validator. 1) the one which I used in my example and 2) as a directive which Provide validators via NG_ASYNC_VALIDATORS (See UniqLoginValidator and NgFormControl to see how it work). You can compose more than one validator (not tested yet but functions to do this are in code, see https://github.com/angular/angular/commit/cf449dd).
But when I finally reach to up and running validators a new problem arrived. Async validator is perfect to used it in server side validation. But the validation is invoked after each change of model.fe after each keyup. So if we will send request to a server after each key up, it won't be too efficient way ;) I checked how it is done in angular 1 and they is a possibility to debounce validation events.
My questions are:
How to implement throttle or debounce with async validators? I saw some ideas but none of them were fine (mostly because they need to change angular code itself). Is there any valid way to do this without waiting for new angular release ?
I was thinking about to warping a validator function with debounce (from underscorejs) but it will not work because angular expects to get a valid promise every time.
My second though was that if all event use RxJs under the hood then maybe I can apply debounce on stream of event which is responsible for validation. In model.ts the promise returned from validator is change to observable and a new subscribed is added. We don't have any access to obs(Observable) to apply debounce there.
Is there any way or id to change,easy extend a control over the form validation ?
I spotted a close related problem in How to trigger Form Validators in angular2
PS there is other issue related to async validators and it is still open https://github.com/angular/angular/issues/1068
Here is a helper class that you can use to debounce all your async validators:
import {Component} from 'angular2/core';
import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import {Control} from 'angular2/common';
export class AsyncValidator {
_validate;
constructor(validator: (control: Control) => any, debounceTime = 1000) {
let source: any = new Observable((observer: Observer<Control>) => {
this._validate = (control) => observer.next(control);
});
source.debounceTime(debounceTime)
.distinctUntilChanged(null, (x) => x.control.value)
.map(x => { return { promise: validator(x.control), resolver: x.promiseResolver }; })
.subscribe(
(x) => x.promise.then(resultValue => x.resolver(resultValue),
(e) => { console.log('async validator error: %s', e); }));
}
private _getValidator() {
return (control) => {
let promiseResolver;
let p = new Promise((resolve) => {
promiseResolver = resolve;
});
this._validate({ control: control, promiseResolver: promiseResolver });
return p;
};
}
static debounce(validator: (control: Control) => any, debounceTime = 400) {
var asyncValidator = new this(validator, debounceTime);
return asyncValidator._getValidator();
}
}
Then all you have to do where use async validators is just wrap your validator with this call and write your validator the same as you would normally:
AsyncValidator.debounce(control => this.asyncValidator(control));
Here is an example usage:
export class AppComponent {
form: ControlGroup;
constructor(private _formBuilder: FormBuilder) {
var validator = AsyncValidator.debounce(control => this.asyncValidator(control));
this.form = _formBuilder.group({
name: ['', Validators.required, validator],
});
}
asyncValidator(control): any {
let p = new Promise(resolve => {
// get from server information need to validate control
if (control.value === 'valid value') {
resolve(null);
} else {
resolve({
asyncValidator: {
valid: false
}
});
}
});
return p;
}
}
There is an awesome issue on angular site that deals with the problem of both debouncing and switchMapping the validation:
https://github.com/angular/angular/issues/6895
This is mine working solution (but all the credit goes to guys from thread)
class AsyncValidator{
private validatorInput: Subject<string>;
private validatorChain: Observable<any>;
constructor(service: ManageUsersService) {
this.validatorInput = new Subject();
this.validatorChain = this.validatorInput
.debounceTime(400)
.distinctUntilChanged()
.switchMap(value => service.findUsersByName(value)
.map(() => ({error: 'Error'})) //example of failed validation
.catch(() => Observable.of(null))) //example of successful validation
.do(v => console.log('mapped', v))
.share()
.take(1);
}
validate = (control: AbstractControl) => {
// An immediate timeout is set because the next has to occur after the
// validator chain is subscribed to.
setTimeout(() => this.validatorInput.next(control.value), 0);
return this.validatorChain;
}
You use it like this:
this.createUserForm = fb.group({
login: [ null,
Validators.required,
new AsyncValidator(userService).validate
],
});
}

Resources