Hi i'm trying to create an Observable that will have values emitted to it from another subscription, it this case an ngrx Store Reducer.
export class IsolatedAgentService {
missionList$: Observable<any>; // I need this observables subscription to emit to calculatedValue$
calculatedValue$:Observable<any>; // I need this observable to get its values from the subscription of missionList$ subscription
missionList:any;
constructor(
private _store:Store<any>
){
this.missionList$ = this._store.select(root_reducers.getMissionList).pipe(skip(1));
this.missionList$.subscribe((val:any)=> {
let mostIsolatedCountry:any; //will hold value of calculation
this.missionList = val;
mostIsolatedCountry = this.getMostIsolatedCountry(this.missionList);
// I want tot emit mostIsolatedCountry to another subscription
});
}
What I'm trying to do:
export class IsolatedAgentService {
missionList$: Observable<any>;
calculatedValue$:Observable<any> = Observable.create((observer)=>{
// moved this line here from the previous missionList$ subscription
let calculated:any = this.getMostIsolatedCountry(this.missionList);
observer.next(calculated)
});
missionList:any;
calculatedValue:any;
constructor(
private _store:Store<any>
){
this.missionList$ = this._store.select(root_reducers.getMissionList).pipe(skip(1));
this.missionList$.subscribe((val:any)=> {
let mostIsolatedCountry:any;
this.missionList = val;
this.calculatedValue$.subscribe((value)=>{
this.calculatedValue = value;
});
});
}
Currently, I'm basically seting a class property in one subscription, then inside that same subscription, after setting the class property, I'm calling the second subscription which calculates the value from that set class property.
This does not feel right, and I'm sure its not the way to do it, but I'm lacking in my rxjs/observable knowledge at this point.
Note! Im not interested in emiting the calculated value trough a Store Action, I want an Observable that is specific to the class instance.
Here's the answer to your question:
export class IsolatedAgentService {
missionList$: Observable<Mission[]>;
calculatedValue$:Observable<any>;
constructor(
private _store:Store<any>
){
this.missionList$ = this._store.select(root_reducers.getMissionList).pipe(skip(1));
this.calculatedValue$ = this.missionList$.pipe(
map( missions => this.getMostIsolatedCountry(missions) )
);
}
}
or even
this.calculatedValue$ = this.missionList$.pipe(
map( this.getMostIsolatedCountry )
);
See more about NGRX facades: https://medium.com/#thomasburleson_11450/ngrx-facades-better-state-management-82a04b9a1e39
Why you arent exposing & using observables why even subscribe in a service?
What you should have instead
export class IsolatedAgentService {
missionList$: Observable<Mission[]>;
calculatedValue$:Observable<any>;
constructor(
private _store:Store<any>
){
this.missionList$ = this._store.select(root_reducers.getMissionList).pipe(skip(1));
this.calculatedValue$ = this._store.select(root_reducers.getMissionCalculatedValue).pipe(skip(1));
}
}
And a selector for doing that calculation you need.
export const getMissionCalculatedValue= createSelector(
getMissionList,
(missionList) => {
// do the calculations here
return calculationResult;
}
);
Related
I have seen the following code in an angular application. In the code, a Subscription is being used to subscribe and unsubscribe. I don't understand why a Subscription is used. Is there any benefit in using this pattern?
public routeChangeSub$: Subscription;
this.routeChangeSub$ = this.route.firstChild.paramMap
.subscribe((map: ParamMap) =>
this.getRouteParams(map));
this.routeChangeSub$.unsubscribe();
getRouteParams(map: ParamMap): number {
const characterId = map.get('id');
let id: number = null;
if (characterId) {
this.character$ = this.store.pipe(
select(selectCharacterById, { id: characterId })
);
id = parseInt(characterId, 10);
}
return id;
}
Update:
How this can be different from
this.route.firstChild.paramMap
.subscribe((map: ParamMap) =>
this.getRouteParams(map));
getRouteParams(map: ParamMap): number {
const characterId = map.get('id');
let id: number = null;
if (characterId) {
this.character$ = this.store.pipe(
select(selectCharacterById, { id: characterId })
);
id = parseInt(characterId, 10);
}
return id;
An observable (which is the type of route.firstChild.paramMap) will not emit anything unless something is subscribed to it.
In this file, the author explicitly subscribes to paramMap to trigger a state change by calling getRouteParams(). Then they immediately unsubscribe. If they didn't unsubscribe, the subscription will continue to run which may cause state issues and memory leaks.
A far simpler solution is to use the take(1) operator. This will take an emitted value (the 1 is the number of values to take) then will send a complete signal to the subscription. This causes the subscription to automatically unsubscribe.
this.route.firstChild.paramMap.pipe(
take(1),
tap(map=>this.getRouteParams(map))
).subscribe();
Because we're using take(), we don't need to assign the subscription to a property. The observable will emit one value, and will invoke getRouteParams() before unsubscribing.
Note: If you're not aware, tap() is the operator to use if you want to apply an effect to any state property outside of the observable.
I have a class with more than 30 observable attributes. Each time my server receives a payload containing these 30 attributes I call the next() method for all the corresponding attributes of the instance, so far so good.
The problem is that, sometimes, I have to check for an attribute's value, outside the scope of the observer that subscribed to that observable attribute.
What comes to mind is that I have to have duplicate attributes for everything, one is the observable and the other one is a stateful attribute to save the arriving values for later consumption.
Is there some way to avoid this with a method like: Observable.getCurrentValue()?
As requested, some example code
class Example {
public subjects = {
a1: new Subject<any>(),
a2: new Subject<any>(),
a3: new Subject<any>(),
a4: new Subject<any>(),
a5: new Subject<any>()
}
public treatPayload(data: any) {
for (const prop in data) {
if (data.hasOwnProperty(prop) && prop in this.subjects){
Reflect.get(this.subjects, prop).next(data[prop])
}
}
}
public test() {
const a1_observable = this.subjects.a1.asObservable()
const a2_observable = this.subjects.a2.asObservable()
const example_payload_1 = {
a1: "first",
a2: "second",
a10: "useless"
}
const example_payload_2 = {
a1: "first-second",
a2: "second-second",
a10: "useless-second"
}
a1_observable.subscribe((a1_new_value: any) => {
const i_also_want_the_last_value_emitted_by_a2 = a2_observable.last_value() // of course, this doesn't exist
console.log(a1_new_value)
console.log(i_also_want_the_last_value_emitted_by_a2)
})
this.treatPayload(example_payload_1)
this.treatPayload(example_payload_2)
}
}
So, is there a way to retrieve the correct value of i_also_want_the_last_value_emitted_by_a2 without a pipe operator? I think it would be a problem to emit all values I could possibly use in a subscriber within a pipe of the a2_observable.
You could use BehaviorSubject.value, where you could store your server data.
Hi i have a global service for several applications and i wish to make a method with several subscribes in order to stock and initialize all my datas and i wish make a subscribe to this method in my appComponent but i don't know how to make that
In my service
private initData(isLogged: boolean) {
this.http.get('/api/conf').subscribe(
conf => {
this.http.get('api/param').subscribe(
tokResp => {
this.appParams.token = tkResp.queoval;
this.appParams.culture = tkResp.culture;
this.appParams.GMT = tkResp.gmt;
this.http.get('/api/trad').subscribe(
trad => {
this.label = trad
// return an Observable
}
)
}
)
}
)
}
In my AppComponent
this.service.initData().subscribe(
result => {
this.test = result
}
How can i make that? I can't find the information in the documentation. Thank you for your help. It's important for my work, i used so much time to research for nothing :(
So since you want to make multiple async requests one after the other, you should use the observable function ".flatMap" (this is very similar to a Promises ".then"). The ".flatMap" function allows you to wait until the first request is completed before you continue.
So in your case, you would want your service to look something like this:
private initData(isLogged: boolean) {
return this.http.get('/api/conf').flatMap(
conf => {
return this.http.get('api/param');
}
).flatMap(
tokResp => {
this.appParams.token = tkResp.queoval;
this.appParams.culture = tkResp.culture;
this.appParams.GMT = tkResp.gmt;
return this.http.get('/api/trad');
}
).flatMap(
trad => {
this.label = trad;
return trad;
}
);
}
This function has all of the async requests chained together through ".flatMap" so that they are only called after the previous request completes.
The component file looks fine and should work with this new service.
As a general note, you should never subscribe to the observable inside
of the service. You should instead use functions like map, flatMap,
forkJoin ...
I'm wanting to implement an Observable / Subject with 3 particular attributes
Remember last emitted value and be able to surface it via a getter (BehaviorSubject)
Only emit when value changes
It must have a strong type such that the getter is known to be available by a consumer (aka. BehaviorSubject.getValue())
I'm thinking of just extending BehaviorSubject but want to make sure I'm not introducing any potential gotchas based on my novice understanding.
export class DistinctUntilChangedBehaviorSubject<T, TValue> extends BehaviorSubject<T> {
constructor(
initialValue: T,
private _distinctKeySelector?: (value: T) => TValue,
private _comparer?: _Comparer<TValue, boolean>
) {
super(initialValue);
}
public subscribe() {
// I'm particularly interested in knowing if this has any gotchas.
// Mostly things like creating subscriptions that don't get disposed as expected.
return super.distinctUntilChanged(
this._distinctKeySelector,
this._comparer
).subscribe.apply(this, arguments);
}
}
So 2 questions:
Does this seem like a reasonable approach / are there any gotchas here?
Is there another preferred way of doing this?
I do not know really why, but I tend to prefer composition over extension.
So I would do something along these lines
import {BehaviorSubject} from 'rxjs';
export class BehaviourSubjectAugmented<T> {
bs: BehaviorSubject<T>;
constructor(initialValue: T, private comparer: (p: T, q: T) => boolean) {
this.bs = new BehaviorSubject(initialValue);
}
getValue() {
return this.bs.getValue();
}
asObservable() {
return this.bs.asObservable()
.distinctUntilChanged(this.comparer);
}
complete() {
return this.bs.complete();
}
next(value: T) {
return this.bs.next(value);
}
}
Turns out my original idea causes a call stack exceeded issue. I'm assuming that distinctUntilChanged must call subscribe internally thus causing infinite recursion.
I ended up finding a simpler way to get what I needed by simply adding a method to an ISubject instance.
function distinctUntilChangedBehaviorSubject(
initialValue: number
): ISubject<number> & { getValue(): number } {
const observer = new BehaviorSubject<number>(initialValue);
const observable = observer.distinctUntilChanged();
const subject: ISubject<number> = Subject.create(
observer,
observable
);
return Object.assign(
subject,
{
getValue: () => observer.getValue()
}
);
}
I am building a service which exposes an Observable. In this service I receive external function calls which should trigger a next call on the Observable so that various consumers get the subscribe event. During Observer constructor I can call next and everything works great, but how can I access this outside of the constructor so that external triggers can fire next calls?
private myObservable$: Observable<any>;
During service init I do
this.myObservable$ = new Observable(observer => {
observer.next("initial message");
}
Then in other methods of the same service I want to be able to execute something like
this.myObservable$.observer.next("next message");
The above obviously doesn't work, but how can I accomplish this goal?
I'm assuming I'm missing something basic since there must be a way to emit further messages outside of the Observable's initial constructor
You should create a Subject for that
this.myObservable$ = new Subject();
And then you can call at any point:
this.myObservable$.next(...);
Or use subscribe:
this.myObservable$.subscribe(...)
Actually Subject is used for both publisher and subscriber, and here I think you need only to publish your value, so simply use Observable.
By using observable, assign Subscriber to class level variable and then use it, like below code
subscriber: Subscriber<boolean>;
public observe(): Observable<boolean> {
return new Observable<boolean>(subs => {
this.subscriber = subs;
});
}
public callNext() {
if (this.subscriber) {
this.subscriber.next();
this.subscriber.complete();
}
}
Two ways:
Make myObservable$ public:
public myObservable$: Observable;
Encapsulate the observable in a subject stream, and provide a helper to call next:
export class TestService {
public myObservable$: Observable;
private _myObservableSubject: Subject;
constructor() {
this._myObservableSubject = new Subject();
this.myObservable$ = this._myObservableSubject.asObservable();
}
public NextMessage(message?: string): void {
this._myObservableSubject.next(message);
}
}
Observable: You have to call the next() function from inside the constructor and only one time you can subscribe
message = new Observable((observer)=>{
observer.next(9);
})
this.messsage.subscribe((res)=>{
console.log(res)
})
output: 9
Subject: You have to call next() function from outside the constructor and multiple times you can subscribe.
The subject does not store any initial value before subscribe.
messsage = new Subject()
this.messsage.next(3)
this.messsage.subscribe((res)=>{
console.log(' A '+res)
})
this.messsage.next(4)
this.messsage.next(5)
this.messsage.subscribe((res)=>{
console.log(' B '+res)
})
this.messsage.next(6)
output:
A 4
A 5
A 6
B 6
BehaviorSubject: You have to call next() function from outside the constructor and multiple times you can subscribe.
The BehaviorSubject does store only one initial value before subscribe.
messsage = new BehaviorSubject ()
this.messsage.next(3)
this.messsage.subscribe((res)=>{
console.log(' A '+res)
})
this.messsage.next(4)
this.messsage.next(5)
this.messsage.subscribe((res)=>{
console.log(' B '+res)
})
this.messsage.next(6)
output:
A 3
A 4
A 5
B 5
A 6
B 6
I ended up combining a couple of things:
olsn's answer, which nicely demonstrates Subject's ease of use
Ravi's answer, which correctly points out that we only want an Observable exposed
a more functional approach, because this and Class give me the shivers
TypeScript typings for generic use
const createObservableWithNext = <T>(): {
observable: Observable<T>;
next: (value: T) => void;
} => {
const subject = new Subject<T>();
const observable = subject.asObservable();
const next = (value: T) => subject.next(value);
return {
observable,
next,
};
};