Behaviour subject initial value null?
Asked Answered
S

6

95
private customer: Subject<Object> = new BehaviorSubject<Object>(null);

setCustomer(id, accountClassCode) {
    this.customer.next({'id': id, 'accountClassCode': accountClassCode});
}

getCustomer() {
    return this.customer.asObservable();
}

I'm using this part of code but I'm getting an error that can not find id of null. Is there any solution to get initial value that is not null?

Scutate answered 22/6, 2017 at 7:54 Comment(8)
Did you try an empty object? new BehaviorSubject<Object>({});Chromoprotein
im gettin an error Supplied parameters do not match any signature of call target.Scutate
There's a solution. Do not use BehaviorSubject if you don't need initial value.Arezzo
I dont need inital value but my service is not working if i dont use BehaviorSubjectScutate
Is it an option to use new BehaviorSubject<Object>({}); instead?Veteran
I don't understand, why not just use new Subject<Object>()Moraceous
@Moraceous because if you then have any new subscriptions created afterwards they ever won't get that valueMccullough
@dotbert not really in this case :-) the aim here is not to trigger the subscription handler unless there's a value that needs processing.Mccullough
A
182

The purpose of BehaviorSubject is to provide initial value. It can be null or anything else. If no valid initial value can be provided (when user id isn't known yet), it shouldn't be used.

ReplaySubject(1) provides a similar behaviour (emits last value on subscription) but doesn't have initial value until it is set with next.

It likely should be

private customer: Subject<Object> = new ReplaySubject<Object>(1);
Arezzo answered 22/6, 2017 at 9:0 Comment(15)
The default is 1 if you don't specify it, so new ReplaySubject<Foo>() is better. Specifying the type does nothing at runtime btw, but provides type safety at compile time.Mccullough
If you're interested here's the source code : github.com/ReactiveX/rxjs/blob/master/src/internal/…Mccullough
Prefer ReplaySubject.create<Object>(1)Oas
@RomanTikhonov You likely don't want to do that. It's not the same thing. See https://mcmap.net/q/225061/-difference-between-new-observable-and-rx-observable-create .Arezzo
@Mccullough the current default (Nov 2019) is infinity. setting 1 is required. rxjs-dev.firebaseapp.com/api/index/class/ReplaySubjectSanction
Thank you a lot. How can I map pairwise result to object with keys: {one: value, two: value}?Sukhum
@Mamkad Likely with basic destructuring, map(([one, two]) => ({ one, two }))Arezzo
How to get the Data out?Insectivorous
@Insectivorous The same way as with any other observable, by using subscribe() or converting it to a promise.Arezzo
Since BehaviorSubject where updating for every same value. I made a Set extension method that checks the current value. Also I believe I need current value in other placesInsectivorous
@Insectivorous Was the question about ReplaySubject or BehaviorSubject? I'm not totally sure what is your case, but BehaviorSubject has value property, exactly because it always has a value. Other subjects and observables don't. In case you want to be able to always get current value from other kinds of subjects, you need to extend them and expose it in the same way that BehaviorSubject does, github.com/ReactiveX/rxjs/blob/master/src/internal/…Arezzo
I would like to have access to current value but I don't want the initial subscribe trigger. Thanks. I just realized this was marked as rxjs I am .Net. Unfortunately the C# implementation loos more complicated: github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/…Insectivorous
@Insectivorous I see. I don't deal with net, but probably the option would be the same, to extend replay subject class and override next method to expose current value on an instance.Arezzo
@EstusFlask Thank you for your great post. You helped me allot. Attaching example from my code for others to user (tested working) private _user$= new ReplaySubject<User>(); this._user$.next(user) ;Polymer
ReplySubject will not hold value in it, every time we need to subscribe and get it. we can assign empty value as an initial value in behaviour subject private customer = new BehaviorSubject<Customer>({} as Customer);Cassatt
P
19

Since the object can be null, a better choice is to infer the type like this

 private customer = new BehaviorSubject<Customer|null>(null);
Pah answered 1/1, 2022 at 19:6 Comment(2)
This is the answer.Dexterdexterity
private customer = new BehaviorSubject<Customer>({} as Customer); could be better which will avoid defining null to the assigning variables.Cassatt
V
5

Try structuring this way your service:

Service:

@Injectable()
export class MyService {
    customerUpdate$: Observable<any>;

    private customerUpdateSubject = new Subject<any>();

    constructor() {
        this.customerUpdate$ = this.customerUpdateSubject.asObservable();
    }

    updatedCustomer(dataAsParams) {
        this.customerUpdateSubject.next(dataAsParams);
    }
}

Remember to add MyService to providers.

Where you update your client (if this is the case), you do something like this:

Component (The one that triggers):

constructor(private myService: MyService) {
        // I'll put this here, it could go anywhere in the component actually
        // We make things happen, Client has been updated
        // We store client's data in an object
        this.updatedClient = this.myObjectWithUpdatedClientData;  // Obj or whatever you want to pass as a parameter
        this.myService.updatedCustomer(this.updatedClient);
    }

Component (The one that is Subscribed):

this.myService.customerUpdate$.subscribe((updatedClientData) => {
            // Wow! I received the updated client's data
            // Do stuff with this data!
        }
    );

From what I understood, you are trying to pass data from 1 component to another. You get your Client's data and send it over your App to another component, right? That's why I posted this solution.

If you are interested in other types of subscriptions, read this:

Angular 2 special Observables (Subject / Behaviour subject / ReplaySubject)

Voyage answered 22/6, 2017 at 9:0 Comment(1)
Hm, I copy-pasted the exact solution and it doesn't work for me for some reason, very strange.Joost
E
4

Another way is to use pipe to filter until a non-null value is received. It can be done with takeWhile also.

.pipe(filter(val => !!val)).subscribe(x => {});

Euchology answered 10/2, 2022 at 3:34 Comment(1)
... or pipe(skipWhile(val => !!val))Poleax
B
2

I have found many cases where I want the simplicity of ReplaySubject(1) but also want the value property of BehaviorSubject, i.e. it stores your most recent value for retrieval without having to subscribe. And using BehaviorSubject was always annoying given the need for an initial value that gets broadcast, a condition I rarely wanted. To that end I created my own CurrentSubject.

import { ReplaySubject } from "rxjs";

export class CurrentSubject<T> extends ReplaySubject<T> {
    value: T;

    set(value?: T) {
        this.value = value;
        this.next(value);
    }
}

I wanted to override the next method but I couldn't get that to work for some reason, so I settled on a method called set. My override attempt looked like this...

export class CurrentSubject<T> extends ReplaySubject<T> {
    value: T;

    next(value?: T) {
        this.value = value;
        super.next(value);
    }
}

I don't know why overriding didn't work.

Bifilar answered 29/7, 2020 at 19:13 Comment(5)
what was the problem? do you need to call super() inside the constructor here?Folder
The problem with attempting to override next? I dunno, I never solved that and instead opted for the set property as I outlined. If you want to try explicitly adding a constructor and calling super() just to see if that somehow works let me know! :)Bifilar
I also had the same problem when I attempted to override next. Through debugging, I noticed that without overriding, when next is called on the extension class, nextInfiniteTimeWindow is actually executed by ReplaySubject, but when overriding next and calling super.next, it's next from the Subject class that get called instead of nextInfiniteTimeWindow. I didn't bother fixing the problem so I instead wrapped the ReplaySubject class in my own class and exposed the same methods.Wadmal
The same is happening for me as well that I can't override the next method with the latest versions of TypeScript & RxJS. Any idea?Eldin
Explained the problem here- https://mcmap.net/q/225062/-override-the-subject-next-methodEldin
T
0

you can initialize empty

final xpto = BehaviorSubject<List< XPTO >>.seeded([]);

Tartuffery answered 4/5, 2024 at 8:40 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.