Passing value from one RxJS operator to another - rxjs

Here is my code:
#Injectable()
export class TraitementDetailEffects {
ingoing_loadDetail: { traitementID: number, obs: Promise<any> };
#Effect()
loadTraitementDetail$: Observable<Action> = this.actions$.pipe(
ofType(ETraitementDetailActions.loadTraitementDetail),
map((action: LoadTraitementDetail) => action.payload),
switchMap((traitementID) => {
if (this.ingoing_loadDetail && this.ingoing_loadDetail.traitementID === traitementID) {
return this.ingoing_loadDetail.obs;
}
const obs = this.traitementsService.loadDetail(traitementID);
this.ingoing_loadDetail = {traitementID: traitementID, obs: obs};
return obs;
}),
map(result => {
this.ingoing_loadDetail = null;
//here I don't have access to traitementID :'(
return new LoadTraitementDetailSuccess(traitementID, result);
})
);
constructor(
private actions$: Actions,
private traitementsService: TraitementsService
) {
}
}
I'm trying to pass the variable or value traitementID to the last map.
I tried to avoid the last map with an async await but then I get a weird errors "Effect dispatched an invalid action" and "Actions must have a type property" (FYI all my actions have a type property).

Try to bake this id into observable's resolve, like:
switchMap((traitementID) => {
return this.traitementsService.loadDetail(traitementID).pipe(
map(detail => ({detail,traitementID}))
);
}),
map(({detail,traitementID}) => {
...
})

Related

Observable subscribe vs pipe - rxjs

