Dependency injection in custom filters - javascript

I'm new to angular and trying create a custom filter which requires a service. I followed the answer here https://stackoverflow.com/a/43506252/15817005. It solved my issue partially.
Registering filter
angular.module('filters',[]).filter('dataFormat',['studentService', dataFormatFilter])
My Filter factory and filter function.
export function dataFormatFilter(studentService){
console.log(studentService); // Having access here
return dataFormatFunction;
}
function dataFormatFunction(name){
// All the formatting logic
//Need properties from studentService in this function.
}
I am able to access the properties from studentService in dataFormatFilter(factory function). Is there a way to get it in dataFormatFunction.
All the solutions i have seen use dataFormatFunction inside of factory itself dataFormatFilter. But i cannot follows this way.
Thanks!

What it looks like you need to to is return an arrow function that will keep the closure of your filter function so you can use the dependencies without having to pass them around.
I wrote this plunkr to demonstrate.
The critical part being:
const dataFormatFilter = function (studentService) {
return (name) => {
return myReusableFunction(studentService, name);
}
}
app.filter('dataFormat',['studentService', dataFormatFilter]);
Note that the returned value for the function is an arrow function. This isn't the only way to achieve what you are attempting, but should be the simplest.

Related

AngularJS 1.8 and ES6 modules: how to make an service or factory that "passes through" a class based API interface?

