3
votes

I am trying to learn @ngrx/data by creating simple TODO app

I created simple ToDoDataService to override default HTTP

@Injectable()
export class TodosDataService extends DefaultDataService<Todo> {

  constructor(httpClient: HttpClient, httpUrlGenerator: HttpUrlGenerator) {
    super('Todo', httpClient, httpUrlGenerator);
  }

  getAll(): Observable<Array<Todo>> {
    console.log('asfsfsfsf')
    return this.http.get('https://jsonplaceholder.typicode.com/todos')
      .pipe(
        map((res: any) => res)
      );
  }

  getWithQuery(query): Observable<Array<any>> {
    console.log('asfsfsfsf', query);
    return this.http.get(`https://jsonplaceholder.typicode.com/todos?${query}`)
      .pipe(
        map((res: any) => res)
      );
  }    
}

In the route-resolver i am doing like this

@Injectable()
export class TodosResolver implements Resolve<boolean> {

  constructor(private todoService: TodoEntityService) {

  }
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>  {

    return this.todoService.loaded$
      .pipe(
        tap(loaded => {
          if (!loaded) {
            this.todoService.getAll();
          }
        }),
        filter(loaded => !!loaded),
        first()
      );
  }

}

This is working fine and store/entities is updated

But in my component I am doing like this

export class HomeComponent implements OnInit {

  loading$: Observable<boolean>;

  todos$: Observable<Todo[]>;

  constructor(
    private todoService: TodoEntityService
  ) { }

  ngOnInit() {
    console.log('dsdf');
    this.todos$ = this.todoService.entities$
      .pipe(
        tap(todos => {
          console.log(todos);
          this.loadMore();
          console.log(todos);

        }),
        map((todos: any) => todos),
        first()
      );
  }

  loadMore() {
    this.todoService.getWithQuery('_start=20&_limit=5');
  }

}

Here API is calling but entities is still showing old data. Not sure what i am doing wrong,

Please help

1
Good action hygiene - use a distinct, traceable and event driven names - something like "[HomeComponent ONINIT] ONINIT" then create effect that calls loadMore(). Otherwise you'll end up with just a QUERY_ALL that is hard to trace, especially of you call it in multiple places. Similarly with the resolver "[TodosResolver RESOLVE] RESOLVE TODOS"Andrew Allen

1 Answers

2
votes

Welcome on @ngrx/data.

I suggest you to review the code snippet inside ngOnInit. Here, you call loadMore method which implies a new Http request, each time the collection is updated.

Your code should look like this:

ngOnInit() {
  // keep a local reference to observable of entities
  this.todos$ = this.todoService.entities$;

  // request a first load on view init
  this.loadMore();
}

In this way, the code inside TodosResolver is not really needed.

If you want to implement a Load More feature, you should react to another event (like scrolling or a click on button), and then call again loadMore.

Your observables like entities$ or loaded$ are just pipelines of data, and you shouldn't dispatch action when new values are emitted. If really needed, prefer to write a @ngrx/effect, it's done for that.

If you want to change the data or its format, you can use the Observable pipe for that, but not for calling a new action.

For example, this is ok:

this.todos$ = this.todoService.entities$.pipe(
  map(entities => entities.map(entity => ({
    ...entity,
    authorName: entity.authorFirstname + ' ' + entity.authorLastName
  })));
);

Hope my answer is clear and will help you a little.