Pattern for sharing a library between angularjs and node.js - javascript

How can I share a library between angularjs and node.js?
For example an angularjs service is often a reusable piece of code. Let's take a URL library as an example (pick apart and construct URLs).
The same library should be usable in node.js.
My constraint is that I want to share the library code, but I do not want to restrict myself to any loader library on the browser side. So if I need to use RequireJS in the browser, I need to disable any loading part so that can be controlled elsewhere.
So how do I share code?

What you'll see in a lot of different places that support multiple environments is wrapping the entire returned value from your 'service' into a parameter passed to an initialization function from a closure. The one catch to keep in mind with angular is that service probably shouldn't have other dependencies to remain environment agnostic (If this was a simple utility file for example, there would not likely be conflict).
As an example consider:
(function( myService){
if (typeof module !== 'undefined' && module.exports ) {
module.exports = myService;
} else if( angular ){
angular.module('yourModule', [])
.factory('serviceNameHere', function(){ return myService; });
} else {
window.myService = myService;
}
}(function(){
function foo(){/* Do something */}
function bar(){/* Do something else */}
return {
foo: foo,
bar: bar
}
}()))
You could still have dependencies if desired via nodes require syntax, or angular's dependency injection, but the service would likely need modification as it moved from one project to another.

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.

Mediate and share data between different modules

I am just trying to get my head around event driven JS, so please bear with me. There are different kinds of modules within my app. Some just encapsulate data, others manage a part of the DOM. Some modules depend on others, sometimes one module depends on the state of multiple other modules, but I don't want them to communicate directly or pass one module to the other just for easy access.
I tried to create the simplest scenario possible to illustrate my problem (the actual modules are much more complex of course):
I have a dataModule that just exposes some data:
var dataModule = { data: 3 };
There is a configModule that exposes modifiers for displaying that data:
var configModule = { factor: 2 };
Finally there is a displayModule that combines and renders the data from the two other modules:
var displayModule = {
display: function(data, factor) {
console.log(data * factor);
}
};
I also have a simple implementation of pub-sub, so I could just mediate between the modules like this:
pubsub.subscribe("init", function() {
displayModule.display(dataModule.data, configModule.factor);
});
pubsub.publish("init"); // output: 6
However this way I seem to end up with a mediator that has to know all of the module-instances explicitly - is there even a way to avoid that? Also I don't know how this would work if there are multiple instances of these modules. What is the best way to avoid global instance-variables? I guess my question is what would be the most flexible way to manage something like that? Am I on the right track, or is this completely wrong? Sorry for not being very precise with my question, I just need someone to push me in the right direction.
You are on the right track, I'll try to give you that extra push you're talking about:
It you want loose coupling, pub-sub is a good way to go.
But, you don't really need that "mediator", each module should ideally be autonomous and encapsulate its own logic.
This is done in the following way: each module depends on the pubsub service, subscribe to all relevant events and act upon them. Each module also publishes events which might be relevant to others (code samples in a minute, bear with me).
I think the bit you might be missing here is that modules, which use events, will hardly never be just plain models. They will have some logic in them and can also hold a model (which they update when receiving events).
So instead of a dataModule you are more likely to have a dataLoaderModule which will publish the data model (e.g. {data: 3}), once he finishes loading.
Another great requirement you set is sharing data while avoiding global instance-variables - this is a very important concept and also a step in the right direction. What you miss in your solution for this is - Dependency Injection or at least a module system which allows defining dependencies.
You see, having an event driven application doesn't necessarily mean that every piece of the code should communicate using events. An application configuration model or a utility service is definitely something I would inject (when using DI, like in Angular), require (when using AMD/CommonJS) or import (when using ES6 modules).
(i.e. rather then communicating with a utility using events).
In your example it's unclear whether configModule is a static app configuration or some knob I can tweak from the UI. If it's a static app config - I would inject it.
Now, let's see some examples:
Assuming the following:
Instead of a dataModule we have a dataLoaderModule
configModule is a static configuration model.
We are using AMD modules (and not ES6 modules, which I prefer), since I see you stuck to using only ES5 features (I see no classes or consts).
We would have:
data-loader.js (aka dataLoaderModule)
define(['pubsub'], function (pubsub) {
// ... load data using some logic...
// and publish it
pubsub.publish('data-loaded', {data: 3});
});
configuration.js (aka configModule)
define([], function () {
return {factor: 2};
});
display.js (aka displayModule)
define(['configuration', 'pubsub'], function (configuration, pubsub) {
var displayModule = {
display: function (data, factor) {
console.log(data * factor);
}
};
pubsub.subscribe('data-loaded', function (data) {
displayModule.display(data, configuration.factor);
});
});
That's it.
You will notice that we have no global variables here (not even pubsub), instead we are requiring (or injecting) our dependencies.
Here you might be asking: "and what if I meant for my config to change from the UI?", so let's see that too:
In this case, I rather rename configModule to settingsDisplayModule (following your naming convention).
Also, in a more realistic app, UI modules will usually hold a model, so let's do that too.
And lets also call them "views" instead of "displayModules", and we will have:
data-loader.js (aka dataLoaderModule)
define(['pubsub'], function (pubsub) {
// ... load data using some logic...
// and publish it
pubsub.publish('data-loaded', {data: 3});
});
settings-view.js (aka settingsDisplayModule, aka config)
define(['pubsub'], function (pubsub) {
var settingsModel = {factor: 2};
var settingsView = {
display: function () {
console.log(settingsModel);
// and when settings (aka config) changes due to user interaction,
// we publish the new settings ...
pubsub.publish('setting-changed', settingsModel);
}
};
});
data-view.js (aka displayModule)
define(['pubsub'], function (pubsub) {
var model = {
data: null,
factor: 0
};
var view = {
display: function () {
if (model.data && model.factor) {
console.log(model.data * model.factor);
} else {
// whatever you do/show when you don't have data
}
}
};
pubsub.subscribe('data-loaded', function (data) {
model.data = data;
view.display();
});
pubsub.subscribe('setting-changed', function (settings) {
model.factor = settings.factor;
view.display();
});
});
And that's it.
Hope it helps :)
If not - comment!
You do not need a mediator. Just import data, config, and display and call display(data, config) where you need to.
// import data
// import config
function render(){
display(data, config)
}

