Angular 2 Convert Function Name to Function Reference - javascript

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.

Related

Dependency injection in custom filters

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.

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.

What is a factory in Angular

I was reading an article of Max NgWizard K, about how Angular updates the DOM. I came across the following:
For each component that is used in the application Angular compiler generates a factory. When Angular creates a component from a factory Angular uses this factory to instantiate View Definition which in turn is used to create component View. Under the hood Angular represents an application as a tree of views.
In another article from Max NgWizard K I found the definition of a factory:
Factories describe the structure of a component view and are used when instantiating the component.
I'm not really sure what is meant with this.
Questions:
What exactly are factories in Angular(2+)?
Are there scenarios that a developer benefits form knowing how they work?
What exactly are factories in Angular(2+)?
Factory is one of the design patterns mentioned by Gang of Four (Basically they wrote a book on the design patterns they discovered).
Design Patterns help programmers solve common development tasks in a specific way.
And in this case, the Factory pattern helps in instantiation and creation of Objects.
It is also known as the Virtual Constructor.
Think of it, like this:
Say you are making a 2D shooter game, and you have to shoot bullets out of barrels.
Instead of instantiating bullets like new Bullet(), every time trigger is pulled, you can use a factory to create bullets, i.e. WeaponsFactory.createInstance(BulletTypes.AK47_BULLET).
It becomes highly scalable, since all you have to do is change the enum and the factory will make it for you.
You won't have to manually instantiate it.
That is what angular does, it automatically creates factory of all the components. Which makes its job easier.
Are there scenarios that a developer benefits form knowing how they work?
You don't have to know the inner workings of a Factory to use Angular, but it's useful for creating components dynamically!
e.g. A lot of *ngIf, or *ngSwitchCase can be replaced by a simple dynamic generation of components
Components can be created dynamically like this:
createComponent(type) {
this.container.clear();
const factory: ComponentFactory = this.resolver.resolveComponentFactory(AlertComponent);
this.componentRef: ComponentRef = this.container.createComponent(factory);
}
Reference for understanding the above code: Dynamically Creating Components
'A factory' in this case is an instance of ComponentFactory, a class that has create method that implements Factory method pattern.
When componentFactory.create is called (either directly or via ComponentFactoryResolver - which is essential for dynamic components, as linked article explains), new component instance is created.
In general factory is a creational design pattern. It is an object for creating other objects – formally a factory is a function or method that returns objects of a varying prototype or class from some method call.
From the Angular docs
#Component({
selector: 'app-typical',
template: '<div>A typical component for {{data.name}}</div>'
)}
export class TypicalComponent {
#Input() data: TypicalData;
constructor(private someService: SomeService) { ... }
}
The Angular compiler extracts the metadata once and generates a
factory for TypicalComponent. When it needs to create a
TypicalComponent instance, Angular calls the factory, which produces a
new visual element, bound to a new instance of the component class
with its injected dependency.
This is something which happens behind the scenes. But you create dynamic components using ComponentFactoryResolver as well (Dynamic component loader)
//Only dynamic component creation logic is shown below
loadComponent() {
this.currentAdIndex = (this.currentAdIndex + 1) % this.ads.length;
const adItem = this.ads[this.currentAdIndex];
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);
const viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear();
const componentRef = viewContainerRef.createComponent<AdComponent>(componentFactory);
componentRef.instance.data = adItem.data;
}
Also read this article about how the component factories work in Ivy.

Parse and share obj resources in module

I wanted to know if its good practice to use it like following since I used a global field cacheObj
I need to parse the data and share it between other modules,any module can take any property but only the first module which called to this parser is responsible to provide the data to parse(I need to do this parse just once and share properties in different modules)
This code is from other SO post and I want to use it
var Parser = require('myParser'),
_ = require('lodash');
var cacheObj; // <-- singleton, will hold value and will not be reinitialized on myParser function call
function myParser(data) {
if (!(this instanceof myParser)) return new myParser(data);
if (!_.isEmpty(cacheObj)) {
this.parsedData = cacheObj;
} else {
this.parsedData = Parser.parse(data);
cacheObj = this.parsedData;
}
}
myParser.prototype = {
//remove `this.cacheObj`
getPropOne: function () {
return this.parsedData.propOne;
},
getPropTwo: function () {
return this.parsedData.propTwo;
}
};
module.exports = myParser;
It kindda looks like the Context Object pattern, which is used for maintaining state and for sharing information. Some consider it a bad practice and prefer Singleton when it comes to share the object between layers, but if suites your case (in the same module) - my advice is to use it.
UPDATE
The main reason why you shouldn't use ContextObject through your layes is because it binds all sub-systems together( one object is referencing everything else). While Singleton is not just for creating objects, it is also services as access point that can be loaded by the corresponding sub-system. Having a Singleton represent every service access point allows for seamless vertical integration of cooperating components/modules. Simple code example:
Singleton:
// returns the "global" time
var time = Clock.getInstance().getTime();
Context object:
// allows different timezones to coexist within one application
var time = context.getTimezoneOffset().getTime();

