Im trying to add a helper method to my service that will call another method in the same service which returns an Observable. The helper method needs to return a Boolean. The problem is the logic required to determine whether the return value is true or false is done inside the subscribe block which is obviously async. So even through the value is true I still always get a false returned. Maybe trying to mix synchronous and asynchronous programming like this is not a good approach.
In my component I am trying to make the following call to the service:
this.isAdmin = this.teamsService.isAdmin(this.teamId, this.user['userId'])
Service:
getAssociations(id, postfix) {
return this.httpService.get(this.base, id, postfix)
.map((response) => {
return this.responseHandler.handleData(response);
})
.catch((error) => {
return this.responseHandler.handleError(error);
}
);
}
isAdmin(teamId, userId) {
let isAdmin = false;
this.getAssociations(teamId, '/teamMembers')
.subscribe(
_data => {
if (_data['teamMembers']) {
for (let i = 0; i < _data['teamMembers'].length; i++) {
if (_data['teamMembers'][i]['id'] === userId) {
isAdmin = true;
break;
}
}
}
}
);
return isAdmin;
}
So the isAdmin method always return false for obvious reasons. I initially was dealing with this logic inside the component by doing the following:
private checkIsAdmin() {
this.teamsService.getAssociations(this.teamId, '/teamMembers')
.subscribe(
_data => {
if (_data['teamMembers'] && _data['teamMembers'].length) {
_data['teamMembers'].map(member => {
if (member['id'] === this.user['userId']) {
this.isAdmin = true;
}
});
}
this.spinner.hide();
this.loading = false;
},
_error => {
this.spinner.hide();
}
);
}
This worked fine until I had to repeat this same logic on other components. So I am trying to come up with a way to abstract that logic into the service itself returning a boolean since that is really all I need from the function.
I have tried a few ideas but none of them worked. Any help would be great. Thanks.
You can also move all control logic up into the Rx stream like so:
isAdmin(teamId, userId) {
return this.getAssociations(teamId, '/teamMembers')
.mergeMap(data => data['teamMembers'] != null ? Rx.Observable.empty() : Rx.Observable.from(data['teamMembers']))
.filter(user => user['id'] === userId)
.mapTo(true) // found admin user
.take(1)
.concat(Rx.Observable.of(false));//default state; no admin user
}
Depending on the usage of getAssociations()
you can also change its return type signature from Observable<ResponseTeamWrapperSomething>
towards Observable<TeamMember>
so you can lose the mergeMap operator in this function.
By moving this logic and replacing your custom control flow with platform supplied function you reduce the cognitive overload of what your code is doing. This also reduces the testing burden on you because you do not have to test framework supplied logic.