TL,DR; I have successfully passed the output of Async Pipe into a child component. But when the condition fails ( filter => false
), I want to pass in the previously emitted value from an Observable.
Detail;
Using Selectors , I subscribe to two slices of @ngrx/store :
export interface AppState {
data: string[];
pending: boolean; // Are we waiting for a HTTP response?
dirty: boolean; // Has the data been edited locally?
}
...although there are three slices above, this question involves only dirty
and data
.
The reducer has three cases:
AppActions.GET_NEW_DATA:
pending
to true
to update the UI. AppActions.GET_NEW_DATA_SUCCESS:
pending
to false
to update the UI. dirty
to false
because no edits have been made. AppActions.EDIT_DATA:
dirty
to true
because an edit has been made. The code:
export function appReducer(state: AppState = initialState,
action: AppActions.All
): AppState {
switch (action.type) {
case AppActions.GET_NEW_DATA:
// Action results in HTTP request.
return {
...state,
pending: true
};
case AppActions.GET_NEW_DATA_SUCCESS:
// HTTP response, update data!
return {
data: action.payload,
pending: false,
pristine: true,
};
case AppActions.EDIT_DATA:
return {
...state,
data: action.payload,
pristine: false
};
default: {
return state;
}
}
}
As mentioned earlier, using Selectors , I subscribe to two slices of @ngrx/store from within the smart component
@Component({
selector: 'app-smart',
templateUrl: './smart.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SmartComponent {
public data$: Observable<string[]>;
public dirty$: Observable<boolean>;
constructor(private readonly store: Store<AppState>) {
this.data$ = this.store.select(fromReducer.getData);
this.dirty$ = this.store.select(fromReducer.getDirty);
}
}
The smart component uses Async Pipe to detect when a new value is emitted from the Observable and mark the dumb component to be checked for changes.
<!-- From within <app-smart> -->
<app-dumb [data]="data$|async"></app-dumb>
This works as expected, however, I only want to be updated if dirty
is false
- ie when a new HTTP request is made and response received.
I don't want to redraw <app-dumb>
when the data$
Observable emits a change from an editing. In summary, I require the following behavior:
When creating:
data$
emits new value.
dirty$
is false
. <app-dumb>
to be checked for changes. When editing:
data$
emits new value.
dirty$
is true
. <app-dumb>
to be checked for changes. This looked like a prime case for combing Observables:
this.data$ = this.store
.select(fromReducer.getData)
.withLatestFrom(this.dirty$)
.filter(([_, dirty]) => !dirty)
.map(([tree, _]) => tree);
...breaking this down
this.store.select(fromReducer.getData)
grab an Observable from ngrx/store using selectors . data$
result with latest result from dirty$
. dirty$
is false.
<app-dumb>
. data$
to emit a new value, and we should ignore it. The problem is <app-smart>
is contained within a Material Tab , and as such may be removed ( ngOnDestroy
) and recreated ( ngOnInit
) as the user switches tabs. See Life Cycle Hooks .
If and edit has been made, dirty$
is true. If the user then switches tabs and subsequently returns, data$
is filtered out because dirty$
is true
:
filter(([_, dirty]) => !dirty)
I have successfully passed the output of Async Pipe into the child <app-dumb>
component. But. When the condition fails, I want to pass in the previously emitted value from data$
- and this is where I could really do with some help.
try to create the observable of data starting from the dirty observable something like this
this.data$ = this.store.select(fromReducer.getDirty)
.filter(dirty=>dirty)
.flatMap(()=>this.store.select(fromReducer. getData))
In that way you always going to receive new data if the filter of dirty pass
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.