0

Here I am getting data from storage

sendRequest() {
    this.userservise.getAllUsers(...)
    console.log("bedore")
    this.query.selectAll().subscribe(users => {
        console.log("inside")
        this.users = users
    })
    console.log("after")
}

Here I write them in storage

  getAllUsers(...) {
    var url = ...
    return this.http.get<User[]>(url, { withCredentials: true }).subscribe(u => {
      this.store.set(u)
    });
  }

The strange behavior is that the "insede" function fires many times, with one more time on each new request. look scan

The query to the database is always sent one, as it should be. Also, if you exclude the Akita, everything will work fine.

  sendRequest() {
    this.userservise.getAllUsers(...).subscribe(users => {
        this.users = users
      })
  }

  getAllUsers(...): Observable<User[]> {
    var url = ...
    return this.http.get<User[]>(url, { withCredentials: true })
  }

Store:

import { Injectable } from '@angular/core';
import { EntityState, EntityStore, StoreConfig } from '@datorama/akita';
import { User } from '../user';

export interface TodosState extends EntityState<User, number> { }

@Injectable({
  providedIn: 'root'
})
@StoreConfig({ name: 'todos' })
export class TodosStore extends EntityStore<TodosState> {
  constructor() {
    super() ;
  }
}

Query:

import { Injectable } from "@angular/core";
import { QueryEntity } from "@datorama/akita";
import { TodosState, TodosStore } from "./store";
    
@Injectable({
    providedIn: 'root'
})
export class TodosQuery extends QueryEntity<TodosState> {
    constructor(protected override store: TodosStore) {
        super(store);
    }
}

What could be the problem?

Tidus213
  • 1
  • 4
  • Can you change your getAllUsers() function to as follows: getAllUsers(...) { var url = ... return this.http.get(url, { withCredentials: true }).pipe(tap(u => { this.store.set(u) })); } – Haseeb Ahmed Apr 25 '22 at 17:27
  • @HaseebAhmed This only works if I subscribe to the getAllUsers method. If you try q.selectAll().subscribe then it doesn't even initiate sending the request. But it will probably be right, my double-subscription syntaxis is wrong i think – Tidus213 Apr 25 '22 at 18:04
  • I think I might have an idea what might be going wrong. I'll get back to you in a little bit. – Haseeb Ahmed Apr 26 '22 at 08:44
  • Can you please check my implementation at (https://stackblitz.com/edit/angular-ivy-ux5gfg)? I think you're using RxJS observable in some odd manner which might be causing this. I can't tell what's wrong until I have a look at the complete code but maybe my example will help you understand the appropriate way of doing it. – Haseeb Ahmed Apr 26 '22 at 15:24
  • @HaseebAhmed Hello! If i just use todos$ in html, then it works fine, but it's not convenient to work with Observable because you can't use *ngIf and access fields. And if i try `this.todos$.subscribe(u => { this.users = u; console.log(1);});` then the situation is the same. – Tidus213 Apr 26 '22 at 16:37
  • Sorry @Tidus213 but you're wrong about that. using observable direct in the HTML is very beneficial and you can things like the following: on some parent, do the following "*ngIf="todos$ | async as todos"" and you can do something like this then: "{{ todos.length }}" and "{{ todos | json }}". – Haseeb Ahmed Apr 26 '22 at 18:12
  • @HaseebAhmed I read about it, indeed, using async directly in the template instead of subscribing seems like a good pattern. Of course it is interesting why in my example there was such behavior, but I will consider that it is simply absolutely wrong. Thank you =) – Tidus213 Apr 26 '22 at 18:48
  • I'll be sharing my suggestion as an answer below. Please accept it :-D I'll also make an example of how you were trying to do it. – Haseeb Ahmed Apr 26 '22 at 19:43

1 Answers1

0

I think there are a few things wrong with what you're doing. I think you're using RxJS observable in some odd manner which might be causing this. I can't tell what's wrong until I have a look at the complete code but maybe my example will help you understand the appropriate way of doing it.

In your service, you should have a getUsers method as follows:

public getUsers(): Observable<User[]> {
    var url = "";
    return this.http.get<User[]>(url).pipe(tap((data) => {
        this._store.add(data);
    }));
}

Then in your component.ts file, do the follows:

public readonly todos$ = this._query.selectAll();

And finally, in the HTML template, you can use it as follows:

<ol>
  <li *ngFor="let user of todos$ | async">
    {{ user.name }}
  </li>
</ol>

Incase you want to access properties of the User[], you can do it as follows in the HTML:

<p *ngIf="todos$ | async as todos">
    You have: {{ todos.length }}. <br />
    JSON: {{ todos | json }}
</p>

You can also see a running example of the above on Stackblitz.

Update:

I think I also realized, what you're doing wrong. Below is the example that you shared in OP.

sendRequest() {
    this.userservise.getAllUsers(...)
    console.log("bedore")
    this.query.selectAll().subscribe(users => {
        console.log("inside")
        this.users = users
    })
    console.log("after")
}

The reason why you're getting inside one more time than the last time, every time is because you're subscribing to a new observable every time without discarding the old one. Your problem can be fixed as follows but it still won't be a recommended approach.

sendRequest() {
    this.userservise.getAllUsers(...)
    console.log("bedore")
    this.query.selectAll()
      .pipe(first())
      .subscribe(users => {
        console.log("inside")
        this.users = users
    })
    console.log("after")
}
Haseeb Ahmed
  • 643
  • 5
  • 16