Reputation: 93
I have a service in which Im casting my http payload to an Observable array, I would like to be able to call a method within that service to act on that observable. In my case I would like to confirm an ID exists in the array of Observable ID's. Ive done some research on stackoverflow and I think its because im mishandling the return of the find operator( as I understand it returns an observable ) .
Service :
@Injectable({
providedIn: 'root'
})
export class RuleFieldsService {
baseUrl: string;
relativeUrl: string;
httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
private rulefieldIdSub = new BehaviorSubject<any[]>([]);
private rulefieldIdStore: { ruleFieldIds: any } = { ruleFieldIds: [] };
rulefieldIds$ = this.rulefieldIdSub.asObservable();
constructor(private readonly http: HttpClient, @Inject(APP_BASE_HREF)
private readonly baseHref: string)
{
this.baseUrl = this.baseHref + environment.baseAPIUrl;
};
getRuleFields() {
this.relativeUrl = 'rulefields';
return this.http.get(this.baseUrl + this.relativeUrl);
}
getList(){
this.relativeUrl = 'rulefields';
this.http.get(this.baseUrl + this.relativeUrl + '/enabledropdown').subscribe(data => {
this.rulefieldIdStore.ruleFieldIds = data;
this.rulefieldIdSub.next(Object.assign({}, this.rulefieldIdStore).ruleFieldIds);
});
}
checkRuleFieldType(ruleFieldId) {
console.log('check method')
this.rulefieldIds$.pipe(find((id: any) => id === ruleFieldId)).subscribe(items => {
console.log("ID is in the list")
});
}
}
Component :
@Component({
selector: 'app-rules-tab-wrapper',
templateUrl: './rules-tab-wrapper.component.html',
styleUrls: ['./rules-tab-wrapper.component.scss']
})
export class RulesTabWrapperComponent implements OnInit {
public selectedIndex;
public ruleFieldIds$: Observable<any[]>;
constructor(private rulesetService: RulesetService,
private rulefieldService: RuleFieldsService) { }
ngOnInit() {
this.rulefieldService.getList();
this.ruleFieldIds$ = this.rulefieldService.rulefieldIds$;
this.rulefieldService.checkRuleFieldType(15);
console.log(this.ruleFieldIds$ )
}
}
Ive looked into the Observable to try and get a better idea of where the error may be, but I cant make actionable sense of the information :
source: BehaviorSubject
closed: false
hasError: false
isStopped: false
observers: Array(1)
0: FindValueSubscriber
closed: false
destination: Subscriber {closed: false, _parentOrParents: null, _subscriptions: Array(1), syncErrorValue: null, syncErrorThrown: false, …}
index: 2
isStopped: false
predicate: (id) => id === ruleFieldId
source: Observable
source: BehaviorSubject {_isScalar: false, observers: Array(1), closed: false, isStopped: false, hasError: false, …}
_isScalar: false
__proto__: Object
syncErrorThrowable: true
syncErrorThrown: false
syncErrorValue: null
thisArg: undefined
yieldIndex: false
_parentOrParents: Subscriber
closed: false
destination: SafeSubscriber {closed: false, _parentOrParents: null, _subscriptions: null, syncErrorValue: null, syncErrorThrown: false, …}
isStopped: false
syncErrorThrowable: true
syncErrorThrown: false
syncErrorValue: null
_parentOrParents: null
_subscriptions: [FindValueSubscriber]
__proto__: Subscription
_subscriptions: [SubjectSubscription]
__proto__: Subscriber
length: 1
__proto__: Array(0)
thrownError: null
_isScalar: false
_value:
enableDropdownFor: (6) [15, 42, 55, 65, 67, 69]
Thank you for your insight in advance !
Upvotes: 0
Views: 1577
Reputation: 3593
You're understanding on how the "find" operator is a bit off. It is a filter and is meant to filter a stream of data that emits multiple values, but not emit anything till the value matches the predicate and then stop emitting.
https://rxjs-dev.firebaseapp.com/api/operators/find
Your example observable emits a single value, that happens to be an array. What you want to do in your function is use the rxjs map operator, and inside that you will have the array of values, and use the standard array's find (or findIndex), and return a boolean.
I've put together a little stack blitz demoing it, with some comments. https://stackblitz.com/edit/angular-dra5wu?file=src%2Fapp%2Fapp.component.html
here are some of the concepts and operators I was mentioning:
export class ExampleService {
// start with "null" so we can filter that out, this way we know our get list has ran at least once if that's important
private rulefieldIdSub = new BehaviorSubject<any[]>(null);
// we skip the intial "null" value, this makes sure "getList" has populated the rules
rulefieldIds$ = this.rulefieldIdSub.asObservable().pipe(filter(ids => ids !== null));
getList(){
// making a fake list for example purposes, running in a set timeout so it takes a second
setTimeout(() => {
this.rulefieldIdSub.next([1,3,5,7,9])
}, 1000);
}
checkRuleFieldType(ruleFieldId): Observable<boolean> {
return this.rulefieldIds$.pipe(
// we're using the "map" operator to transform the result from the observable into something else
// https://rxjs-dev.firebaseapp.com/api/operators/map
map(ruleFieldIds => {
// map gets the full array of your ruleField ids, now we return if we found that id or not
return ruleFieldIds.findIndex(id => id === ruleFieldId) !== -1;
})
)
}
}
And an updated component that uses it
export class AppComponent {
name = 'Angular';
rulefieldIds$: Observable<any[]>;
constructor(public exampleService: ExampleService) {
this.exampleService.getList();
this.rulefieldIds$ = this.exampleService.rulefieldIds$;
this.exampleService.checkRuleFieldType(15).subscribe(result => {
console.log("Do we have 15?", result)
});
this.exampleService.checkRuleFieldType(5).subscribe(result => {
console.log("Do we have 5?", result)
});
}
}
Upvotes: 1