Dependency Injection with RequireJS

How much can I stretch RequireJS to provide dependency injection for my app? As an example, let's say I have a model that I want to be a singleton. Not a singleton in a self-enforcing getInstance()-type singleton, but a context-enforced singleton (one instance per "context"). I'd like to do something like...
require(['mymodel'], function(mymodel) {
...
}
And have mymodel be an instance of the MyModel class. If I were to do this in multiple modules, I would want mymodel to be the same, shared instance.
I have successfully made this work by making the mymodel module like this:
define(function() {
var MyModel = function() {
this.value = 10;
}
return new MyModel();
});
Is this type of usage expected and common or am I abusing RequireJS? Is there a more appropriate way I can perform dependency injection with RequireJS? Thanks for your help. Still trying to grasp this.
This is not actually dependency injection, but instead service location: your other modules request a "class" by a string "key," and get back an instance of it that the "service locator" (in this case RequireJS) has been wired to provide for them.
Dependency injection would involve returning the MyModel constructor, i.e. return MyModel, then in a central composition root injecting an instance of MyModel into other instances. I've put together a sample of how this works here: https://gist.github.com/1274607 (also quoted below)
This way the composition root determines whether to hand out a single instance of MyModel (i.e. make it singleton scoped) or new ones for each class that requires it (instance scoped), or something in between. That logic belongs neither in the definition of MyModel, nor in the classes that ask for an instance of it.
(Side note: although I haven't used it, wire.js is a full-fledged dependency injection container for JavaScript that looks pretty cool.)
You are not necessarily abusing RequireJS by using it as you do, although what you are doing seems a bit roundabout, i.e. declaring a class than returning a new instance of it. Why not just do the following?
define(function () {
var value = 10;
return {
doStuff: function () {
alert(value);
}
};
});
The analogy you might be missing is that modules are equivalent to "namespaces" in most other languages, albeit namespaces you can attach functions and values to. (So more like Python than Java or C#.) They are not equivalent to classes, although as you have shown you can make a module's exports equal to those of a given class instance.
So you can create singletons by attaching functions and values directly to the module, but this is kind of like creating a singleton by using a static class: it is highly inflexible and generally not best practice. However, most people do treat their modules as "static classes," because properly architecting a system for dependency injection requires a lot of thought from the outset that is not really the norm in JavaScript.
Here's https://gist.github.com/1274607 inline:
// EntryPoint.js
define(function () {
return function EntryPoint(model1, model2) {
// stuff
};
});
// Model1.js
define(function () {
return function Model1() {
// stuff
};
});
// Model2.js
define(function () {
return function Model2(helper) {
// stuff
};
});
// Helper.js
define(function () {
return function Helper() {
// stuff
};
});
// composition root, probably your main module
define(function (require) {
var EntryPoint = require("./EntryPoint");
var Model1 = require("./Model1");
var Model2 = require("./Model2");
var Helper = require("./Helper");
var entryPoint = new EntryPoint(new Model1(), new Model2(new Helper()));
entryPoint.start();
});
If you're serious about DI / IOC, you might be interested in wire.js: https://github.com/cujojs/wire
We use a combination of service relocation (like Domenic describes, but using curl.js instead of RequireJS) and DI (using wire.js). Service relocation comes in very handy when using mock objects in test harnesses. DI seems the best choice for most other use cases.
Not a singleton in a self-enforcing getInstance()-type singleton, but
a context-enforced singleton (one instance per "context").
I would recommend it only for static objects. It's perfectly fine to have a static object as a module that you load using in the require/define blocks. You then create a class with only static properties and functions. You then have the equivalent of the Math Object that has constants like PI, E, SQRT and functions like round(), random(), max(), min(). Great for creating Utility classes that can be injected at any time.
Instead of this:
define(function() {
var MyModel = function() {
this.value = 10;
}
return new MyModel();
});
Which creates an instance, use the pattern for a static object (one where values are always the same as the Object never gets to be instantiated):
define(function() {
return {
value: 10
};
});
or
define(function() {
var CONSTANT = 10;
return {
value: CONSTANT
};
});
If you want to pass an instance (the result of using a Module that have return new MyModel();), then, within an initialize function, pass a variable that capture the current state / context or pass on the Object that contains information on state / context that your modules needs to know about.

Categories