Return an empty Observable - javascript

The function more() is supposed to return an Observable from a get request
export class Collection {
public more = (): Observable<Response> => {
if (this.hasMore()) {
return this.fetch();
} else {
// return empty observable
}
};
private fetch = (): Observable<Response> => {
return this.http.get("some-url").map((res) => {
return res.json();
});
};
}
In this case I can only do a request if hasMore() is true, else I get an error on subscribe() function subscribe is not defined, how can I return an empty Observable?
this.collection.more().subscribe(
(res) => {
console.log(res);
}, (err) => {
console.log(err);
}
);

With the new syntax of RxJS 5.5+, this becomes as the following:
// RxJS 6
import { EMPTY, empty, of } from "rxjs";
// rxjs 5.5+ (<6)
import { empty } from "rxjs/observable/empty";
import { of } from "rxjs/observable/of";
empty(); // deprecated use EMPTY
EMPTY;
of({});
Just one thing to keep in mind, EMPTY completes the observable, so it won't trigger next in your stream, but only completes. So if you have, for instance, tap, they might not get trigger as you wish (see an example below).
Whereas of({}) creates an Observable and emits next with a value of {} and then it completes the Observable.
E.g.:
EMPTY.pipe(
tap(() => console.warn("i will not reach here, as i am complete"))
).subscribe();
of({}).pipe(
tap(() => console.warn("i will reach here and complete"))
).subscribe();

For typescript you can specify generic param of your empty observable like this:
import 'rxjs/add/observable/empty'
Observable.empty<Response>();

RxJS6 (without compatibility package installed)
There's now an EMPTY constant and an empty function.
import { Observable, empty, EMPTY, of } from 'rxjs';
//This is now deprecated
var delay = empty().pipe(delay(1000));
var delay2 = EMPTY.pipe(delay(1000));
Observable.empty() doesn't exist anymore.

Several ways to create an Empty Observable:
They just differ on how you are going to use it further (what events it will emit after: next, complete or do nothing) e.g.:
Observable.never() - emits no events and never ends.
Observable.empty() - emits only complete.
Observable.of({}) - emits both next and complete (Empty object literal passed as an example).
Use it on your exact needs)

In my case with Angular2 and rxjs, it worked with:
import {EmptyObservable} from 'rxjs/observable/EmptyObservable';
...
return new EmptyObservable();
...

Yes, there is am Empty operator
Rx.Observable.empty();
For typescript, you can use from:
Rx.Observable<Response>.from([])

Since all the answers are outdated, I will post the up to date answer here
In RXJS >= 6
import { EMPTY } from 'rxjs'
return EMPTY;

You can return Observable.of(empty_variable), for example
Observable.of('');
// or
Observable.of({});
// etc

Differents way to return empty observable :
Observable.from({});
Observable.of({});
EMPTY
https://www.learnrxjs.io/learn-rxjs/operators/creation/empty

Or you can try ignoreElements() as well

RxJS 6
you can use also from function like below:
return from<string>([""]);
after import:
import {from} from 'rxjs';

Came here with a similar question, the above didn't work for me in: "rxjs": "^6.0.0", in order to generate an observable that emits no data I needed to do:
import {Observable,empty} from 'rxjs';
class ActivatedRouteStub {
params: Observable<any> = empty();
}

