How to use #ngrx/store in component? - javascript

My understanding after reading the documentation is that in order to obtain values from an NgRx store, we have to use selectors. So far, I've had luck doing that, only with a small issue regarding typing, which makes me worried if my implementation is incorrect in any way.
So, let's say I want to retrieve a number value from the store, id for example. In my componentName.component.ts I would access this value like this:
id$ = this.store.select(selectId);
where selectId is defined from another file as:
export const selectData = (state: AppState) => state.data;
export const selectId = createSelector(
selectData,
(state: DataState) => state.id,
)
I am able to access id$ in my HTML component easily by doing {{id$ | async}}, but accessing it from the component class itself has proven a bit more difficult. For starters, the variable type is Observable<number> instead of just number, and that makes it hard to use it in cases where it needs to be of number type, such as when comparing it:
ngOnInit(): void {
console.log(this.id$ === 0);
}
The TypeScript error I get from the code above is:
TS2367: This condition will always return 'false' since the types
'number' and 'Observable ' have no overlap.
And console-logging id$ itself confirms that it is indeed of Observable type, so this leads me to believe I am doing something wrong, but I'm unsure exactly what. Any help is appreciated!!!

id$ is an Observable, and you can access its value by subscribing to it from the component class using subscribe function, or from the component template using async pipe.
In Component Class:
ngOnInit(): void {
this.id$.subscribe(id => console.log(id));
}
In Component Template:
<span>{{ id$ | async }}</span>
Check the official docs of Angular about Observable(s):
https://angular.io/guide/observables

Related

Ngrx "Observable" vs Ng Observable [duplicate]