IOC in AngularJS (1.x) - How can I achieve?

I'm looking for some way to add IOC to my angularjs application.
My app is a multi tenant app and I need to use different services for different tenants with the same registration name.
I'm using TypeScript as well and it works well with using concrete types over interfaces.
My main problem is how can I decide how to register the correct service to my application.
This is an example:
var app = angular.module('app',[]);
// common registrations
app.service('commonService1', commonServiceFunction1);
app.service('commonService2', commonServiceFunction2);
app.service('commonService3', commonServiceFunction3);
app.service('commonService4', commonServiceFunction4);
// here I want to register the same service name with a different implementation
app.service('serviceName', serviceOneFunction); // sometimes I will need this service
app.service('serviceName', serviceTwoFunction); // sometimes I will need this service
Things I thought about:
Add some logic and download the correct js files per source - Function names will stay the same and I will need to figure out a way to provide the correct js file that includes the correct functions.
Override the registrations (e.g. register the common service and then override the registration with a specific implementation).
Both solutions ugly and not scalable to me.
I would like to have a IOC container or some other solution that is a bit more nice and scalable.
If I have understood question correct, you can register services(which are actually providers) as provider which you configure at angular configuration step.
So, it may look like this:
var app = angular.module('app',[]);
// common registrations
app.service('commonService1', commonServiceFunction1);
app.service('commonService2', commonServiceFunction2);
app.service('commonService3', commonServiceFunction3);
app.service('commonService4', commonServiceFunction4);
app.provider('serviceName', function ServiceNameProvider() {
var service = DefaultService;
this.setService = function(newService) {
service = newService
}
this.$get = [function() {
return service;
}];
});
app.config(["serviceNameProvider", function(serviceNameProvider) {
if(someCondition) {
serviceNameProvider.setService(ServiceImpl1);
} else {
serviceNameProvider.setService(ServiceImpl2);
}
}]);
Services, factory are just syntactic sugar over provider. You can determine your service function at configuration stage of angular. ServiceImpl1, ServiceImpl2 are just functions but you can use dependency injection there as they will be called by provider with $injector.invoke. Read about providers
Hope this will help you. Try Using angular factory.
angular.module('app').factory('tenantService', function() {
var tenantService = undefined;
if(tenantType == "1"){
tenantService = new TenantService1();
} else if(tenantType == "2"){
tenantService = new TenantService2();
} else {
tenantService = new TenantDefService();
}
return tenantService;
});

