NGXS @Select usage with state model
Asked Answered
H

3

5

When using an NGXS @Select decorator what is the right way to access properties defined on the state model.

E.g with the following state defined:

export interface UserStateModel {
 firstname: string;
 lastname: string;
}

@State<UserStateModel>({
  name: 'user',
  defaults: {}
})
export class UserState {..}

In a component if I want to select the user state like this:

..export class MyComponent {

  @Select(UserState) user$: Observable<UserState>;

   ngOnInit(){
     this.user$.subscribe(u => {
         //do something with user state
         console.log(u.firstname);
     });
   }   
}

I get typescript errors as the firstname property doesn't exist on UserState (as it's defined on the related model type). If I'm referencing the property in the component html template then I don't have any issue.

There is a related discussion around selector usage but I just want to confirm what I should expect with the current version (and if I'm doing this correctly!).

I'm using "@ngxs/store": "^3.0.0-rc.2",

Harmon answered 1/5, 2018 at 1:13 Comment(1)
I am now wondering if the right way is to declare my selector as @Select(UserState) user$: Observable<UserStateModel>.Harmon
S
15

The observable decorated by the @Select decorator will be of the model's data type and not the state class. ie:

export class MyComponent {

  @Select(UserState) user$: Observable<UserStateModel>;

   ngOnInit(){
     this.user$.subscribe(u => {
         //do something with user state
         console.log(u.firstname);
     });
   }   
}

Another note is that I recommend the use of the async pipe in your template so that Angular manages the subscription and unsubscription for you.

Shortchange answered 2/5, 2018 at 6:54 Comment(1)
Thanks @Mark for confirming that from my earlier comment. Re: async pipe, yes have been using that where possible. I'm trying out NGXS on an existing Angular app, so using the pipe in component templates, but I have some services that are subscribers, so I think I need to subscribe manually there (no template).Harmon
D
5

Take a look at this runnable demo project I just created.

It provides a demo to select allBooks$ and thickBooks$ from a BookStateModel that contains books: Book[].

Things to note:

  • @Selector() is declared (memoized) in BookState.ts so that it can be used elsewhere.
  • These memoized selectors can be used via @Select(Xxx).

Hope this helps.

Danielson answered 2/5, 2018 at 14:44 Comment(2)
Thanks for that demo link!Harmon
Invaluable. Thank you.Scots
W
2

Your comment on your question is the correct way to select a whole state container.

An alternative (and in my opinion better solution) is using a root-state model to infer types in Selectors. With those you can select only the necessary state attributes and still have type-safety.

My thoughts on the currently posted solutions:

  • Memoized Selectors: That is a lot of overhead to write a memoized selector for every unmodified state attribute you would want to select. I'd also expect that there's a small performance drawback for memoized selectors, that's probably also the reason why it is not an official solution for the types issue.
  • Selecting the whole state: Super inconvenient since you'd rarely need a whole state (e.g. in your example, select only the firstname of your UserState). Most of the time you'd want to select a specific state attribute and bind that into your view or subscribe to it. You'll gain less overhead and clearer / cleaner code.
Wray answered 14/8, 2019 at 12:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.