I have following function in service to retrieve data:
public getData(): Observable<dataObj> {
const endpointURL = 'xyz';
return response = this.responseHandler
.makeRequest<dataObj>('get', endpointURL)
.pipe(
startWithTap(() => this._requestsInProgress.next(++this.requestsInProgress)),
finalize(() => this._requestsInProgress.next(--this.requestsInProgress))
)
}
in different place I have something like this and I call that function:
export class SearchAndFilterService {
private _data: BehaviorSubject<dataObj> = new BehaviorSubject(null);
private _searchTerm: BehaviorSubject<string> = new BehaviorSubject(null);
private _filterTerms: BehaviorSubject<string[]> = new BehaviorSubject(null);
public readonly data$: Observable<dataObj>;
constructor(
private service: Service
) {
this.data$ = combineLatest([
this._data.asObservable(),
this._searchTerm.asObservable(),
this._filterTerms.asObservable()
]).pipe(
map((tuple) => {
const [data, searchTerm, filterTerms] = tuple;
if (data) {
return {
...data,
array: this.searchAndFilterData(
data?.array,
searchTerm,
filterTerms
)
};
}
return data;
}),
);
}
public updateDonorAppointments() {
this.service.getData().pipe(tap(
(data) => this._data.next(data)
))
}
I thought that pipe should create a subscription to returned observable and this will work. However instead of pipe I must use .subscribe to make it work. Like this:
public updateDonorAppointments() {
this.service.getData().subscribe(
(data) => this._data.next(data)
)
}
Am I missing something? Or is this approach totally wrong?
Thanks

Angular11 asyncvalidator custom validator error not added to reactive form errors list

I have a reactive form and adding a custom async validator to check if the values entered is unique by checking the array of available values. The validator is being invoked, but the validation error 'duplicate' is not getting added to the form field errors and not displaying in the template. Not sure what i am doing wrong here.
component.ts
private registerFormGroup(): void {
this.titleField = new FormControl(
{ value: this.auditTemplate.title, disabled: true },
[Validators.required],
[TemplateNameValidator.createValidator(this.auditTemplateService)]
);
this.templateForm = this.formBuilder.group({
title: this.titleField,
tags: [this.auditTemplate.tags]
});
}
name validator:
export class TemplateNameValidator {
static createValidator(auditTemplateService: AuditTemplateService): AsyncValidatorFn {
console.log("Static factory call");
return (control: AbstractControl): Observable<ValidationErrors | null> => {
if(isEmptyInputValue(control.value)) {
return of(null);
} else {
return control.valueChanges.pipe(
debounceTime(500),
distinctUntilChanged(),
switchMap((name: string) =>
auditTemplateService.isNameUnique(name)
.pipe
(
tap(response => console.log('inside switchmap', response)),
map(isUnique => !isUnique ? { 'duplicate' : true } : null),
catchError(() => of(null))
)
)
);
}
};
}
}
function isEmptyInputValue(value: any): boolean {
return value === null || value.length === 0;
}
in the template html, if i try to display the error, it is not displaying anything:
<span>Duplicate: </span>{{templateNameField.errors?.duplicate}}<br>
Thanks
AsyncValidators are used every time the AbstractControl state is changed, so you don't need to use the control.valueChanges observable.
Additionally, the observable you're returning from the validator function should send a complete signal after sending null or an error. If you return a hot observable, then the async validator will not work.
Try replacing control.valueChanges.pipe( with of(control.value).pipe(. Also, if your service method .isNameUnique() doesn't emit a complete signal, add take(1) before your the first tap() operator.
return (control: AbstractControl): Observable<ValidationErrors | null> => {
if (isEmptyInputValue(control.value)) {
return of(null);
} else {
return of(control.value).pipe(
distinctUntilChanged(),
debounceTime(500),
switchMap((name: string) =>
auditTemplateService.isNameUnique(name).pipe(
take(1),
tap((response) => console.log("inside switchmap", response)),
map((isUnique) => (!isUnique ? { duplicate: true } : null)),
catchError(() => of(null))
)
)
);
}
};

rxjs: subscribing to observable in map

My first observable returns an array of Persons. I want to update each person of that array with a list of clients from second observable. How do I do that? So far I have this:
const json: Person[] = [new Person('Alice'), new Person('Bob')];
const resultsObservable = new Observable<string[]>(subscriber => {
setTimeout(() => {
subscriber.next(['Client1', 'Client2', 'Client3']);
subscriber.complete();
}, 1000);
});
of(json).pipe(
switchMap( dataArray => {
return from(dataArray);
}),
map((x: Person) => {
resultsObservable.subscribe(r => {
x.clients = r;
});
return x;
}),
).subscribe(value => {
console.log(value);
});
}
Person:
export class Person{
name: string;
clients?: string[];
constructor(name: string) {
this.name = name;
}
}
But the problem is that return happens before the values are set, so at the end value of person.clients is undefined. How do I fix this? Or what is a better way to achieve what I'm trying to do?
Ok I think I found what I was looking for:
const result = persons.pipe(
mergeMap(p => resultsObservable.pipe(map(clients => {
p.clients = clients;
return p;
}))),
);
result.subscribe(p => console.log(p));

Nested dispatch function does not get update props

app.js
const mapStateToProps = (state) => {
return {home:state}
}
const mapDispatchToProps = (dispatch) => {
return {
guestLogin: (data)=>{dispatch(guestLogin(data)).then(()=>{
dispatch(initiateTrans(stateProps.home))
})},
};
}
const mergeProps = (stateProps, dispatchProps, ownProps) => {
return Object.assign({}, ownProps, stateProps, dispatchProps,{
initiateTrans: () => dispatchProps.initiateTrans(stateProps.home),
})
}
Action.js
export const guestLogin= (state)=>{
var data={
'email':state.email,
'name':state.name,
'phone_number':state.ph_number,
'phone_code':state.country_code
}
return function(dispatch) {
return dataservice.guestSignup(data).then(res => {
dispatch(afterLoggedGuest(res))
}).catch(error => {
throw(error);
});
}
}
function afterLoggedGuest(result) {
return {type: guestLoginChange, result};
}
export const initiateTrans= (updatedState)=>{
return function(dispatch) {
return dataservice.initiateTransaction(updatedState).then(res => {
console.log("initiateTransaction",res)
}).catch(error => {
throw(error);
});
}
}
Reducer.js
if(action.type === guestLoginChange){
return {
...state,guestData: {
...state.guestData,
Authorization: action.result.authentication ,
auth_token: action.result.auth_token ,
platform: action.result.platform
} ,
}
}
I am having two api requests.. After first api request success i want to update state value then pass that updated state to another api request..
I tried to get the updted props
how to dispatch the initiateTrans with update props
I need to update value at api request success in call back i need to call one more request with updated state value
currently i am not able to get the update props value
I think this is a good use case for thunk (redux-thunk), which is a middleware that allows you to execute multiple dispatches in an action.
You will need to apply the middleware when you configure the initial store (see docs on link above). But then in your actions, you can wrap the code with a dispatch return statement, which gives you access to multiple calls. For example:
export const guestLogin= (state)=>{
return dispatch => {
var data={...} // some data in here
return dataservice.guestSignup(data).then(res => {
dispatch(afterLoggedGuest(res))
}).catch(error => {
throw(error);
// could dispatch here as well...
});
}
}

Angular 2 Observable get into an Infinite loop

I'm very new to Angular2!. We have a search method which create an async observable and executing it. When provide a valid input, it works good. But an invalid input is causing an Infinite loop. Please let me know if you see the issue.
Please see the code below.
const ngPromiseToObservable = <T>(p: ng.IHttpPromise<ng.IHttpPromiseCallbackArg<T>>): Observable<ng.IHttpPromiseCallbackArg<T>> => {
const o = new Subject();
p.catch((e) => o.error(e));
p.then((v) => o.next(v));
return o;
};
export class HttpObservable
{
public static $inject = [
"$rootScope",
"$http",
"HttpErrors",
];
constructor(
private $rootScope: ng.IScope,
private $http: ng.IHttpService,
private httpErrors: Subject<Object>
) {}
handleHttpResponseErrors<T>(o: Observable<T>): Observable<T>
{
return o.do(
_.noop,
(e: HttpResponseError) => this.httpErrors.next(e),
_.noop
);
}
applyAsyncOnAction<T>(o: Observable<T>): Observable<T>
{
let applyAsync: () => void = () => {
return this.$rootScope.$applyAsync();
};
return o.do(applyAsync, applyAsync, applyAsync);
}
// tslint:disable-next-line:no-any
post<T>(url: string, data: any, config?: Object): Observable<T>
{
return this.handleHttpResponseErrors(this.applyAsyncOnAction(ngPromiseToObservable(this.$http.post(url, data, config)).map((x) => x.data)));
}
// tslint:disable-next-line:no-any
put<T>(url: string, data: any, config?: Object): Observable<T>
{
return this.handleHttpResponseErrors(this.applyAsyncOnAction(ngPromiseToObservable(this.$http.put(url, data, config)).map((x) => x.data)));
}
get<T>(path: string): Observable<T>
{
return this.handleHttpResponseErrors(this.applyAsyncOnAction(ngPromiseToObservable(this.$http.get(path)).map((x) => x.data)));
}

Resources