Typescript Angular - Observable: how to change its value?
Maybe I'm missing something. I can't find a simple tutorial for Observable and its syntax. I'm working with Angular, I need to call a function (defined in a component) from a service. I read this solution. But I can't figure out how to change the value in the Observable created in the service (maybe the creation is not the best method).
I have a component like in the solution:
@Component({
selector: 'my-component',
...
)}
export class MyComponent {
constructor(myService:MyService) {
myService.condition.subscribe(value => doSomething(value));
}
doSomething(value) {
if (value) // do stuff
else // other stuff
}
}
and this is my service:
import { Injectable } from '@angular/core';
import { Observable} from 'rxjs/Observable';
@Injectable()
export class MyService {
private condition: Observable<boolean>;
constructor() {
this.condition= new Observable(ob => {ob.next(false); })
// maybe ob.next is not the best solution to assign the value?
}
change() {// how can i change the value of condition to 'true', to call
// the doSomething function in the component??
}
}
Solution 1:
From the comments on my other answer (preserved since it may be helpful to someone), you seem to want to leverage the power of something to emit values over time.
As DOMZE proposed, use a Subject, but here's a (trivial) example showing how you could do that. Though there are evidently some pitfalls to avoid in using Subject directly, I'll leave that up to you.
import { Component, NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { Observable, Subject } from 'rxjs/Rx';
@Component({
selector: 'my-app',
template: `
<div>
<h2>Open the console.</h2>
</div>
`,
})
export class App {
constructor() {}
let subject = new Subject();
// Subscribe in Component
subject.subscribe(next => {
console.log(next);
});
setInterval(() => {
// Make your auth call and export this from Service
subject.next(new Date())
}, 1000)
}
@NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
Plunker
In my humble opinion, for this scenario, I don't see why a simple Service/Observable doesn't suffice, but that's none of my business.
Further reading: Angular 2 - Behavior Subject vs Observable?
Solution 2:
I would like to explain how to use Observables in case you want to update the value but I will not use your example. I will just show a few lines of code written in ES5 with explanations.
var updateObservable; // declare here in order to have access to future function
var observable = rx.Observable.create(function (observer) {
observer.next(1);
observer.next(2);
observer.next(3);
// attach a function to the variable which was created in the outer scope
// by doing this, you now have your 'updateObservable' as a function and the reference to it as well
// You will be able to call this function when want to update the value
updateObservable = function (newValue) {
observer.next(newValue);
observer.complete();
};
});
// Suppose you want to update on click. Here actually can be whatever (event) you want
vm.click = function () {
// you can call this function because you have a reference to it
// and pass a new value to your observable
updateObservable(4);
};
// your subscription to changes
observable.subscribe(function(data) {
// after 'click' event will be performed, subscribe will be fired
// and the '4' value will be printed
console.log(data);
});
The main idea here is that if you want to update the value of an Observable you have to do this inside the 'create' function. This will be possible if you declare a function inside this 'create' function.
Solution 3:
Managing login state
For this implementation, you only need one Service. In it, you would make your backend request to see if the user has a session, and then you can save that in a class variable in the Service. Then, return that variable if it's set, or return the result of a REST call directly.
For example:
export class AuthenticationService {
private loggedIn: boolean = null;
constructor(private http: Http) { }
getUserSession(credentials): Observable<boolean> {
if (this.loggedIn !== null) {
return Observable.of(this.loggedIn);
} else {
return this.http.get('/authenticate?' + credentials)
.map((session: Response) => session.json())
.catch(e => {
// If a server-side login gate returns an HTML page...
return Observable.of(false);
});
}
}
And then in the Component, just subscribe to the Observable as usual and act on it on-demand.
There are other methods of achieving this with Observable.share()
and Observable.replay()
Observable Syntax
To answer part of the question regarding the syntax of an Rx Observable in Angular2 (should someone Google it), the generic form is:
In a Service:
return this.http.get("/people", null)
.map(res.json())
.catch(console.error("Error in service")
And in a Component, by way of example:
this.someService.getPeople()
.subscribe(
people => this.people,
error => console.warn('Problem getting people: ' + error),
() => this.doneHandler();
);
Formally:
interface Observer<T> {
onNext(value: T) : void
onError(error: Error) : void
onCompleted() : void
}
The first function is called when the "next" value is received. In the case of REST calls (most common case) this contains the entire result.
The second function is an error handler (in the case Observable.trow() was called in the service).
The last is called when the result set was has, and takes no parameters. This is where you can call your doSomething() function.
Solution 4:
I suggest you change your condition to be a Subject. The subject is both an observer and an observable. You then will be able to emit a value.
See https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/subjects.md