A way to avoid explicitly passing the “this” context?












0















While working on a new product I have created a back-end and front-end project. For front-end I am using Angular framework with Typescript. Below is a question due to me being new to the language (a few days old). My question is around callbacks and how to avoid the explicit pass with the "this" context. There are a few resources I have read which I will link.



Below I am implementing a wrapper for the HttpClient. The quick version is flow control with modals that follow a plugin architecture(backed by angular routing) is best complimented with a central delegation using observers and subscribers to broadcast the errors like 401 for a graceful re-entry(in my opinion) - we won't get into that though but was mention as context may help.



Here are the bare bones of my code:
The Wrapper =>



export class WebService {

constructor(private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>) { }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) =>
void, callBackInstance: any): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack.call(callBackInstance, data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
}
);


Now I am able to explicitly manage the "this" context for the callback using the .call() to insert it. I don't mind using this in any of your suggestions - however looking at the signature, you will find that the method requires you to pass in the "this" context you want(callbackInstance). This pushes some responsibility onto the caller of the method that I do not want. To me a class is very much like an array with the "this" as an initial displacement - since I am passing in the method for the callback; is there really no way to inspect that method to derive the appropriate "this"? Something along the lines of:
callbackInstance = callback.getRelativeContext();
callBack.call(callBackInstance, data);
This would eliminate the extra param making the method less error prone for my team to use.



Links to resources are welcome - but please try to narrow it down to the relevant part if possible.



Links:



For updating the "this" context



Parameter callbacks



EDIT:
From accepted answer I derived and placed in test case:



const simpleCallback = (response) => {holder.setValue(response); };
service.post<LoginToken>(Service.LOGIN_URL, '', simpleCallback);









share|improve this question




















  • 1





    Pass a callback that's already correctly bound, e.g. using an arrow function in the caller? Also you could return an observable and let the caller subscribe.

    – jonrsharpe
    Nov 26 '18 at 8:30











  • Would passing an observable back not make the caller responsible for the error delegation as well? Arrow function in callback...hmm I actually did not know I could do that. Let me try

    – Nilo
    Nov 26 '18 at 8:32











  • Not necessarily, you can use catch / pipe with catchError to handle that in the service.

    – jonrsharpe
    Nov 26 '18 at 8:34











  • Hmm I see...and this in turn would remove the need for a callback. Okay tweeking test cases to try it out.

    – Nilo
    Nov 26 '18 at 8:40











  • Thank you for your pointers. However after trying them both out I quite like the arrow method. I made a feeble attempt at it and only got the .bind method to work like this: service.post<LoginToken>(Service.LOGIN_URL, '', holder.setValue.bind(this)); However below hlfrmn has shown me quite a neat way of doing so. It is most clear that these issues stem from not knowing the language. Thanks to you both. Will accept hlfrmn answer as correct:

    – Nilo
    Nov 26 '18 at 9:03
















0















While working on a new product I have created a back-end and front-end project. For front-end I am using Angular framework with Typescript. Below is a question due to me being new to the language (a few days old). My question is around callbacks and how to avoid the explicit pass with the "this" context. There are a few resources I have read which I will link.



Below I am implementing a wrapper for the HttpClient. The quick version is flow control with modals that follow a plugin architecture(backed by angular routing) is best complimented with a central delegation using observers and subscribers to broadcast the errors like 401 for a graceful re-entry(in my opinion) - we won't get into that though but was mention as context may help.



Here are the bare bones of my code:
The Wrapper =>



export class WebService {

constructor(private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>) { }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) =>
void, callBackInstance: any): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack.call(callBackInstance, data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
}
);


Now I am able to explicitly manage the "this" context for the callback using the .call() to insert it. I don't mind using this in any of your suggestions - however looking at the signature, you will find that the method requires you to pass in the "this" context you want(callbackInstance). This pushes some responsibility onto the caller of the method that I do not want. To me a class is very much like an array with the "this" as an initial displacement - since I am passing in the method for the callback; is there really no way to inspect that method to derive the appropriate "this"? Something along the lines of:
callbackInstance = callback.getRelativeContext();
callBack.call(callBackInstance, data);
This would eliminate the extra param making the method less error prone for my team to use.



Links to resources are welcome - but please try to narrow it down to the relevant part if possible.



Links:



