Reputation: 1552
I am building an Angular application, and I have two services, call them ServiceA
and ServiceB
. ServiceB
has two methods that ServiceA
uses in the same way, something like this:
@Injectable({
providedIn: 'root'
})
export class ServiceA {
constructor(private _b: ServiceB) {}
doFoo(input: Whatever): void {
this._b.foo(input)
.pipe(...);
.subscribe(...);
}
doBar(input: Whatever): void {
this._b.bar(input)
.pipe(...); // exact same pipe as above
.subscribe(...); // exact same subscribe as above
}
}
Since I have duplicate logic, I tried to do this in ServiceA
:
performFooBarAction(input: Whatever, fooBarFn: (input: Whatever) => Observable<void>): void {
fooBarFn(input).pipe(...).subscribe(...);
}
And call it from both methods like this:
doFoo(input: Whatever): void {
this.performFooBarAction(input. this._b.foo);
}
But, if I do it like that I get an error:
Cannot read properties of undefined
So, I tried to add console.log(this)
to serviceB.foo()
, and – lo and behold – this
is undefined
when I pass the function as an argument. It worked before my little refactor.
Any idea if this is unavoidable, or is it me who's doing it wrong?
Upvotes: 2
Views: 1547
Reputation: 371
bind context when passing function as an argument:
doFoo(input: Whatever): void {
this.performFooBarAction(input. this._b.foo.bind(this._b));
}
Upvotes: 1
Reputation: 1552
I managed to solve this by invoking the call
method and passing it the reference to ServiceB
as context, like so:
performFooBarAction(input: Whatever, fooBarFn: (input: Whatever) => Observable<void>): void {
fooBarFn.call(this._b, input).pipe(...).subscribe(...);
}
But if someone can tell me if there is a better way, I'd be thankful.
[EDIT] - Another solution
Since I discovered I actually won't have the same parameter list for foo()
and bar()
, I came up with another way, which doesn't require context passing, and works in both cases - I simply passed the Observable
s that come from _b.foo()
and _b.bar()
like this:
handleFooBarAction(input$: Observable<void>): void {
input$.pipe(...).subscribe(...);
}
Which I can now call like this:
doFoo(input: Whatever): void {
this.handleFooBar(this._b.foo(input));
}
doBar(): void { // no params
this.handleFooBar(this._b.bar()); // no params
}
Upvotes: 0
Reputation: 8773
You can also try this:
@Injectable({
providedIn: 'root'
})
export class ServiceA {
constructor(private _b: ServiceB) {}
doFoo(input: Whatever): void {
this._b.foo(input)
.pipe(...);
.subscribe(...);
}
doBar(input: Whatever): void {
this._b.bar(input)
.pipe(...); // exact same pipe as above
.subscribe(...); // exact same subscribe as above
}
callMethodByName(input: Whatever, funcName: string) {
this._b[funcName](input).pipe().subscribe();
}
}
A working sample is attachted below:
class Testing {
logName(name) {
console.log(`Name is ${name}`);
}
logAge(age) {
console.log(`Age is ${age}`);
}
print(funcName, data) {
this[funcName](data);
}
}
const tes = new Testing();
tes.print('logName', 'Apoorva Chikara');
tes.print('logAge', 29)
Upvotes: 1