Communication between ViewModels in KnockoutJS

I'm trying to implement communication between my view models in a knockoutjs driven application. I scaffolded it using yeoman tool, and as you can see it uses AMD:
define(['knockout', 'signals', 'text!./nav-bar.html'], function(ko, Signal, template) {
function NavBarViewModel(params) {
this.route = params.route;
}
return { viewModel: NavBarViewModel, template: template };
});
I have to define an object that I would later use to dispatch events, right? Something like that:
var EventDispatcher = {
itemClicked: new Signal()
};
And then, whenever something happens in the NavBarViewModel I'd like to do:
EventDispatcher.itemClicked.dispatch();
The problem is - where should I put this EventDispatcher thing? It's obvious that it should be some kind of a global object, so that every VM could get a hold on it, but it would be ugly. Dependency injection came to mind, since everything else in this architecture I choose is done this way, but how to achieve that? I come from WPF, MVVM world, and so far I've used MVVMLight framework which had this great Messenger component. Is there anything like that in the JS world (and if it's js-signals lib I'm already using, then how should I use it to achieve my goal?)
I could also use the subscribable object built into the knockout fw, but the question still stands - where to put it (or how to share the instance between VMs)?
You'd quite simply inject it by including it in your define.
First, create a new file, EventDispatcher.js with your EventDispatcher code inside (and other relevant Knockout bits, like returning the view model and whatnot).
Then in your current file add it in:
define([ ... , ... , "EventDispatcher"], function( ... , ... , EventDispatcher )
Now you can simply call its methods within this file by using:
EventDispatcher.itemClicked.dispatch()
(Where EventDispatcher is what we've named it in our define parameters).
Do bear in mind though that your EventDispatcher.js file will also need the signals file passed to it through its own define wrapper.

How can requirejs provide a new "class" instance for each define with properties based on the filename?

Use case is implementing a mediator pattern where each instance is a postal.js channel.
Explaining it with the actual code would require some postal.js knowledge, so my example below is conceptually similar, but only uses requirejs.
// animalSounds.js
define([],function(){
return {
cat:'meow',
dog:'woof'
};
});
// animalFactory.js ... the requirejs plugin
define(['animalSounds'],function(animalSounds){
function animalClass(name){
this.sound = animalSounds[name];
}
return {
load:function(name, req, onload, config){
onload(new animalClass(name));
}
}
});
// cat.js
define(['animalFactory'],function(animalInstance){
animalInstance.sound === 'meow'; // should be true
});
// dog.js
define(['animalFactory'],function(animalInstance){
animalInstance.sound === 'woof'; // should be true
});
Basically it's a factory that returns a new instance whenever it's required. That part I can create fine. What I'm struggling with is getting dynamic properties from other sources based on what module is requiring it. While I could define the key as a string in each file, I'm hoping to use the filename as a key to stay DRY.
I would just minimally break the DRY principle and have cat, for instance, like this:
define(['animalFactory!cat'],function(animalInstance){
animalInstance.sound === 'meow'; // should be true
});
The fact of the matter is while it would be possible to do what you want using a loader, the resulting code would be complicated, and would obscure the logic of your application. For instance, requiring animalFactory multiple times and expecting different results (which is what you have in your question) goes against one of the fundamental principles of RequireJS: requiring the same module name over and over again should return the same value over and over again.
Note that it is possible to require the module module (it is a special module) and then use module.id to know the name of the module you are in but unfortunately, this is not available for building the dependency list on your define.

Categories