For updating the "this" context



Parameter callbacks



EDIT:
From accepted answer I derived and placed in test case:



const simpleCallback = (response) => {holder.setValue(response); };
service.post<LoginToken>(Service.LOGIN_URL, '', simpleCallback);









share|improve this question




















  • 1





    Pass a callback that's already correctly bound, e.g. using an arrow function in the caller? Also you could return an observable and let the caller subscribe.

    – jonrsharpe
    Nov 26 '18 at 8:30











  • Would passing an observable back not make the caller responsible for the error delegation as well? Arrow function in callback...hmm I actually did not know I could do that. Let me try

    – Nilo
    Nov 26 '18 at 8:32











  • Not necessarily, you can use catch / pipe with catchError to handle that in the service.

    – jonrsharpe
    Nov 26 '18 at 8:34











  • Hmm I see...and this in turn would remove the need for a callback. Okay tweeking test cases to try it out.

    – Nilo
    Nov 26 '18 at 8:40











  • Thank you for your pointers. However after trying them both out I quite like the arrow method. I made a feeble attempt at it and only got the .bind method to work like this: service.post<LoginToken>(Service.LOGIN_URL, '', holder.setValue.bind(this)); However below hlfrmn has shown me quite a neat way of doing so. It is most clear that these issues stem from not knowing the language. Thanks to you both. Will accept hlfrmn answer as correct:

    – Nilo
    Nov 26 '18 at 9:03














0












0








0








While working on a new product I have created a back-end and front-end project. For front-end I am using Angular framework with Typescript. Below is a question due to me being new to the language (a few days old). My question is around callbacks and how to avoid the explicit pass with the "this" context. There are a few resources I have read which I will link.



Below I am implementing a wrapper for the HttpClient. The quick version is flow control with modals that follow a plugin architecture(backed by angular routing) is best complimented with a central delegation using observers and subscribers to broadcast the errors like 401 for a graceful re-entry(in my opinion) - we won't get into that though but was mention as context may help.



Here are the bare bones of my code:
The Wrapper =>



export class WebService {

constructor(private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>) { }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) =>
void, callBackInstance: any): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack.call(callBackInstance, data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
}
);


Now I am able to explicitly manage the "this" context for the callback using the .call() to insert it. I don't mind using this in any of your suggestions - however looking at the signature, you will find that the method requires you to pass in the "this" context you want(callbackInstance). This pushes some responsibility onto the caller of the method that I do not want. To me a class is very much like an array with the "this" as an initial displacement - since I am passing in the method for the callback; is there really no way to inspect that method to derive the appropriate "this"? Something along the lines of:
callbackInstance = callback.getRelativeContext();
callBack.call(callBackInstance, data);
This would eliminate the extra param making the method less error prone for my team to use.



Links to resources are welcome - but please try to narrow it down to the relevant part if possible.



Links:



For updating the "this" context



Parameter callbacks



EDIT:
From accepted answer I derived and placed in test case:



const simpleCallback = (response) => {holder.setValue(response); };
service.post<LoginToken>(Service.LOGIN_URL, '', simpleCallback);









share|improve this question
















While working on a new product I have created a back-end and front-end project. For front-end I am using Angular framework with Typescript. Below is a question due to me being new to the language (a few days old). My question is around callbacks and how to avoid the explicit pass with the "this" context. There are a few resources I have read which I will link.



Below I am implementing a wrapper for the HttpClient. The quick version is flow control with modals that follow a plugin architecture(backed by angular routing) is best complimented with a central delegation using observers and subscribers to broadcast the errors like 401 for a graceful re-entry(in my opinion) - we won't get into that though but was mention as context may help.



Here are the bare bones of my code:
The Wrapper =>



export class WebService {

constructor(private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>) { }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) =>
void, callBackInstance: any): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack.call(callBackInstance, data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
}
);


Now I am able to explicitly manage the "this" context for the callback using the .call() to insert it. I don't mind using this in any of your suggestions - however looking at the signature, you will find that the method requires you to pass in the "this" context you want(callbackInstance). This pushes some responsibility onto the caller of the method that I do not want. To me a class is very much like an array with the "this" as an initial displacement - since I am passing in the method for the callback; is there really no way to inspect that method to derive the appropriate "this"? Something along the lines of:
callbackInstance = callback.getRelativeContext();
callBack.call(callBackInstance, data);
This would eliminate the extra param making the method less error prone for my team to use.



