3

The problem appears to be beyond me. I have built store correctly and now I can't retrieve current element from the store.

showDialog(id_clicked) {
    this.id = id_clicked
    this.store.dispatch(new action.loadExampleAction(this.id));
    this.observable$ = this.store.select(selectors.getObject);
}

With this simple example I am updating the "observable" whenever button is clicked, dispatching the new action with currently selected id. Showing this.observable$ in the console shows correctly the currently selected object in its payload. But now when I do this:

tmp: <Object> = null
showDialog(id_clicked) {
        this.id = id_clicked
        this.store.dispatch(new action.loadExampleAction(this.id));
        this.observable$ = this.store.select(selectors.getObject);

        this.observable$.subscribe(object => {
        this.tmp = object
        }, first())
        console.log(this.tmp)
}

The tmp inside subscribe returns previous state. I.e. if I click the object with id = 1 at first it will return "undefined", then when I click the object with id = 2 then it returns the object with id = 1 and so on. Even though the console.log on this.observable$ shows that its payload (value) is correct. I am not sure what is going on. I can't simply retrieve current value from this observable. If anything more about it is needed I'll submit additional info.

ThatKidMike
  • 326
  • 3
  • 11
  • Does this answer your question? [How do I return the response from an Observable/http/async call in angular?](https://stackoverflow.com/questions/43055706/how-do-i-return-the-response-from-an-observable-http-async-call-in-angular) – R. Richards Jul 30 '20 at 17:45
  • No, the problem persist. I moved subscribtion to the constructor to not subscribe the same thing several times but make it as a callback or even launching subscribe method in ths method: "showDialog()" still makes problem. – ThatKidMike Jul 30 '20 at 17:57
  • Can you please recreate this issue in stackblitz? – yurzui Jul 30 '20 at 18:23
  • Probably related to asynchronous/synchronous processing. The dispatch/the handling of that action is probably not done when you do the log at the end. Bind tmp to some element in the dom and inspect that. – Gunnar B. Jul 30 '20 at 18:25

2 Answers2

1

You should avoid breaking the Observable chain wherever possible, and also avoid using local variables. Don't use your tmp variable and keep the observable$ instead, and instantiate it in ngOnInit(). Then, wherever you need to consume observable$, subscribe to it appropriately (generally using the async pipe):

ngOnInit() {
  this.observable$ = this.store.select(selectors.getObject);
}

showDialog(idClicked: string) {
  this.store.dispatch(new action.loadExampleAction(this.id));
}

Later, in a template somewhere:

<ng-container *ngIf="observable$ | async as myStuff">
</ng-container>
Will Alexander
  • 3,425
  • 1
  • 10
  • 17
  • So the code in the template suggest that I subscribe the observable$ with async pipe? I found that before - it works but I find it rather difficult to maintain - it means that any of the methods where I pass the value from observable$ as an argument has to be called from html - is this even the right way to maintain it? – ThatKidMike Jul 30 '20 at 18:42
  • @ThatKidMike the main advantage of using the async pipe is that you have not to subscribe to the observable and you does not need to manage the unsubscription, so, when possible, is the preferred way to manage observable used in templates – ale Jul 30 '20 at 19:29
  • All right I understand - gonna use it as much as I can but what if I have to call some method with arguments taken from observable$ as I mentioned before? Do I have to call it from the template? In my situation it is necessary to pass an argument to the method directly from observable$ and it seems to be not doable for now with the problem I have here, unless it is wise to use template as a method-caller. – ThatKidMike Jul 30 '20 at 19:53
  • What do you need the result from `observable$` for, in this case? – Will Alexander Aug 01 '20 at 07:27
0

Not sure but it seems like you try to get only the first value that come up in subscription by first().

The problem that NgRx use memoization for selectors and can return previous value if state for this selector doesn't change yet. So when you subscribe it's return init value undefined, after the click you set the state to "1", but you didn't get it because you take only the first value in subscription.

Also clicking is async operation so when you click second time on the next object the new subscription created and immediately return the value stored in the store, it's 1, you see it on the screen and only after that because of "click" async nature the new value in the store become "2", but you didn't see it until click next object and see old value.

As the solution you can remove first() option in your code but than you will get two values, previous and next one, the current. To avoid it you can try to use "Resetting Memoized Selectors" by release(); function from the documentation and than filter the 'null' values in pipe like that:

store
  .pipe(
    select(selectValues),
    filter(val => val !== null)
  )
  .subscribe(/* .. */);
Experimenter
  • 2,084
  • 1
  • 19
  • 26