I had this query:
this.firestore.collection('name', ref => ref.limit(1)).get().subscribe(x => {
console.log(x);
});
That didn't return any data
Just for a test I changed that to this a and it works:
this.firestore.collection('name', ref => ref.limit(1)).valueChanges().subscribe(x => {
console.log(x);
});
However I want to use .get(), any ideas what is the problem with that?
The problem is that .subscibe method deals with Observable. It's simlar mechanizm like in Promise, but different.(reference)
According to this reference valueChanges():
Returns an Observable of document data. All Snapshot
metadata is stripped. This method provides only the data
While get() takes snapshot using Promise mechanism. So you cannot use subscribe with get.
I hope it will help!
Related
Given this method:
public logIn(data:any): Observable<any> {
this.http.get('https://api.myapp.com/csrf-cookie').subscribe(() => {
return this.http.post('https://api.myapp.com/login', data);
});
}
I would like it to return that nested observable, so that my calling code can use it like so:
this.apiService.logIn(credentials).subscribe(() => {
// redirect user to their dashboard
});
without needing to know about the first /csrf-cookie request. Obviously the above doesn't work - but I'm struggling to understand how to make the inner HTTP request wait for the outer one to finish AND be returned by the method.
you should use switchMap see the documentation on switch map
public logIn(data:any): Observable<any> {
return this.http.get('https://api.myapp.com/csrf-cookie').pipe(
switchMap(x => this.http.post('https://api.myapp.com/login', data))
);
}
with rxjs nested subscribes are generally not a good idea. There are many great operators within the library that will get you around it. In this case above where one call depends on another switchMap(...) is the best fit.
Also the code has been modified to return the observable not the subscription
I'm banging my head against the wall with observables. Almost all of the documentation I can find is in the older rxjs syntax.
I have an API call which is an observable. I'm calling it elsewhere and subscribing to it - trying to populate a table with the data from this GET request.
If I simply console.log my getData function, it logs the subscription rather than my data.
I can successfully console.log data within the .subscribe function, but I want to use data outside of .subscribe().
How do I extract data out of the .subscribe() function and use it elsewhere? Or, must all of my logic be contained within the .subscribe() function to use data?
getData2() {
return this.m_dbService.get('api/myApiPath').subscribe(
data => (console.log(data)), //This properly logs my data. How to extract `data` out of here and actually use it?
error => { throw error },
() => console.log("finished")
);
}
workbookInit(args){
var datasource = this.getData2(); // this returns the subscription and doesn't work.
}
just return the HTTP req from getData() and subscribe it inside the workbookInit function.
getData2() {
return this.m_dbService.get('api/myApiPath')
}
workbookInit(args){
this.getData2().subscribe(
data => {
var datasource = data
},
error => { throw error },
() => console.log("finished")
}
What you probably want to do is to populate another Observable with the data so that you can access it elsewhere in your project without the need for calling the API more than once.
To do this, you create what is known as a Subject (in this case a BehaviorSubject) and you can populate that with data when your API call returns a response.
Then, in order to access this data elsewhere, you can create a "get" function to return the Subject (which is itself an Observable) whenever you need the data.
Here is an example:
my-data.service.ts
myData: BehaviorSubject<number> = new BehaviorSubject<number>(0);
callApi() {
this.dbService.get('apiUrl').subscribe(
(data) = > this.myData.next(data) // Assuming data is a 'number'
);
}
getMyData() {
return this.myData.asObservable();
}
Now to use this in a component:
this.myService.getMyData().subscribe(
(data) => { /* Use the value from myData observable freely */ }
);
Or you could rely on the Angular async pipe (which is a very convenient method for dealing with observables in your code).
You should not subscribe to the Observable inside getData2. Return it as is instead, then do the following:
var dataSource;
this.getData2().subscribe(res => dataSource = res);
Please note that the variable dataSource will be set when the request is done (asynchronously), so you can't use it immediately in the same block scope.
If you want to use it immediately, then put your code inside the subscription.
If you have an observable that provides data to populate a table, the best way is not to use subscribe(), but use the observable directly in your html template by using the async pipe. You'll have less to worry about and your code will be much simpler.
When I'm trying to get data I got undefined, but in one second after recalling the method again I get what I want. How I understand, I just wait for a response and when I try to return an object, I have nothing because it is on his way to me. .findOne() hasn't callback, what I can do in this situation?
handleLogin = () => {
Meteor.loginWithPassword(this.state.loginField,this.state.passwordField,(error)=>{
if (!error) {
Meteor.subscribe('xxx')
let data = Meteor.collection('xxxy').findOne();
console.log(data);
}
}
}
You might want to take a look at this section of documentation. It says
The onReady callback is called with no arguments when the server marks the subscription as ready.
Basically Meteor.subscribe() allows you to include callback which is called when the subscription is ready. It might look like below.
Meteor.subscribe('xxx', function () {
const data = Meteor.collection('xxxy').findOne()
console.log(data)
})
I have an array of observables which was created in a loop. And then merged all these observables using merge, and subscribed to the merged observable. I could not find a way to retrieve context of observable where it was created (in loop). Here is code
let observable = Rx.Observable.bindNodeCallback(request);
let streams = _(['a', 'b', 'c', 'd'])
.someMoreLodashStuff()
.map(val => {
// HERE SOMEHOW I WANT TO BIND CONTEXT (e.g. loop val),
// SO THAT SUBSCRIBER CAN KNOW THE EXACT LOOP STATE
// WHEN RECEIVING RESULT
return observable(mutate(val))
})
.value();
Rx.Observable
.merge(...streams)
.subscribe(
(res) => {
// HERE I WANT TO GET CONTEXT (e.g. val)
}, (err) => {
// HERE I WANT TO GET CONTEXT (e.g. val)
},
() => {
//on complete stuff
});
Update (as asked by #martin)
Since #martin asked about purpose of this binding and what problem I am trying to solve, so I will describe the real problem.
Pupose and real problem
I am trying to crawl list of websites (passed as query params), fetch their titles and render them in an html and return the html back to user. This is part of my open source repo, where solved this exact problem using node.js callbacks, async.js waterfall and promises. Now solving it using rxjs. Its just a way to learn different async techniques. This is file from github repo where using rxjs to solve this problem
If you want to retain a reference to the input state you could use the flatMap overload which takes a resultSelector function to create a tuple containing the merged input + output state:
// given a function which can return the body of the page requested
function doRequest(url) : Observable<string>
const urls = Rx.Observable.from([a,b,c])
.flatMap(
a => doRequest(url),
(a,res) => ({ url: a, body: res})
)
.subscribe(resTuple => console.log(`url ${resTuple.url} returned ${resTuple.body}`)
Im fairly new to RxJs and I would like to understand what the best way is to work with Rx in combination with Promises.
What I want to create is a service in Angular that acts much as an event dispatcher pattern and emits an event once a promise is complete. What I also require is that, if there are no (event) subscribers the observable never gets called. The last thing I want to happen is that any subsequent subscribers to the observable get the same result without triggering another request to the server.
I have managed to implement my own solution here:
// ... CountryService code
var COUNTRIES_LOADED = Rx.Observable
.create(function (observer) {
$http
.get('/countries')
.then(function (res) {
observer.onNext(res);
}, function (err) {
observer.onError(err);
})
.finally(function () {
observer.onCompleted();
});
})
.shareReplay();
Now anytime I subscribe a new "listener" to subject the observable will be pulled. Any new subscribers will get the value cached without touching the server again.
So inside my "consumer" (Angular Directive) I would like to do something like this:
// ... countryInput directive code:
COUNTRIES_LOADED.subscribe(function (response) {
// Fill in countries into scope or ctrl
scope.countries = response.countries;
});
Any future subscribers to the COUNTRIES_LOADED observer MUST NOT trigger an $http request. Likewise, if the directive is never included on the page, $http will never get called.
The solution above works, however I am not aware of the potential drawbacks and memory implications of this approach. Is this a valid solution? Is there a better / more appropriate way to achieve this using RxJs?
Many thanks!
Use Rx.Observable.fromPromise(promise)
fromPromise:
Converts a Promises/A+ spec compliant Promise and/or ES2015 compliant
Promise or a factory function which returns said Promise to an
Observable sequence.
example:
var source = Rx.Observable.fromPromise(promise);
var subscription = source.subscribe(
function (x) {
console.log('Next: %s', x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
update
rxjs6 method is from
update
As of rxjs6 you can use from()
Did you tried to use the fromPromise() API of rxjs5 ?
Check it's documentation here !
I found the answer here (Just slightly differently named)
rxjs using promise only once on subscribe
So for my example the answer is as simple as:
var loadCountries = function () { return $http.get('/countries'); };
var observable = Rx.Observable.defer(loadCountries).shareReplay();
This is how you can use Observables
Lets say you have a method called getuser(username).
//Returns an observable
getUser(username){
return $http.get(url)
.map(res => res.json());
}
And you can use it as below
getUser.subscribe(res => console.log(response));
BUT if you want to use promises
//Returns an Promise
//Donot forget to import toPromise operator
getUser(username){
return $http.get(url)
.map(res => res.json())
.toPromise();
}
And you can use it as below
getUser.then(res => console.log(response));