Links to resources are welcome - but please try to narrow it down to the relevant part if possible.



Links:



For updating the "this" context



Parameter callbacks



EDIT:
From accepted answer I derived and placed in test case:



const simpleCallback = (response) => {holder.setValue(response); };
service.post<LoginToken>(Service.LOGIN_URL, '', simpleCallback);






angular typescript






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 26 '18 at 9:04







Nilo

















asked Nov 26 '18 at 8:21









NiloNilo

134




134








  • 1





    Pass a callback that's already correctly bound, e.g. using an arrow function in the caller? Also you could return an observable and let the caller subscribe.

    – jonrsharpe
    Nov 26 '18 at 8:30











  • Would passing an observable back not make the caller responsible for the error delegation as well? Arrow function in callback...hmm I actually did not know I could do that. Let me try

    – Nilo
    Nov 26 '18 at 8:32











  • Not necessarily, you can use catch / pipe with catchError to handle that in the service.

    – jonrsharpe
    Nov 26 '18 at 8:34











  • Hmm I see...and this in turn would remove the need for a callback. Okay tweeking test cases to try it out.

    – Nilo
    Nov 26 '18 at 8:40











  • Thank you for your pointers. However after trying them both out I quite like the arrow method. I made a feeble attempt at it and only got the .bind method to work like this: service.post<LoginToken>(Service.LOGIN_URL, '', holder.setValue.bind(this)); However below hlfrmn has shown me quite a neat way of doing so. It is most clear that these issues stem from not knowing the language. Thanks to you both. Will accept hlfrmn answer as correct:

    – Nilo
    Nov 26 '18 at 9:03














  • 1





    Pass a callback that's already correctly bound, e.g. using an arrow function in the caller? Also you could return an observable and let the caller subscribe.

    – jonrsharpe
    Nov 26 '18 at 8:30











  • Would passing an observable back not make the caller responsible for the error delegation as well? Arrow function in callback...hmm I actually did not know I could do that. Let me try

    – Nilo
    Nov 26 '18 at 8:32











  • Not necessarily, you can use catch / pipe with catchError to handle that in the service.

    – jonrsharpe
    Nov 26 '18 at 8:34











  • Hmm I see...and this in turn would remove the need for a callback. Okay tweeking test cases to try it out.

    – Nilo
    Nov 26 '18 at 8:40











  • Thank you for your pointers. However after trying them both out I quite like the arrow method. I made a feeble attempt at it and only got the .bind method to work like this: service.post<LoginToken>(Service.LOGIN_URL, '', holder.setValue.bind(this)); However below hlfrmn has shown me quite a neat way of doing so. It is most clear that these issues stem from not knowing the language. Thanks to you both. Will accept hlfrmn answer as correct:

    – Nilo
    Nov 26 '18 at 9:03








1




1





Pass a callback that's already correctly bound, e.g. using an arrow function in the caller? Also you could return an observable and let the caller subscribe.

– jonrsharpe
Nov 26 '18 at 8:30





Pass a callback that's already correctly bound, e.g. using an arrow function in the caller? Also you could return an observable and let the caller subscribe.

– jonrsharpe
Nov 26 '18 at 8:30













Would passing an observable back not make the caller responsible for the error delegation as well? Arrow function in callback...hmm I actually did not know I could do that. Let me try

– Nilo
Nov 26 '18 at 8:32





Would passing an observable back not make the caller responsible for the error delegation as well? Arrow function in callback...hmm I actually did not know I could do that. Let me try

– Nilo
Nov 26 '18 at 8:32













Not necessarily, you can use catch / pipe with catchError to handle that in the service.

– jonrsharpe
Nov 26 '18 at 8:34





Not necessarily, you can use catch / pipe with catchError to handle that in the service.

– jonrsharpe
Nov 26 '18 at 8:34













Hmm I see...and this in turn would remove the need for a callback. Okay tweeking test cases to try it out.

– Nilo
Nov 26 '18 at 8:40





Hmm I see...and this in turn would remove the need for a callback. Okay tweeking test cases to try it out.

– Nilo
Nov 26 '18 at 8:40