I'm new to Redux and started with ngrx. I'm unable to understand the meaning of this line of code store.select:
clock: Observable<Date>;
this.clock = store.select('clock');
In very simple terms select gives you back a slice of data from the application state wrapped into an Observable.
What it means is, select operator gets the chunk of data you need and then it converts it into an Observable object. So, what you get back is an Observable that wraps the required data. To consume the data you need to subscribe to it.
Lets see a very basic example.
Lets define the model of our store
export interface AppStore {
clock: Date
}
Import the Store into your component from '#ngrx/store'
Create a store by injecting into the constructor
constructor(private _store: Store<AppStore>){}
Select returns an Observable.
So, declare the clock variable in your component as follows:-
public clock: Observable<Date>;
Now you can do something like follows:-
this.clock = this._store.select('clock');
Wow, this is a big topic. So basically "select" is really a RXJS operator that is used in this case to retrieve the value of a part of the application state object. So say your main app state has a array of users and a array of security functions. "Select" allows you to get a reference to a observable whose value is just that array of users. Before you get into ngrx you really need to study up on Observables and RXJS. I found this article linked off of the main Github project page for ngrx helpful.
https://gist.github.com/btroncone/a6e4347326749f938510
RXJS and redux can be a big topic but I suggest working on your knowledge in small bite size chunks. It took me about 2 months of working with it before I really started to feel comfortable. Even if you don't stay with ngrx, understanding how RXJS works is incredibly useful and is worth the time investment to learn it.
Here is a gist article that also gives a good intro into RXJS.
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
It returns the state called 'clock'.
Here is an example. In the constructor store.select is called, this time with 'todos'.
https://github.com/btroncone/ngrx-examples/blob/master/todos/src/app/todo-app.ts
export class TodoApp {
public todosModel$ : Observable<TodoModel>;
//faking an id for demo purposes
private id: number = 0;
constructor(
private _store : Store<AppState>
){
const todos$ = _store.select<Observable<Todo[]>>('todos');
const visibilityFilter$ = _store.select('visibilityFilter');
...
In the bootstrap, provideStore is given APP_REDUCERS
import {bootstrap} from '#angular/platform-browser-dynamic';
import {TodoApp} from './todo-app';
import {provideStore} from "#ngrx/store";
import * as APP_REDUCERS from "./reducers/reducers";
export function main() {
return bootstrap(TodoApp, [
provideStore(APP_REDUCERS)
])
.catch(err => console.error(err));
}
APP_REDUCERS is all the reducers defined. The todos reducer is defined as follows:
import {ActionReducer, Action} from "#ngrx/store";
import {Todo} from "../common/interfaces";
import {ADD_TODO, REMOVE_TODO, TOGGLE_TODO} from "../common/actions";
export const todos : ActionReducer<Todo[]> = (state : Todo[] = [], action: Action) => {
switch(action.type) {
case ADD_TODO:
return [
...state,
action.payload
];
There are a few ways to do this, and you can compose a list of all your reducers, essentially defining a series of object keys that refer to a reducer object.
Store.select returns an observable that you can subscribe to either in your component or template via '|async'.
This.store.select('keyname') will return the data from store object of 'keyname' property has. you can further look for inner object in store using multiple reducer with StoreModule.forFeature("master", masterReducer) in main module with createSelector
export const getMasterState = createFeatureSelector<myModels.MasterState>('master');
export const getMatserBranchList = createSelector(
getMasterState,
(state: myModels.MasterState): myModels.Branch[] => state.branchList
);

Typescript and react conditional render

I am new to typescript and not an expert in FE development. I've encountered issue that seems pretty basic, but I failed to found any solution. Maybe I just don't know how to google it properly.
In react component I have a button, that is disabled on some condition, which triggers a component's function:
import React, {Component} from 'react';
type DraftCompany = {
id: null
name: string,
};
type Company = Omit<DraftCompany, 'id'> & {
id: number;
};
type Props = {
company: Company | DraftCompany,
onDeleteCompany: (companyId: number) => void,
}
class CompanyRow extends Component <Props> {
handleDeleteCompany = () => {
this.props.onDeleteCompany(this.props.company.id);
};
render = () => {
return (
<div>
<div>{this.props.company.name}</div>
<div>
<button disabled={this.props.company.id === null} onClick={this.handleDeleteCompany}/>
</div>
</div>
)
}
}
export default CompanyRow;
I am getting typescript error on calling this.props.onDeleteCompany(this.props.company.id); that says that there is a chance I will pass null as a parameter. I fully understand why typescript gives me this error, the question is: what would be the best way to deal with this error?
I have found 3 ways:
1) Add 'if' guard
handleDeleteCompany = () => {
if (this.props.company.id) {
this.props.onDeleteCompany(this.props.company.id);
}
};
It works, but I don't like the idea of adding such guards into every function, if someone removes disabled logic, I want to receive console error telling me about it immediately, not to have it be silently swallowed. In my project I have a lot of such code that relies on render, I doubt it is a best practice to add such checks everywhere. Maybe I am wrong.
2) Apply as to field operator:
handleDeleteCompany = () => {
this.props.onDeleteCompany(this.props.company.id as number);
};
It works, but looks kinda hacky.
3) Apply as operator to whole object and pass it to function:
<button disabled={this.props.company.id === null}
onClick={() => this.handleDeleteCompany(this.props.company as Company)}/>
handleDeleteCompany = (company: Company) => {
this.props.onDeleteCompany(company.id as number);
};
It works, but it looks like I am unnecessary passing the value I could have grabbed in function itself from props. I am not sure it is best practice to do such things.
I am sure there should be some pure typescript solution like defining Props type as a union or using conditional types with some combination of any and never. But I haven't figured it out .
Here is a playground:
playground
You can force the compile to assume a value is never null or undefined with the ! operator:
handleDeleteCompany = () => {
this.props.onDeleteCompany(this.props.company.id!);
};
I think based on your requirement
if someone removes disabled logic, I want to receive console error telling me about it immediately
There is a very simple solution that makes perfect sense, simply change your onDeleteCompany type from (companyId: number) => void to (companyId: number | null) => void, then TypeScript will be happy.
It also semantically make sense to you as you want the runtime report this error when companyId is null. Then you should allow companyId with null to be passed in as parameter.

How do I use a withLatestFrom in an effect with selector with props (MemoizedSelectorWithProps) coming from the action

I have a selector with props (of type MemoizedSelectorWithProps). I'd like to use it in an effect inside WithLatestFrom. The thing is - the parameter for the selector (the props) is coming from the action payload. And I can't make the withLatestFrom access the action payload.
I'm using angular 7 (and naturally ngrx7). I've tried using a map to somehow create a new observable, but nothing is working...
these are some demo lines I've wrote here, to simplify my use case:
action:
export const GET_INVENTORY = '[App] Get Inventory';
export class GetInventory implements Action {
readonly type = GET_INVENTORY;
constructor (public branchId: number) {}
}
effect:
#Effect()
getInventory$ = this.actions$.pipe(
ofType(GET_INVENTORY)
withLatestFrom(this.store$.pipe(select(getIsStoreInventoryLoaded, {branchId: action.branchId}))), // this is not working obviously, since action is unknown
switchMap([action, loaded]: [GetInventory, boolean] => {
if (loaded) {
console.log('already loaded inventory for this branch', action.branchId);
} else {
console.log('never loaded inventory for this branch', action.branchId);
}
}
although this is a simplified version of my code, the design is kinda similar in my real project - I have a store with keys per "branch" inventory. say I'm a chain of supermarkets and each branch has it's own inventory page with lots of data and I want to not-fetch again if I already fetched. So if you have a different approach then working with MemoizedSelectorWithProps - feel free to suggest that too.
A simple switchMap or a mergeMap with a combineLatest should do the trick.
For example:
#Effect()
getInventory$ = this.actions$.pipe(
ofType(GET_INVENTORY),
mergeMap(action =>
combineLatest(
of(action),
this.store$.pipe(select(getIsStoreInventoryLoaded, {branchId: action.branchId}))
)
),
tap(([action, loaded]) => {
// The rest of your code...
})
)

Refactoring Angular components from many inputs/outputs to a single config object

My components often start out by having multiple #Input and #Output properties. As I add properties, it seems cleaner to switch to a single config object as input.
For example, here's a component with multiple inputs and outputs:
export class UsingEventEmitter implements OnInit {
#Input() prop1: number;
#Output() prop1Change = new EventEmitter<number>();
#Input() prop2: number;
#Output() prop2Change = new EventEmitter<number>();
ngOnInit() {
// Simulate something that changes prop1
setTimeout(() => this.prop1Change.emit(this.prop1 + 1));
}
}
And its usage:
export class AppComponent {
prop1 = 1;
onProp1Changed = () => {
// prop1 has already been reassigned by using the [(prop1)]='prop1' syntax
}
prop2 = 2;
onProp2Changed = () => {
// prop2 has already been reassigned by using the [(prop2)]='prop2' syntax
}
}
Template:
<using-event-emitter
[(prop1)]='prop1'
(prop1Change)='onProp1Changed()'
[(prop2)]='prop2'
(prop2Change)='onProp2Changed()'>
</using-event-emitter>
As the number of properties grows, it seems that switching to a single configuration object might be cleaner. For example, here's a component that takes a single config object:
export class UsingConfig implements OnInit {
#Input() config;
ngOnInit() {
// Simulate something that changes prop1
setTimeout(() => this.config.onProp1Changed(this.config.prop1 + 1));
}
}
And its usage:
export class AppComponent {
config = {
prop1: 1,
onProp1Changed(val: number) {
this.prop1 = val;
},
prop2: 2,
onProp2Changed(val: number) {
this.prop2 = val;
}
};
}
Template:
<using-config [config]='config'></using-config>
Now I can just pass the config object reference through multiple layers of nested components. The component using the config would invoke callbacks like config.onProp1Changed(...), which causes the config object to do the reassignment of the new value. So it seems we still have one-way data flow. Plus adding and removing properties doesn't require changes in intermediate layers.
Are there any downsides to having a single config object as an input to a component, instead of having multiple input and outputs? Will avoiding #Output and EventEmitter like this cause any issues that might catch up to me later?
personally if I see I need more than 4 inputs+outputs, I will check my approach to create my component again , maybe it should be more than one component and I'm doing something wrong.
Anyway even if I need that much of input&outputs I won't make it in one config, for this reasons :
1- It's harder to know what should be inside inputs and outputs,like this:
(consider a component with to html inputs element and labels)
imagine if you got only 3 of this component and you should comeback to work on this project after 1 or 2 month, or someone else gonna collaborate with you or use your code!.
it's really hard to understand your code.
2- lack of performance. it's way cheaper for angular to watch a single variable rather than watching an array or object. beside consider the example i gave you at first one, why you should force to keep track of labels in which may never change alongside with values which always are changing.
3- harder to track variables and debug. angular itself comes with confusing errors which is hard to debug, why should I make it harder. tracking and fixing any wrong input or output one by one is easier for me rather than doing it in one config variable which bunch of data.
personally I prefer to break my components to as small as possible and test each one. then make bigger components out of small ones rather than having just a big component.
Update :
I use this method for once input and no change data ( like label )
#Component({
selector: 'icon-component',
templateUrl: './icon.component.html',
styleUrls: ['./icon.component.scss'],
inputs: ['name', 'color']
});
export class IconComponent implements OnInit {
name: any;
color: any;
ngOnInit() {
}
}
Html:
<icon-component name="fa fa-trash " color="white"></icon-component>
with this method angular wont track any changes inside your component or outside.
but with #input method if your variable changes in parent component, you will get change inside component too.
I would say it could be OK to use single config objects for Inputs but you should stick to Outputs at all the time. Input defines what your component requires from outside and some of those may be optional. However, Outputs are totally component's business and should be defined within. If you rely on users to pass those functions in, you either have to check for undefined functions or you just go ahead and call the functions as if they are ALWAYS passed within config which may be cumbersome to use your component if there are too many events to define even if the user does not need them. So, always have your Outputs defined within your component and emit whatever you need to emit. If users don't bind a function those event, that's fine.
Also, I think having single config for Inputs is not the best practice. It hides the real inputs and users may have to look inside of your code or the docs to find out what they should pass in. However, if your Inputs are defined separately, users can get some intellisense with tools like Language Service
Also, I think it may break change detection strategy as well.
Let's take a look at the following example
#Component({
selector: 'my-comp',
template: `
<div *ngIf="config.a">
{{config.b + config.c}}
</div>
`
})
export class MyComponent {
#Input() config;
}
Let's use it
#Component({
selector: 'your-comp',
template: `
<my-comp [config]="config"></my-comp>
`
})
export class YourComponent {
config = {
a: 1, b: 2, c: 3
};
}
And for separate inputs
#Component({
selector: 'my-comp',
template: `
<div *ngIf="a">
{{b + c}}
</div>
`
})
export class MyComponent {
#Input() a;
#Input() b;
#Input() c;
}
And let's use this one
#Component({
selector: 'your-comp',
template: `
<my-comp
[a]="1"
[b]="2"
[c]="3">
</my-comp>
`
})
export class YourComponent {}
As I stated above, you have to look at the code of YourComponent to see what values you are being passed in. Also, you have to type config everywhere to use those Inputs. On the other hand, you can clearly see what values are being passed in on the second example better. You can even get some intellisense if you are using Language Service
Another thing is, second example would be better to scale. If you need to add more Inputs, you have to edit config all the time which may break your component. However, on the second example, it is easy to add another Input and you won't need to touch the working code.
Last but not least, you cannot really provide two-way bindings with your way. You probably know that if you have in Input called data and Output called dataChange, consumers of your component can use two-way binding sugar syntax and simple type
<your-comp [(data)]="value">
This will update value on the parent component when you emit an event using
this.dataChange.emit(someValue)
Hope this clarifies my opinions about single Input
Edit
I think there is a valid case for a single Input which also has some functions defined inside. If you are developing something like a chart component which often requires complex options/configs, it is actually better to have single Input. It is because, that input is set once and never changes and it is better to have options of your chart in a single place. Also, the user may pass some functions to help you draw legends, tooltips, x-axis labels, y-axis labels etc.
Like having an input like following would be better for this case
export interface ChartConfig {
width: number;
height: number;
legend: {
position: string,
label: (x, y) => string
};
tooltip: (x, y) => string;
}
...
#Input() config: ChartConfig;
The point of having the Input besides its obvious functionality, is to make your component declarative and easy to understand.
Putting all the configs in one massive object, which will grow definitely (trust me) is a bad idea, for all the above reasons and also for testing.
It's much easier to test a component's behaviour with a simple input property, rather than supplying a giant confusing object.
You're going backwards and thinking like the way jQuery plugins used to work, where you'd call a function called init and then you provide a whole bunch of configuration which you don't even remember if you should provide or not, and then you keep copy pasting this unknown and ever-growing object across your components where they probably don't even need them
Creating defaults is extremley easy and clear with simple Inputs whereas it becomes a little bit messy with objects to created defaults.
If you have too many similar Input, Outputs, you can consider below :
1- You can create a Base class and put all your Input/Outputs that are similar and then extend all your components from it.
export class Base{
#Input() prop1: number;
#Output() prop1Change = new EventEmitter<number>();
#Input() prop2: number;
#Output() prop2Change = new EventEmitter<number>();
}
#Component({})
export class MyComponent extends from Base{
constructor(){super()}
}
2- If you don't like this, you can use composition and create a reusable mixin and apply all your Input/Outputs like that.
Below is an example of a function that can be used to apply mixins, NOTE may not necessarily be exactly what you want, and you need to adjust it to your needs.
export function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
derivedCtor.prototype[name] = baseCtor.prototype[name];
});
});
}
And then create your mixins :
export class MyMixin{
#Input() prop1: number;
#Output() prop1Change = new EventEmitter<number>();
#Input() prop2: number;
#Output() prop2Change = new EventEmitter<number>();
}
applyMixins(MyComponent, [MyMixin]);
3- You can have default properties for inputs so you only override them if you need:
export class MyComponent{
#Input() prop1: number = 10; // default
}
Are there any downsides to having a single config object as an input to a component, instead of having multiple input and outputs?
Yes, when you want to switch to the onpush change detection strategy, which is often needed in bigger projects to mitigate performance issues caused by too many render-cycles, angular will not detect changes that happened inside your config object.
Will avoiding #Output and EventEmitter like this cause any issues that might catch up to me later?
Yes, if you start to move away from #Output and in your template directly operate on the config object itself, then you are causing side-effects in your view, which will be the root of hard-to-find bugs in the future. Your view should never modify the data it get's injected. It should stay "pure" in that sense and only inform the controlling component via events (or other callbacks) that something happened.
Update: After having a look at the example in your post again, it looks like you did not mean that you want to directly operate on the input model but pass event emitters directly via the config object. Passing callbacks via #input (which is what you are implicitly doing) also has it's drawbacks, such as:
your component gets harder to understand and reason about (what are its inputs vs its outputs?)
cannot use banana box syntax anymore
If you want to bundle input parameters as an object, i'd suggest to do it like this:
export class UsingConfig implements OnInit {
#Input() config: any;
#Output() configChange = new EventEmitter<any>();
ngOnInit() {
// Simulate something that changes prop1
setTimeout(() =>
this.configChange.emit({
...this.config,
prop1: this.config.prop1 + 1
});
);
}
}
You are creating a new config object when changing a property.
You are using an Output-Event to emit the changed config object.
Both points ensure that ChangeDetection will work properly (assuming you use the more efficient OnPush strategy). Plus it's easier to follow the logic in case of debugging.
Edit:
Here's the obvious part within the parent component.
Template:
<using-config [config]="config" (configChange)="onConfigChange($event)"></using-config>
Code:
export class AppComponent {
config = {prop1: 1};
onConfigChange(newConfig: any){
// if for some reason you need to handle specific changes
// you could check for those here, e.g.:
// if (this.config.prop1 !== newConfig.prop1){...
this.config = newConfig;
}
}

