As I continue to enhance a codebase that originally consisted of a mix of different versions of AngularJs and some unstructured code utilizing various versions of a software API, I encounter an interesting quirk. It appears that this API is accessible through $window.external
in the context of AngularJS loaded application pages - quite peculiar.
During my pre-ES6 phase with AngularJs 1.8, I have three services (referred to as someAPIget
, someAPIset
, and someAPIforms
) that interact with the software's API. The structure looks something like this:
// someAPIget.service.js
;(function () {
var APIget = function ($window, helperfunctions) {
function someFunc (param) {
// Performing operations using $window.external.someExternalFunc
return doSomethingWith(param)
}
return {
someFunc: someFunc
}
}
angular.module('someAPIModule').factory('someAPIget', ['$window', 'helperfunctions', someAPIget])
})()
I also had a service and module located one level above these services, with someAPIModule
declared as a dependency. This setup aggregated the functions and passed them through under a unified name, as shown below:
// apiinterface.service.js
;(function () {
var APIget = 'someAPIget'
var APIset = 'someAPIset'
var APIforms = 'someAPIforms'
var APIInterface = function (APIget, APIset, APIforms) {
return {
someFunc: APIget.someFunc,
someSettingFunc: APIset.someSettingFunc,
someFormLoadingFunc: APIforms.someFormLoadingFunc
}
}
angular.module('APIInterface').factory('APIInterface', [APIget, APIset, APIforms, APIInterface])
})()
This architecture allowed me to call these functions across other controllers and services by utilizing APIInterface.someFunc(etc)
. While this approach was effective and modular, it would enable us to seamlessly transition to a new software provider without overhauling everything except for the interface logic.
However, in my pursuit of transitioning to TypeScript and ES6, aiming to leverage import/export functionalities and facilitate command line accessibility, I decided to refactor someAPIget
into a class:
// someAPIget.service.ts
export class someAPIget {
private readonly $window
private readonly helperfunctions
static $inject = ['$window', 'helperfunctions']
constructor ($window, helperfunctions) {
this.$window = $window
this.helperfunctions = helperfunctions
}
someFunc (param) {
// Implementing actions involving this.$window.external.someExternalFunc
return doSomethingWith(param)
}
}
}
angular
.module('someAPImodule')
.service('someAPIget', ['$window', 'helperfunctions', someAPIget])
Initially, it seemed like the transition went smoothly (my tests were passing after cleaning up the TypeScript compilation issues), but upon integration into the live app, I encountered a perplexing issue - this.$window
was not defined. Oddly enough, directly invoking someAPIget.someFunc(param)
bypassed the problem, while using APIInterface.someFunc(param)
caused the issue to resurface. Rewriting thousands of lines to replace references to APIInterface wasn't ideal, and it defeated the purpose of encapsulating the functions within an interface.
I experimented with converting APIInterface into a class, setting getters for each function that returned the imported function, yet $window
remained undefined. Through console.log statements, I confirmed that within someFunc
itself, the property this.$window
was defined, as well as inside the getter in APIInterface. However, when attempting to access it via APIInterface
, it appeared to skip executing the constructor on someAPIget
, even when utilizing $onInit()
for pertinent calls.
I sense that I am overlooking a simple solution here. Is there a correct method to aggregate and rename these functions for widespread program usage? How can I alias them appropriately to ensure post-construction usability?
Edit: I've attempted multiple configurations, such as having someAPIget and APIInterface as both factories and services, and calling APIInterface within the .run()
block of the overarching app.module.ts
file - unfortunately, none yielded success (the latter simply shifting the undefined error location).
Another edit: I also explored using static
in this scenario, which clearly proved erroneous; however, it at least triggered the informative VSCode error highlight stating "Property 'someProp' is used before its initialization.ts(2729)".
How should a property initialized in the constructor be properly utilized? Is there a way to compel AngularJS to execute the constructor before accessing the class members? Any insights or guidance would be greatly appreciated.