Thank you for your pointers. However after trying them both out I quite like the arrow method. I made a feeble attempt at it and only got the .bind method to work like this: service.post<LoginToken>(Service.LOGIN_URL, '', holder.setValue.bind(this)); However below hlfrmn has shown me quite a neat way of doing so. It is most clear that these issues stem from not knowing the language. Thanks to you both. Will accept hlfrmn answer as correct:

– Nilo
Nov 26 '18 at 9:03





Thank you for your pointers. However after trying them both out I quite like the arrow method. I made a feeble attempt at it and only got the .bind method to work like this: service.post<LoginToken>(Service.LOGIN_URL, '', holder.setValue.bind(this)); However below hlfrmn has shown me quite a neat way of doing so. It is most clear that these issues stem from not knowing the language. Thanks to you both. Will accept hlfrmn answer as correct:

– Nilo
Nov 26 '18 at 9:03












1 Answer
1






active

oldest

votes


















0














If you need to pass the context to the callback, then the callback itself will rely on that context:



function explicitContext(callback, context) {
const arg = 1;
callback.call(context, arg);
}

function implicitContext(callback) {
const arg = 1;
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
callback.call(someCleverContext, arg);
}


Consider the usage, if we need to actually access the context in the callback:



function explicitUsage() {
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
const callback = function(arg) {this.importantFunc(arg);}
explicitContext(callback, someCleverContext);
}

function implicitUsage() {
const callback = function(arg) {this.importantFunc(arg);}
implicitContext(callback);
}


In both cases we are actually leaking the details about the context and forcing some responsibility on the consumer! Now, there isn't a magic way to go around it if we really need to pass the context. The good news is, we probably don't need to pass the context in the first place.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack(data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
},
);
}
}


This way we can let the client code only care about the responseData, and if they need some clever context, they are free to bind it themselves:



function usage() {
let webService: WebService;
const simpleCallback = (response) => {console.log(response);} // can inline too
webService.post('/api', {data: 1}, simpleCallback);

const cleverContextCallback = function(response) {this.cleverLog(response)};
const cleverContext = {cleverLog: (data) => console.log(data)};
const boundCallback = cleverContextCallback.bind(cleverContext);
webService.post('/api', {data: 1}, boundCallback );
}


Having said all that, I would definitely recommend just returning the observable from your services.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): Observable<T> {

const observable = this.httpClient.post<T>(url, dataToPost);
// Note that httpClient.post automatically completes.
// If we were using some other library and we would want to close the observable ourselves,
// you could close the observable yourself here after one result:

if ('we need to close observable ourselves after a result') {
return observable.pipe(take(1));
}

if ('we need to handle errors') {
return observable.pipe(
catchError(error => {
this.exceptionService.notify(error);
if ('We can fallback') {
return of('Fallback Value');
} else {
throw new Error('OOPS');
}
}),
);
}

return observable;
}
}


Dealing with errors, closing and other chores inside the service would let the consumer of the service focus on the data from the response.






share|improve this answer


























  • Thank you for this. This is what i was looking for. You have my gratitude!

    – Nilo
    Nov 26 '18 at 9:04











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53477080%2fa-way-to-avoid-explicitly-passing-the-this-context%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









0














If you need to pass the context to the callback, then the callback itself will rely on that context:



function explicitContext(callback, context) {
const arg = 1;
callback.call(context, arg);
}

function implicitContext(callback) {
const arg = 1;
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
callback.call(someCleverContext, arg);
}


Consider the usage, if we need to actually access the context in the callback:



function explicitUsage() {
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
const callback = function(arg) {this.importantFunc(arg);}
explicitContext(callback, someCleverContext);
}

function implicitUsage() {
const callback = function(arg) {this.importantFunc(arg);}
implicitContext(callback);
}


In both cases we are actually leaking the details about the context and forcing some responsibility on the consumer! Now, there isn't a magic way to go around it if we really need to pass the context. The good news is, we probably don't need to pass the context in the first place.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack(data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
},
);
}
}


This way we can let the client code only care about the responseData, and if they need some clever context, they are free to bind it themselves:



function usage() {
let webService: WebService;
const simpleCallback = (response) => {console.log(response);} // can inline too
webService.post('/api', {data: 1}, simpleCallback);

const cleverContextCallback = function(response) {this.cleverLog(response)};
const cleverContext = {cleverLog: (data) => console.log(data)};
const boundCallback = cleverContextCallback.bind(cleverContext);
webService.post('/api', {data: 1}, boundCallback );
}


