1
Within my anguular app , i ve this service :

@Injectable()
export class myService{

  myBehaviouSubject= new BehaviorSubject("");


  setData(){
     this.myBehaviouSubject.next("123");
  }

}

Inside my app.component , i m able to get the value , but i want to keep it readonly or editable only inside the service itself , i want to prevent to push any data from component (.next('DATA'))

    @Component({

    })
    export class AppComponent implements OnInit {

      constructor(public myService : MyService) { }

    getData(){
      // GET VALUE
      this.myService.myBehaviouSubject.value
    }

    unwantedMethodToSetValue(){
           // SET VALUE -> i want to prevent this
           this.myService.myBehaviouSubject.next("unwanted value")

    }

}

Suggestions ?

firasKoubaa
  • 6,439
  • 25
  • 79
  • 148

4 Answers4

4

You can keep the observable inside service only by declaring it as private field of a class.

@Injectable()
export class myService {

  private myBehaviouSubject = new BehaviorSubject("");

  // Use this observable inside the app component class.
  myBehaviouSubjectObservable = myBehaviouSubject.asObservable();

  setData() {
    this.myBehaviouSubject.next("123");
  }
}

@Component({

})
export class AppComponent implements OnInit {

  constructor(public myService: MyService) {}

  getData() {
    // You can subscribe to observable and can get value here
    this.myService.myBehaviouSubjectObservable.subscribe((value) => {
      console.log(value);
    })
  }

  unwantedMethodToSetValue() {
    // SET VALUE -> you cannot do this here now.
    this.myService.myBehaviouSubject.next("unwanted value")

  }

}
Jasdeep Singh
  • 7,901
  • 1
  • 11
  • 28
2

Use property access modifiers:

@Injectable()
export class MyService{
  private myValueSubject: BehaviorSubject<string> = new BehaviorSubject<string>("");
  public readonly myValueObservable: Observable<string> = this.myValueSubject.asObservable();

  public setData() {
     this.myValueSubject.next("123");
  }
  public getData(): string {
    return this.myValueSubject.value;
  }
}

Instances of MyService will not have a publicly accessible subject.

I usually try to avoid a method like getData, favoring subscriptions to the related observable. If I ever find myself writing those kinds of methods, it's a warning flag to re-evaluate my architecture. If you just want to store a value and get/set it with methods, use a plain old private property. The entire purpose of the subject is defeated if you are only ever getting the value through a method like getData()

Check out the documentation for typescript classes, which discusses access modifiers: https://www.typescriptlang.org/docs/handbook/classes.html

Chris Baker
  • 49,926
  • 12
  • 96
  • 115
  • This achieves the same thing as my answer except you're able to override myValueObservable on accident. Not sure what extra this contributes ? – Boyen Feb 20 '20 at 18:19
  • @Boyen Well, I started typing this probably at the same time as you were typing yours. It isn't typical to patrol a question after you've answered and guard it against similar answers. Not sure what that contributes. – Chris Baker Feb 20 '20 at 18:21
  • I'm not trying to guard anything. I just attempted to see if your answer contains something mine does not, or if I understand yours wrong, allowing both of us to learn. No need to feel attacked and retaliate. – Boyen Feb 20 '20 at 18:25
  • And as it turns out, I did understand yours wrong. It does achieve the exact same thing as mine, in a different way. My statement about being able to override myValueObservable was incorrect, as it is marked as readonly. You could have told me that instead of attacking my merits. – Boyen Feb 20 '20 at 18:28
  • I believe it does not, it just returns the subject typed as an observable ( which is what as Observable does). No new instance is created – Boyen Feb 20 '20 at 18:32
  • https://stackblitz.com/edit/angular-rxjs-playground-qiwfms?file=app/app.component.ts I objectively don't. – Boyen Feb 20 '20 at 18:36
  • I do still see where you're coming from, I've upvoted your answer. Odd conversation we've had. Text does not read emotion well. – Boyen Feb 20 '20 at 18:41
1

The traditional answer : If you return the Subject as an observable, you disallow .next() calls.

But in your case, you also want direct access to the current value without subscribing, so you could add a getter for that too.

@Injectable()
export class myService{

  private readonly myBehaviouSubject = new BehaviorSubject("");


  setData(){
     this.myBehaviouSubject.next("123");
  }
  public get myObservable$(): Observable<string>{
    return this.myBehaviourSubject;
  }
  public get currentValue(): string{
    return this.myBehaviourSubject.value;
  }

}
Boyen
  • 1,429
  • 2
  • 15
  • 22
  • 2
    The `asObservable` is actually redundant, as the TS notation informs, that result is observable thus not having `next` nor `value` properties/functions – Akxe Feb 20 '20 at 18:12
0

https://stackblitz.com/edit/angular-protected-rxjs-subject

in this solution which I hope meet you needs:

  • be aware that there is no subscription
  • fetching updates handled manually
  • Property 'myBehaviourSubject' is private and only accessible
    within class 'TestService'.
HoseinGhanbari
  • 1,058
  • 1
  • 11
  • 23