I feel like I am missing something here. I have a service that grabs some data. I convert it to a promise and then try and work on the data in a seperate method.
When once it hits the method I loose the ability to access my objects that i would normally access from this.whatever. If I leave all the code from the addJobsToTree in the then block, it works fine. I can also access this from every where else in the component. I'm sure i'm doing something dumb but can't figure it out.
ngOnInit(){
this._scheduleDataService.getSavedScheduleData(this.buildDateStringFromCalendar(),1004)
.toPromise()
.then(this.addToJobsTree);
}
private addToJobsTree(res){
for(let xx of res){
this._sharedService.jobs.push(xx); //Comes back as cannot read _sharedService of null
console.log(this._sharedService.jobs);
}
}
It's because you reference a function and you lose the context of the function. To fix that you need to explicitly link the function to an object.
You can use either the bind method:
ngOnInit(){
this._scheduleDataService.getSavedScheduleData(this.buildDateStringFromCalendar(),1004)
.toPromise()
.then(this.addToJobsTree.bind(this); // <-----
}
(note: here is the drawback to using the bind method with TypeScript: https://basarat.gitbooks.io/typescript/content/docs/tips/bind.html)
or an arrow function to fix that:
ngOnInit(){
this._scheduleDataService.getSavedScheduleData(this.buildDateStringFromCalendar(),1004)
.toPromise()
.then((data) => { // <-----
this.addToJobsTree(data);
});
}
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
For code shortness I'm trying to store some similar functions inside an array, and call the correct function depending on a numeric key.
Each function returns a Promise, that actually wraps an Observable, I'm fecthing some data and pushing it to the corresponding data array.
Calling each individual functions works fine, but when I'm trying to call them from the functions array (event based call), the "this' keyword refers to the function array instead of the class.
Here is the code:
public currentKey = 0;
...
constructor(private api: ApiService) {
}
ngOnInit() {
this.loadTrending(0); //THIS WORKS!
this.loadNews(0);
....
this.loadingMethods = [this.loadTrending, this.loadNews, this.loadCrowdfunding, this.loadUpcoming]
}
loadData(event) {
this.loadingMethods[this.currentKey](this.offsets[this.currentKey]).then(bool => {
event.target.complete(); // THIS DOESN'T WORK
});
}
public loadTrending(offset: number): Promise<boolean> {
return new Promise((resolve, reject) => {
this.api.trending(offset).subscribe(trending => { //'THIS' REFERS HERE TO THE ARRAY
this.gameLists[this.TRENDING_KEY].push(...trending.list);
resolve(true);
});
});
}
Is there a way to achieve this function call and have this refering to the class as usual ?
EDIT : I'm already using ECMA6 arrow functions that works througout all my project, so I don't think How to access the correct `this` inside a callback? is the answer here. Even though it is well explained there.
Error is:
core.js:15724 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'trending' of undefined
TypeError: Cannot read property 'trending' of undefined
at news.page.ts:68
at new ZoneAwarePromise (zone.js:910)
Thanks.
Answer given by #ConnorsFan & #Pointy.
Need to to use .bind() in my array since calling a method from an array will normally change the context of 'this' even though I'm using arrow functions.
this.loadingMethods = [this.loadTrending.bind(this), ...]
Thanks.
I have a service that returns me the user's data according to the Token stored in the localStorage and everything is returned correctly until I get to my component.
The real problem is that I have the following code in my component.ts file:
Apparently, at least everything should work out for me. However, in the console it is possible to notice that even after I have assigned the return to my user property it is printed as undefined. As shown in the image below.
When trying to use in the HTML template I get messages saying that it was not possible to load data from the user property. I have already tried using async pipe like this: (user $ | async) as user. I tried to use the safe navigation like this:user?.email.
But it did nothing and I have no idea why this happens. Any help will be welcome!
User$ represents a stream, should be assigned this way:
export class {
// a stream type
user$: Obserable<User>;
ngOnInit() {
// a stream type
this.user$ = this.clienteHeaderService.getUser();
}
}
and the template add |async pipe.
this.user is not immediately available after you call subscribe(), it is very possible that the getUser() hasn't emitted any result by the time console.log(this.user) is called.
If you just wanted to see what's in this.user, you may try it in the callback of subscribe()
this.clientHeaderService.getUser().subscribe((response) => {
this.user = response;
console.log(this.user); // this.user should be available here
})
On the template side, you should be able to just access the user via {{ user }}.
I'd also suggest to use share your minimum reproducible code at https://stackblitz.com/, to get help more easily.
Subscribe almost work like promise in javascript and it has a callback
.subscribe(response=>this.user=response)
Callback are pushed to the end of current event loop .So you are accessing
console.log(this.user)
before callback in your subscribe get executed.
instead
try
.subscribe((response) => {
this.user=response;
//you can access value of this.user here or call other function here to
//access this.user
console.log(this.user);
})
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.
Ok, this is a quick one, i'm kinda exhausted already and am confusing myself :D
I'm working with angular2 and RxJS Observables.
I have a service with a property "data", which is an Observable that get's set in the constructor, and a method to return this observable to subscribe to.
export class test{
private data: Observable<Object>
constructor(private http: Http){
this.data = this.http.get(url).map( res => res.json());
}
getData(): Observable<Object>{
return this.data
}
}
I have worked wit replaySubjects a while ago to always emit all values of the sequence to new subscribers. However, with the code above the Observable seems to emit it's latest value to new subscribers. Is this intended?
test(i: number) {
if (i > 0) {
setTimeout( () => {
this.dataService.getData().subscribe( (data) => {
this.debug.log(data);
this.test(i-1);
});
}, 2000);
}
}
test(4)
I get a value for every iteration. I am confused, 'cause a year ago when i wanted that behaviour, i got no new values when subscribing 'too late'.
Essentially, i just want to cache the result of the http.get, and deliver the same value to all subscribers, instead of making a new http request for every subscription (returning the http.get(url).. in getData())
I know this question is a bit old, but the answers seem to me quite confusing.
The Observable you return from the method getData() is just that, an Observable. So every time a consumer subscribes it gets the response. So it is working fine, but it is indeed making a new request every time.
In order to cache the result there are plenty of ways to do it depending on the behavior you want. To just cache a single request I would recommend t use the #publishBehavior operator:
export class test{
private data: Observable<Object>;
constructor(private http: Http){
this.data = this.http.get(url)
.map(res => res.json())
.publishBehavior([])
.refCount();
}
getData(): Observable<Object>{
return this.data;
}
}
The parameter passed to the publishBehavior is the initial value. With this two operators, the request will be made when the first subscriber arrived. Next subscribers will get the cached answer.
In others answers the use of Subjects has been suggested. The publishBehavior is using subjects under the hood. But to directly call next() it is consider bad practice unless there is no other remedy, and thats not the case for this scenario in my opinion. Even if you use Subjects directly, it will be wise to return an Observable to the Components by using the #asObservable() operator so the component won't have access to the next, error and complete methods.
No. You need to use Subject for this. It has a method next() to which you will send your newly arrived property so that it pushes it to the subscribers.
In addition to this, you should create a service that will be a singleton. Whenever your components instantiate it in a constructor, they will receive the object already formed with all the data. There will be no need to fetch the data every time.
Also, instead of instantiating your data in the constructor, implement OnInit and do the calls to the server from there.