I am gradually improving a codebase that originally had some AngularJs in various versions and some code that was not in a framework at all using various versions of a software API. (For some reason this API is available - to pages loaded through the application - on AngularJS's $window.external...go figure.)
In my pre-ES6, AngularJs 1.8 phase, I have three services that interact with the software's API (call them someAPIget, someAPIset, and someAPIforms). Something like this:
// someAPIget.service.js
;(function () {
var APIget = function ($window, helperfunctions) {
function someFunc (param) {
// Do something with $window.external.someExternalFunc
return doSomethingWith(param)
}
return {
someFunc: someFunc
}
}
angular.module('someAPIModule').factory('someAPIget', ['$window', 'helperfunctions', someAPIget])
})()
I then had a service and module a level up from this, with someAPIModule as a dependency, that aggregated these functions and passed them through under one name, like this:
// apiinterface.service.js
;(function () {
// Change these lines to switch which API service all functions will use.
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])
})()
I would then call these functions in various other controllers and services by using APIInterface.someFunc(etc). It worked fine, and if we switch to a different software provider, we can use our same pages without rewriting everything, just the interface logic.
However, I'm trying to upgrade to Typescript and ES6 so I can use import and export and build some logic accessible via command line, plus prepare for upgrading to Angular 11 or whatever the latest version is when I'm ready to do it. So I rebuilt someAPIget to 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) {
// Do something with this.$window.external.someExternalFunc
return doSomethingWith(param)
}
}
}
angular
.module('someAPImodule')
.service('someAPIget', ['$window', 'helperfunctions', someAPIget])
Initially it seemed like it worked (my tests still pass, or at least after a bit of cleanup in the Typescript compilation department they do), but then when I load it into the live app... this.$window is not defined. If, however, I use a direct dependency and call someAPIget.someFunc(param) instead of through APIInterface.someFunc(param) it works fine (but I really don't want to rewrite thousands of lines of code using APIInterface for the calls, plus it will moot the whole point of wrapping it in an interface to begin with). I've tried making APIInterface into a class and assigning getters for every function that return the imported function, but $window still isn't defined. Using console.log statements I can see that this.$window is defined inside someFunc itself, and it's defined inside the getter in APIInterface, but from what I can tell when I try to call it using APIInterface it's calling it without first running the constructor on someAPIget, even if I make sure to use $onInit() for the relevant calls.
I feel like I am missing something simple here. Is there some way to properly aggregate and rename these functions to use throughout my program? How do alias them correctly to a post-constructed version?
Edit to add: I have tried with someAPIget as both a factory and a service, and APIInterface as both a factory and a service, and by calling APIInterface in the .run() of the overall app.module.ts file, none of which works. (The last one just changes the location of the undefined error.)
Edit again: I have also tried using static for such a case, which is somewhat obviously wrong, but then at least I get the helpful error highlight in VSCode of Property 'someProp' is used before its initialization.ts(2729).
How exactly are you supposed to use a property that is assigned in the constructor? How can I force AngularJS to execute the constructor before attempting to access the class's members?
I am not at all convinced that I found an optimal or "correct" solution, but I did find one that works, which I'll share here in case it helps anyone else.
I ended up calling each imported function in a class method of the same name on the APIInterface class, something like this:
// apiinterface.service.ts
// Change these lines to switch which API service all functions will use.
const APIget = 'someAPIget'
const APIset = 'someAPIset'
const APIforms = 'someAPIforms'
export class APIInterface {
private readonly APIget
private readonly APIset
private readonly APIforms
constructor (APIget, APIset, APIforms) {
this.APIget = APIget
this.APIset = APIset
this.APIforms = APIforms
}
someFunc(param: string): string {
return this.APIget.someFunc(param)
}
someSettingFunc(param: string): string {
return this.APIset.someSettingFunc(param)
}
someFormLoadingFunc(param: string): string {
return this.APIforms.someFormLoadingFunc(param)
}
}
angular
.module('APIInterface')
.factory('APIInterface', [APIget, APIset, APIforms, APIInterface])
It feels hacky to me, but it does work.
Later Update:
I am now using Angular12, not AngularJS, so some details may be a bit different. Lately I have been looking at using the public-api.ts file that Angular12 generates to accomplish the same thing (ie, export { someAPIget as APIget } from './filename' but have not yet experimented with this, since it would still require either consolidating my functions somehow or rewriting the code that consumes them to use one of three possible solutions. It would be nice not to have to duplicate function signatures and doc strings however. It's still a question I'm trying to answer more effectively, I will update again if I find something that really works.

Should you use this. in reference to a property in factory functions?

everyone, i am trying to wrap my head around factory functions. I want to know what is the proper way to make a factory function that takes in a parameter.
Should the methods we give the objects that it creates refer to its properties by using this. in front on them?
For example:
//using this.
function createPerson(name) {
return {
name,
talk() {
console.log(this.name);
}
}
}
or this way:
//not using this.
function createPerson(name) {
return {
name,
talk() {
console.log(name);
}
}
}
I have tried both and they both seem to perform the same way, which I assume I am probably wrong about. Meaning if i run the following:
const marc = createPerson('marc');
const joe = createPerson('joe');
marc.talk();
joe.talk();
in both cases I get the same output, so is it necessary to use this. in the factory function? What is the common practice? thank you for helping me
Your use case is working with this only because the returned object has that name property.
The context of this is the returned object and not the createPerson function object
If you were to have a variable outside the object and try to access it with this it won't work.
The context of this can be compicated and I think that you can easily get confused knowing what this is in your use case
//not using this.
function createPerson(name) {
// without using new createPerson() "this" is not the function object
const upper = this.upper = name.toUpperCase();
return {
name,
talk() {
console.log('this.upper', this.upper)
console.log('upper', upper);
}
}
}
const foo= createPerson('foo')
foo.talk()

Can I control how the class is being created in ES6?

In Python3 I can use magic function __new__, which executes before class initialization. This helps me control whether new instance will be created or we will use some instance from cache.
Just a little simplified example:
class Something:
def __new__(..., someArgument):
# was a class with someArgument initialized somewhere before?
# is yes, then:
return CACHE[someArgument]
# if no, then:
CACHE[someArgument] = Something(someArgument)
return CACHE[someArgument]
So, can I the same in ES6? Or how can I control class initializing in other way?
This question is not a duplicate of this one, because I'm asking whether I can find some functionality in JS, while the topic above contains a duscussion about this functionality.
As Justinas commented, you can look up about Javascript Factory.
A Javascript Factory define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Some places you can look it up:
Factory Method Design Pattern
Factory Functions with ES6
Calling Javascript Factory Method
I Hope it helped!
You can use factory function:
class Test {
}
function getInstance() {
if (!Test._instance) {
Test._instance = new Test();
}
return Test._instance;
}
No, when a (non-derived) constructor is invoked with new (or super()) then an object is already instantiated before any custom code runs1. However, JS lets you overwrite the result value of the expression by returning an object from the constructor. You can write
const CACHE = new Map();
class Something {
constructor(someArgument) {
if (CACHE.has(someArgument)) return CACHE.get(someArgument);
// ^^^^^^
CACHE.set(someArgument, this);
// initialise `this`
}
}
That said, a factory function (or even static method) - as suggested by the other answers - is usually a more sensible solution. You'd put the cache handling code only in the constructor if you absolutely needed to enforce this.
1: it could be intercepted by the construct trap of a proxy, but you normally would not use that.

Angular 2 Convert Function Name to Function Reference

Am moving some javascript logic into an Angular 2 app. Part of it involves a homegrown library of "formatter" functions [they do more than just formatting, so pipes are not an option] Currently these are passed by name (string) and the receiving function uses window[fName] to convert them to a function reference.
So there's a formatter function:
var tickPositionerYMD = function() { ticks=[]; ... complex logic ... return ticks; }
And a build function:
build(x, y, formatterName) {
...
formatter = window[formatterName];
...
}
And the build is called with a particular formatter:
build(xData, yData, 'tickPositionerYMD');
In javascript, window[] is used to create a function reference.
Question is ... what's the best way to do this in an Angular 2 component? Have seen one approach where a service is created with a reference to window ... this is passed in through DI.
A second approach is to create a factory function which given a name, returns a function.
Given those choices, am inclined to go with the factory function. Being new to Angular, was wondering if I might be missing a better approach.
You should use Angular services.
Each service is a singleton, so when you inject it to your components all all refferences will be pinting to the same instance.
So when you define formatters int your service, you should expose either method getFormatter(formatterName) or Map of Formatters and use it like this:
// method:
let formatter = formatterService.getFormatter('myForm');
// Map:
let anotherFormatter = formatterService.formatters['myForm']
Just don't forget that you inject services not instantiate.

Named Object Property Functions

render: function render(context, partials) {
return this.r(context, partials);
},
Given this code from Twitter's new hogan.js library to demonstrate the issue; what is the goal of naming the function twice?
If it wanted to, the function render would be able to call itself via render(), however, render() is not accessible anywhere else.
Additionally, in a stack trace, you'd see render as the function name, rather than anonymous function.
It's needed for a recursive call.
The first occurance of render is the name of the field the function is stored in, so that you can call the function via
object.render(context, partials);
The second render names the function itself. Debugging tools then display render instead of only function.
A second, possible reason is that the function could call itself recursively.
var render = function render(n) {
console.log("render");
if (n < 1)
render(n + 1);
};
render(0);
Edit: Sorry, I've written something really wrong in the first revision.

Categories