Try this
export class Collection{
public more (): Observable<Response> {
if (this.hasMore()) {
return this.fetch();
}
else{
return this.returnEmpty();
}
}
public returnEmpty(): any {
let subscription = source.subscribe(
function (x) {
console.log('Next: %s', x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
}
}
let source = Observable.empty();

You can return the empty observable with all different ways but challenge is to to return it with the expected type -
Here is the way to create a empty observable with type -
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(this.setHeaders(req))
.pipe(
catchError((error: HttpErrorResponse) => {
// you write your logic and return empty response if required
return new Observable<HttpEvent<any>>();
}));
}

there is another: EMPTY const
Replaced with the EMPTY constant or scheduled (e.g. scheduled([], scheduler)). Will be removed in v8. (got this form phpstorm hint)

Related

Angular TypeScript: waiting for a void method to run before another method

I have a method in an Angular component that pulls data via HttpClient subscription, and assigns it to an attributes this.allData, then instantiates an empty dictionary of parameters based on this, to pass to a second function:
export class TestComponent implements OnInit {
allData: object[] = []
activeData: object = {}
constructor(private http: HttpClient) { }
ngOnInit(): void {
this.getData()
this.makeRequestBasedOnData()
}
getData() {
this.http.get(this.url).subscribe(res => {
for (let datum of res["data"]) {
this.allData.push({
"key": Object.keys(datum)[0],
"values": Object.values(datum)[0]
})
this.activeData[Object.keys(datum)[0]] = ""
}
})
}
makeRequestBasedOnData() {
let testParams = this.activeData
console.log(testParam)
}
}
I need these steps to happen sequentially. At the moment, logging the testParams in makeRequestBasedOnData() simply shows an empty object {}. When I try to return arbitrarily in the first method, I get a TypeScript error that you cannot assign a promise to type void.
How do I enforce synchronicity here even though neither method actually returns anything?
You can return Observable from getData method and proceed with any other methods within subscribe:
ngOnInit(): void {
this.getData().subscribe(() => this.makeRequestBasedOnData());
}
getData() {
return this.http.get(this.url).pipe(
tap(res => {
// update allData
})
);
}
where:
pipe method allows us to provide any kind of transformation with the data returned from http.get(...) call.
tap is a rxjs operator for side-effects, meaning that we can do everything we want with the response without modifying Observable flow.

Type 'Subscription' is missing the following properties from type 'Observable<StringMap<any>>'

ERROR : Type 'Subscription' is missing the following properties from
type 'Observable>': _isScalar, source, operator, lift,
and 6 more.ts(2740)
Here I have attached my code.
Here, in my case, I have two methods which return an observable, but getByTypeData and getByType. But, on returning this.getByType(type).. from getByTypeData() I am getting above error.
P.S.: I want to subscribe getByTypeData in my component which should return me an observable. AND I AM NEW TO RXJS...
/*
interface IStringMap<T> {
[index: string]: T;
}
*/
getByTypeData(type: string, ignoreApi = false): Observable<stringMap<any>> {
if (ignoreApi) {
this.handleConfig(type);
}
return this.getByType(type)
.subscribe(response => {
const config = response.result ? response.data : {};
return this.handleConfig(type, config);
});
}
// This method in another file (Just for reference)
getByType(type: string): Observable<stringMap<any>> {
return this.httpClient.get(`get url`);
}
handleConfig(type: string, config: stringMap<string | number> = {}): Observable<stringMap<any>> {
if (type === this.types) {
config.token = this.anotherservice.GetKey('mykey');
if (config.token) {
// logic
}
}
if (type === this.types) {
// logic
}
return of(config);
}
As pointed out in the comments, you are returning a Subscription instead of returning an Observable. I would suggest you read the documentation to get a good idea of the difference between them.
In your particular case, I would suggest you try something like the following instead:
getByTypeData(type: string, ignoreApi = false): Observable<stringMap<any>> {
if (ignoreApi) {
return this.handleConfig(type);
}
return this.getByType(type).pipe(
switchMap(response => {
const config = response.result ? response.data : {};
return this.handleConfig(type, config);
})
);
}
switchMap is an rxjs operator that needs to be imported with a statement like this:
import { switchMap } from 'rxjs/operators'
Documentation on this operator can be found here
A good article explaining the mapping operators is here

Rxjs from() operator doesn't send data

I have a simple app on Angular/rxjs/Ngrx which requests list of default films from the api.
component.ts
export class MoviesComponent implements OnInit {
private movies$: Observable<{}> =
this.store.select(fromRoot.getMoviesState);
private films = [];
constructor(public store: Store<fromRoot.State>) {}
ngOnInit() {
this.store.dispatch(new MoviesApi.RequestMovies());
this.movies$.subscribe(film => this.films.push(film));
console.log(this.films)
}
effects.ts
#Effect()
requestMovies$: Observable<MoviesApi.MoviesApiAction> = this.actions$
.pipe(
ofType(MoviesApi.REQUEST_MOVIES),
switchMap(actions => this.MoviesApiServ.getDefaultMoviesList()
.pipe(
mergeMap(movies => of(new MoviesApi.RecieveMovies(movies))),
catchError(err => {
console.log('err', err);
return of(new MoviesApi.RequestFailed(err));
})
)
)
);
service.ts
export class MoviesApiService {
private moviesList = [];
constructor(private http: HttpClient) { }
public getDefaultMoviesList(): Observable<{}> {
DEFAULT_MOVIES.map(movie => this.getMovieByTitle(movie).subscribe(item => this.moviesList.push(item)));
return from(this.moviesList);
}
public getMovieByTitle(movieTitle: string): Observable<{}> {
return this.http.get<{}>(this.buildRequestUrl(movieTitle))
.pipe(retry(3),
catchError(this.handleError)
);
}
}
DEFAULT_MOVIES is just array with titles.
So my getDefaultMoviesList method is not sending data. But if I replace this.moviesList to hardcoced array of values it works as expected.
What I'm doing wrong?
UPD
I wanted to loop over the default list of films, then call for each film getMovieByTitle and collect them in array and send as Observable. Is there any better solution?
1) You should probably move this line to the service contructor, otherwise you will push a second array of default movies every time you getDefaultMoviesList:
DEFAULT_MOVIES.map(movie => this.getMovieByTitle(movie).subscribe(item => this.moviesList.push(item)));
2) Actually you should probably merge the output of each http.get:
public getDefaultMoviesList(): Observable<{}> {
return merge(DEFAULT_MOVIES.map(movie => this.http.get<{}>(this.buildRequestUrl(movieTitle))
.pipe(retry(3),
catchError(this.handleError)
)))
}
3) You should actually only do that once and store it in BehaviorSubject not to make new HTTP request on each getDefaultMoviesList
private movies$: BehaviorSubject<any> = new BehaviorSubject<any>();
public getMovies$() {
return this.movies$.mergeMap(movies => {
if (movies) return of(movies);
return merge(DEFAULT_MOVIES.map(movie => this.http.get<{}>(this.buildRequestUrl(movieTitle))
.pipe(retry(3),
catchError(this.handleError)
)))
})
}
4) Your implementation shouldn't work at all since:
public getDefaultMoviesList(): Observable<{}> {
DEFAULT_MOVIES.map(movie => this.getMovieByTitle(movie).subscribe(item =>
this.moviesList.push(item))); // This line will happen after http get completes
return from(this.moviesList); // This line will happen BEFORE the line above
}
So you will always return an Observable of empty array.
5) You shouldn't use map if you don't want to map your array to another one. You should use forEach instead.
map is used like this:
const mappedArray = toMapArray.map(element => someFunction(element));
You can try creating the observable using of operator.
Ex: of(this.moviesList);
One intersting fact to note is that Observable.of([]) will be an empty array when you subscribe to it. Where as when you subscribe to Observable.from([]) you wont get any value.
Observable.of, is a static method on Observable. It creates an Observable for you, that emits value(s) that you specify as argument(s) immediately one after the other, and then emits a complete notification.