Store doesn't fire changes to the async pipe

In my app.component.html I create a custom component (contact-table component) that should get a new value (Account) to present after I update the in the store the connected user to be someone else.
app.component.html:
<contact-table [contacts]="connectedAccount$ |async"></contact-table>
app.component.ts:
export class AppComponent implements OnInit
{
private connectedAccount$: Observable<Account>;
constructor(private store:Store<any>) {
this.connectedAccount$ = this.store
.select(state=> state.connectedAccountReducer.connectedAccount);
this.connectedAccount$
.subscribe(account1=> {
//the app prints any new account that comes up. It's working here.
console.log(account1);
});
}
public ngOnInit() {
this.store.dispatch({
type: updateConnectedAccount,
payload: {
connectedAccount: ACCOUNTS[0]
}
});
}
}
The subscribtion in AppComponent class works great and fires any update that comes up.
The problem is that the async pipe in app.component.html does not send the new account to the contact-table component. I think he doesn't get notified for any updates.
I tried to see what is being sent to the contact-table component,
and I saw he only get one value in the first creation of the contact-table component and its undefined. Thats not possibole becouse my reducer function creates an empty Account object for the initial state of my app.
Is there anything you notice that I missed?
Check and make sure you are using Object.assign to create the changes to your state. It shouldn't return a mutated state. Redux/ngrx and the async pipe detect changes when the object hash changes (or at least this is my understanding). I've run into this problem when I wasn't creating a brand new object but accidentally mutating the existing state and returning it.
To quote the redux site -> http://redux.js.org/docs/basics/Reducers.html
We don’t mutate the state. We create a copy with Object.assign(). Object.assign(state, { visibilityFilter: action.filter }) is also wrong: it will mutate the first argument. You must supply an empty object as the first parameter. You can also enable the object spread operator proposal to write { >...state, ...newState } instead.
I also had a async issue with RC2 so if you aren't using RC3 or above I'd recommend upgrading.
Not saying this is it but it is the most likely candidate from my experience. Hope this helps.
Try using the json pipe just to see if the view is getting anything from that store select.
<span>{{connectedAccount$ |async | json}}</span>
You can include ngrx-store-freeze - the problem with mutable state is easy to address.
Another way of how I often debug the dispatching of Actions is to introduce an Effect for logging purpose. This helps to identify problems as well.

Categories