Having said all that, I would definitely recommend just returning the observable from your services.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): Observable<T> {

const observable = this.httpClient.post<T>(url, dataToPost);
// Note that httpClient.post automatically completes.
// If we were using some other library and we would want to close the observable ourselves,
// you could close the observable yourself here after one result:

if ('we need to close observable ourselves after a result') {
return observable.pipe(take(1));
}

if ('we need to handle errors') {
return observable.pipe(
catchError(error => {
this.exceptionService.notify(error);
if ('We can fallback') {
return of('Fallback Value');
} else {
throw new Error('OOPS');
}
}),
);
}

return observable;
}
}


Dealing with errors, closing and other chores inside the service would let the consumer of the service focus on the data from the response.






share|improve this answer


























  • Thank you for this. This is what i was looking for. You have my gratitude!

    – Nilo
    Nov 26 '18 at 9:04
















0














If you need to pass the context to the callback, then the callback itself will rely on that context:



function explicitContext(callback, context) {
const arg = 1;
callback.call(context, arg);
}

function implicitContext(callback) {
const arg = 1;
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
callback.call(someCleverContext, arg);
}


Consider the usage, if we need to actually access the context in the callback:



function explicitUsage() {
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
const callback = function(arg) {this.importantFunc(arg);}
explicitContext(callback, someCleverContext);
}

function implicitUsage() {
const callback = function(arg) {this.importantFunc(arg);}
implicitContext(callback);
}


In both cases we are actually leaking the details about the context and forcing some responsibility on the consumer! Now, there isn't a magic way to go around it if we really need to pass the context. The good news is, we probably don't need to pass the context in the first place.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack(data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
},
);
}
}


This way we can let the client code only care about the responseData, and if they need some clever context, they are free to bind it themselves:



function usage() {
let webService: WebService;
const simpleCallback = (response) => {console.log(response);} // can inline too
webService.post('/api', {data: 1}, simpleCallback);

const cleverContextCallback = function(response) {this.cleverLog(response)};
const cleverContext = {cleverLog: (data) => console.log(data)};
const boundCallback = cleverContextCallback.bind(cleverContext);
webService.post('/api', {data: 1}, boundCallback );
}


Having said all that, I would definitely recommend just returning the observable from your services.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): Observable<T> {

const observable = this.httpClient.post<T>(url, dataToPost);
// Note that httpClient.post automatically completes.
// If we were using some other library and we would want to close the observable ourselves,
// you could close the observable yourself here after one result:

if ('we need to close observable ourselves after a result') {
return observable.pipe(take(1));
}

if ('we need to handle errors') {
return observable.pipe(
catchError(error => {
this.exceptionService.notify(error);
if ('We can fallback') {
return of('Fallback Value');
} else {
throw new Error('OOPS');
}
}),
);
}

return observable;
}
}


Dealing with errors, closing and other chores inside the service would let the consumer of the service focus on the data from the response.






share|improve this answer


























  • Thank you for this. This is what i was looking for. You have my gratitude!

    – Nilo
    Nov 26 '18 at 9:04














0












0








0







If you need to pass the context to the callback, then the callback itself will rely on that context:



function explicitContext(callback, context) {
const arg = 1;
callback.call(context, arg);
}

function implicitContext(callback) {
const arg = 1;
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
callback.call(someCleverContext, arg);
}


Consider the usage, if we need to actually access the context in the callback:



function explicitUsage() {
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
const callback = function(arg) {this.importantFunc(arg);}
explicitContext(callback, someCleverContext);
}

function implicitUsage() {
const callback = function(arg) {this.importantFunc(arg);}
implicitContext(callback);
}


In both cases we are actually leaking the details about the context and forcing some responsibility on the consumer! Now, there isn't a magic way to go around it if we really need to pass the context. The good news is, we probably don't need to pass the context in the first place.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack(data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
},
);
}
}


This way we can let the client code only care about the responseData, and if they need some clever context, they are free to bind it themselves:



function usage() {
let webService: WebService;
const simpleCallback = (response) => {console.log(response);} // can inline too
webService.post('/api', {data: 1}, simpleCallback);

const cleverContextCallback = function(response) {this.cleverLog(response)};
const cleverContext = {cleverLog: (data) => console.log(data)};
const boundCallback = cleverContextCallback.bind(cleverContext);
webService.post('/api', {data: 1}, boundCallback );
}


Having said all that, I would definitely recommend just returning the observable from your services.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): Observable<T> {

const observable = this.httpClient.post<T>(url, dataToPost);
// Note that httpClient.post automatically completes.
// If we were using some other library and we would want to close the observable ourselves,
// you could close the observable yourself here after one result:

if ('we need to close observable ourselves after a result') {
return observable.pipe(take(1));
}

if ('we need to handle errors') {
return observable.pipe(
catchError(error => {
this.exceptionService.notify(error);
if ('We can fallback') {
return of('Fallback Value');
} else {
throw new Error('OOPS');
}
}),
);
}

return observable;
}
}


Dealing with errors, closing and other chores inside the service would let the consumer of the service focus on the data from the response.






share|improve this answer















If you need to pass the context to the callback, then the callback itself will rely on that context:



function explicitContext(callback, context) {
const arg = 1;
callback.call(context, arg);
}

function implicitContext(callback) {
const arg = 1;
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
callback.call(someCleverContext, arg);
}


Consider the usage, if we need to actually access the context in the callback:



function explicitUsage() {
const someCleverContext = {importantVal: 42, importantFunc: () => {}};
const callback = function(arg) {this.importantFunc(arg);}
explicitContext(callback, someCleverContext);
}

function implicitUsage() {
const callback = function(arg) {this.importantFunc(arg);}
implicitContext(callback);
}


In both cases we are actually leaking the details about the context and forcing some responsibility on the consumer! Now, there isn't a magic way to go around it if we really need to pass the context. The good news is, we probably don't need to pass the context in the first place.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): void {

this.httpClient.post<T>(url, dataToPost).subscribe(
(data: T) => {
callBack(data);
},

(error: HttpErrorResponse) => {
this.exceptionService.notify(error);
},
);
}
}


This way we can let the client code only care about the responseData, and if they need some clever context, they are free to bind it themselves:



function usage() {
let webService: WebService;
const simpleCallback = (response) => {console.log(response);} // can inline too
webService.post('/api', {data: 1}, simpleCallback);

const cleverContextCallback = function(response) {this.cleverLog(response)};
const cleverContext = {cleverLog: (data) => console.log(data)};
const boundCallback = cleverContextCallback.bind(cleverContext);
webService.post('/api', {data: 1}, boundCallback );
}


Having said all that, I would definitely recommend just returning the observable from your services.



export class WebService {

constructor(
private httpClient: HttpClient,
private exceptionService: ExceptionService<Exception>)
{ }

public post<T>(url: string, dataToPost: any, callBack: (responseData: T) => void): Observable<T> {

const observable = this.httpClient.post<T>(url, dataToPost);
// Note that httpClient.post automatically completes.
// If we were using some other library and we would want to close the observable ourselves,
// you could close the observable yourself here after one result:

if ('we need to close observable ourselves after a result') {
return observable.pipe(take(1));
}

if ('we need to handle errors') {
return observable.pipe(
catchError(error => {
this.exceptionService.notify(error);
if ('We can fallback') {
return of('Fallback Value');
} else {
throw new Error('OOPS');
}
}),
);
}

return observable;
}
}


Dealing with errors, closing and other chores inside the service would let the consumer of the service focus on the data from the response.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 26 '18 at 9:02

























answered Nov 26 '18 at 8:50









hlfrmnhlfrmn

1,0321221




1,0321221













  • Thank you for this. This is what i was looking for. You have my gratitude!

    – Nilo
    Nov 26 '18 at 9:04



















  • Thank you for this. This is what i was looking for. You have my gratitude!

    – Nilo
    Nov 26 '18 at 9:04

















Thank you for this. This is what i was looking for. You have my gratitude!

– Nilo
Nov 26 '18 at 9:04





Thank you for this. This is what i was looking for. You have my gratitude!

– Nilo
Nov 26 '18 at 9:04




















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53477080%2fa-way-to-avoid-explicitly-passing-the-this-context%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

Calculate evaluation metrics using cross_val_predict sklearn

Insert data from modal to MySQL (multiple modal on website)