How to get data from the server through an interval?

I use angular6, i try get json from server through 5 seconds interval.
JSFIDDLE
Its my service:
getExample(): Observable<any> {
console.log('getExample');
return Observable
.timer(0, 5000)
.flatMap(() => this.httpClient.get('https://jsonplaceholder.typicode.com/posts'))
.map((r) => {
return r;
});
}
Please help fix this code.
PS: more simple service worked without problem
RxJS 6 has updated to use pipeable operators. Also, timer imports from a different path. You should import timer (or interval in this case) directly and use .pipe with the operators that you need. Finally, use mergeMap instead of flatMap.
import { interval } from 'rxjs';
import { mergeMap, map } from 'rxjs/operators';
return interval(5000).pipe(
mergeMap(() => this.httpClient.get('https://jsonplaceholder.typicode.com/posts')),
map((r) => {
console.log(r);
return r;
})
);

Angular - detecting when two subscriptions have updated

In a component, in ngOnInit() I've got two subscriptions to a data service. I want to do some processing once both subscriptions have returned. Whats the best way to do this? I can just process at the end of each, this just seems a little inefficient and won't work for which ever subscription activates first,
Thanks,
Component.TS
ngOnInit()
{
this.dataService.dataA().subscribe((dataAJSON) =>
{
this.dataA= dataAJSON
}
this.dataService.dataB().subscribe((dataBJSON) =>
{
this.dataB= dataBJSON
}
DataService
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class PMDataService
{
constructor(public http : Http)
{
}
dataA()
{
var dataA: any;
var json;
dataA= this.http.get("./assets/dataa.json")
.map(res => res.json());
return dataA
}
dataB()
{
var dataB: any;
var json;
dataB= this.http.get("./assets/datab.json")
.map(res => res.json());
return dataB
}
}
You can use Observable#forkJoin function on the Observables. It emits the last value from each when all observables complete,
Observable.forkJoin(this.dataService.dataA(), this.dataService.dataB())
.subscribe(val => /* val is an array */)
The method used depends on how you want to receive the data:
You can use the zip function. Emits once when all have emitted once. So similar to Promise.all except not on completion.
Observable.zip(obs1, obs2).subscribe((val) => { ... });
You can use the forkJoin function. Emits once when all have completed. So exactly like Promise.all.
Observable.forkJoin(obs1, obs2).subscribe((val) => { ... });
You can use the merge function. Emits in order of emission so could be 1st then 2nd or 2nd then 1st:
obs1.merge(obs2).subscribe((val) => { ... });
You can use concat function. Emits in order 1st then 2nd regardless if 2nd emits first:
obs1.concat(obs2).subscribe((val) => { ... });
It's best practice to split these up into a couple lines for clarity.
const obs1 = Rx.Observable.of(1,2,3);
const obs2 = Rx.Observable.of(1,2,3);
const example = Observable.zip(obs1, obs2);
//const example = Observable.forkJoin(obs1, obs2);
//const example = obs1.merge(obs2);
//const example = obs1.concat(obs2);
example.subscribe(val => { ... });
You could use the operator Zip or CombineLatest from rxjs.
See ReactiveX operators
You could do something like this:
Observable.zip(
this.http.get("./assets/dataa.json"),
this.http.get("./assets/dataa.json")
.take(1)
.map(values => [values[0].json(), values[1].json()])
.subscribe(values => {
// do something with my values
});
You could You can use concat to combine the observables and return a single observable.
Subscribe to observables in order as previous completes, emit values
changed service code
import 'rxjs/add/operator/concat';
export class PMDataService
{
data(){
return this.dataA().concat(this.dataB());
}
// methods dataA and dataB are unchanged, some of the constructor
}
Component code
ngOnInit(){
this.dataService.data().subscribe((dataJSON) =>
{
this.dataA= dataAJSON[0];
this.dataB= dataAJSON[1];
}
}

Categories