What is preventing me from assigning to a class variable within a $http success handler?

During the course of my project, I have encountered a perplexing situation that is difficult to comprehend. My intuition tells me that the issue lies in a peculiar nuance of javascript while I am working in TypeScript. Unfortunately, I am unable to prove this theory or find a solution.

Background
In the application I am developing, there is a standard service responsible for interacting with a Web API.

The unusual aspect is that instead of using TypeScript's 'lambda' syntax for a $http.get(...).then(...) success callback, I have opted for class functions. The reason being that the existing code utilizes the error callback and the usage of

$http.get(...).then(success, error)
with lambda syntax can be cumbersome to read.

// Not the most elegant, but works.
this.$http.get(...).then(() => { ... });

// Yikes...
this.$http.get(...)
    .then(() => {
        ...
    }, () => {
        ...
    });

// Much cleaner!
this.$http.get(...)
    .then(onFooSuccess, onError);

Below is the relevant service:

namespace MyApp.Models {
    export class WebApiResult<T> {
        public data: T;
    }
}

namespace MyApp.Services {
    export class SomeService {
        public status: SomeServiceStatus = new SomeServiceStatus();

        public static $inject: string[] = ['$http'];
        public constructor(
            public $http: ng.IHttpService
        ) {
        }

        public doSomething(): ng.IPromise<SomeServiceStatus> {
            this.$http.get('/api/some/SomeAction')
                .then(this.onDoSomethingSuccess, this.describeError);
        }

        // This method poses a problem. There seems to be a type error when executed.
        public onDoSomethingSuccess(result: Models.WebApiResult<SomeServiceStatus>): SomeServiceStatus | ng.IPromise<SomeServiceStatus> {
            if(!result.data.isInSpecialState) {
                return result.data;
            }

            // TypeError!  Unable to assign value to undefined.
            this.status = result.data;

            return result.data;
        }

        public describeError(error: any) {
             alert('oops');
        }
    }

    export class SomeServiceStatus {
        public isInSpecialState: boolean = false;
        public someStatusMessage: string = '';
        public someIdentifier: string = '';
    }

    angular
        .module('app')
        .service('someService', SomeService);
}

Problem
Although the $http get request is successful, the issue arises within the success callback. Each time the line this.status = result.data is executed, an exception occurs due to the inability to assign result.data to the status property of undefined.

My current assumption is that this does not reference the SomeService as intended, but rather something else, possibly even the class method used as the delegate.

Questions
This speculation raises several inquiries.

  1. What does this exactly refer to? Hovering over it in Visual Studio 2015 displays the cryptic information: this: this. Thanks Microsoft.
  2. Is it expected behavior for this to function this way? Is this a TypeScript bug or simply a mistake in the generated JavaScript?
  3. Are there preferred stylistic approaches when handling an AngularJS promise with both success and error callbacks? Perhaps there is a more efficient way to write the code that I am overlooking or lacking knowledge about. Asking questions on SO has often led to valuable insights.

Answer №1

In the realm of JavaScript, there is a unique aspect to consider. If you pass a function instance or use traditional JavaScript syntax like this:

..then(function(data) { ...}, function(error) { ... })

the behavior differs from new lambda expressions as "this" references the scope within doSomething() {} for those handlers.

Fortunately, bind() can preserve your elegant syntax while introducing a bit of redundancy:

public doSomething() {
    this.$http.get('/api/some/SomeAction')
        .then(this.onDoSomethingSuccess.bind(this), this.describeError.bind(this));
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

If someone rapidly clicks on an AngularJS app, the page fails to load

Just a heads up - I am pretty new to Angular. Most of the code is from a tutorial on Lynda that I'm experimenting with. I've noticed an issue when I try to implement a type of "pagination" feature to display various elements from a data.json file ...

What could be causing the button click event to not be triggered in AngularJS?

Can you explain why the click event doesn't work when the loader is displayed on click? I have set up a scenario where data load is delayed and trying to navigate away from this situation (trying to move to the next page but if there is a loader on th ...

Combine array elements in Angular/Javascript based on a certain condition

Is there a way to combine elements from two arrays while avoiding duplicates? array = [ {id: 1, name:'abc'},{id: 1, name:'xyz'},{id: 2, name:'text1'},{id: 2, name:'text2'} ]; The desired output is: result = [{id: ...

Tips for determining if a cookie has been set in a controller during an Angular unit test

A scenario involves a controller that transmits a username and password to a REST service, receiving a token in return: .controller('Login', ['$scope', '$cookieStore', '$http', function($scope, $cookieStore, $http) ...

Guide on utilizing tslint in conjunction with npx

I currently have tslint and typescript set up locally on my project. In order to run tslint against the files, I am using the following command: npx tslint -c tsconfig.json 'src/**/*.ts?(x)' However, when I run this command, it seems to have no ...

What is the proper way to utilize setTimeout in TypeScript?

Let's take a look at an example of how to use setTimeout in Angular and TypeScript: let timer: number = setTimeout(() => { }, 2000); However, upon compilation, you may encounter the following error message: Error TS2322: Type 'Timeout' ...

Unexpected output from nested loop implementation

Having some arrays, I am now trying to iterate through all tab names and exclude the values present in the exclusion list. json1 ={ "sku Brand": "abc", "strngth": "ALL", "area ...

Issue encountered when attempting to access interface field in HTML template of Angular 2

export interface Candidate { name: string; surname: string; gender: boolean; dateOfBirth: Date; placeOfBirth: string; originRegion: string; originDivision: string; originSubDivision: string; employmentSituation: string; typeHandicap: st ...

Operator in RxJS that maps the elements within an array composed of multiple arrays

disclaimer: I have a creative solution and would like to share it here. While exploring the RxJS documentation for map methods that meet this specific requirement - let's call it mapArray - I haven't been able to find one. of([1, 2, 3]).pipe( ...

Is there a more efficient solution for incorporating undefined and null into a type apart from developing a custom generic function?

Often in programming, we encounter methods where one or more parameters can be null or undefined (sometimes both with derived types). This can result in long method signatures like this: doThing( user: User | undefined | null, thing: Thing | undefined ...

What is the best way to locate and access a JSON file that is relative to the module I am currently working

I am in the process of creating a package named PackageA, which includes a function called parseJson. This function is designed to accept a file path pointing to a JSON file that needs to be parsed. Now, in another package - PackageB, I would like to invok ...

experimenting with asynchronous promises in Jasmine to test Angular 2 services

Currently, I'm facing some challenges while testing my Angular 2 service. Even though my tests are passing, I keep encountering this error in the console: context.js:243 Unhandled Promise rejection: 'expect' was used when there was no c ...

Is it possible to implement MV* in Polymer using models and services as polymer elements?

Imagine I want two views (polymer-elements) to share a model, how can this be achieved? In Angular, the model would reside in a singleton service that is injected into the views, allowing both views to access the same data source. I attempted to replicat ...

What is the process in Typescript for importing JSON files and dynamically searching for values based on keys?

When working with typescript 3.0.3, I encountered an issue while importing a json file in the following manner: import postalCodes from '../PostalCodes.json'; The json file has the structure shown below: { "555": { "code": 555, "city": "Sc ...

AngularJS handles API errors by resolving them on a case-by-case basis

My project involves using AngularJS along with UI-Router to create a Single Page App that interacts with a REST API to fetch json data. In my app.config, I have defined the following: .state('project', { url: '/project/:projectId/&apos ...

ReactJS tweet screenshot capture

Currently seeking a solution to capture a tweet screenshot, store it in a PostgreSQL database, and display it on my ReactJS webpage using Typescript. I have been utilizing react-tweet-embed for displaying the tweet, but now I require a method to save the i ...

Can a type alias be created for more than one parameter of a class or function with multiple type parameters?

When using Vue, there are situations where a generic function may require 3, 4, or even 5 type parameters. Is it possible to create a type alias for these parameters in order to avoid typing them out repeatedly? Something like this perhaps: // Example of ...

Refresh tab controllers in Angular JS on every click event

Is there a way to refresh the tab controller every time a tab is clicked? Here's the current code: $scope.tabs = [ { id: 'tab1', title: 'tab1', icon: 'comments', templateUrl: 'tab1/tab1.tpl.html&ap ...

New Announcement: Implementing Flex Images in AngularJS

Issue with Image Resizing Solution I am currently developing a responsive Angular.js website and was looking to use different resolution images based on browser width. After some research, I came across a solution called Picturefill.js, which seemed perfe ...

Initiate the Selenium server on a CentOS machine

After setting up a VM with centOS, I attempted to launch the selenium server by following the steps outlined in this tutorial. However, when trying to start the selenium server using webdriver-manager start, I encountered the following error: execvp(): No ...