AngularJS Best Practice: Should components add themselves to AngularJS module - javascript

I have an AngularJS project with a shared directory, in which there are several shared components.
- shared
- index.js
- alert
- index.js
- alert.component.js
- alert.controller.js
- alert.tpl.html
- alert.scss
- logging
- index.js
- logging.component.js
- logging.controller.js
- logging.tpl.html
- logging.scss
The code is written in a modular way in ES6. So for instance alert.component.js might look like this:
import controller from './alert.controller'
export const Alert = {
controller,
templateUrl: 'shared/alert/alert.tpl.html'
};
I would like to have one AngularJS module called shared in which both components are defined. My question is on what level should I actually define the component. Should it be inside the component directory (shared/alert/index.js) or in the shared directory (shared/index.js).
In the first case, the file shared/alert/index.js, would look like this:
import { Alert } from './alert.component';
angular.module('shared').component('Alert', Alert);
And in the second case, the file would look like this:
export { Alert } from './alert.component';
And then both components would be defined in shared/index.js:
import { Alert } from './alert';
angular.module('shared').component('Alert', Alert);
import { Logging } from './logging';
angular.module('shared').component('Logging', Logging);
The first case seems a bit odd to me, since I kind of let the component add itself to the application. In the second case however I end up with a huge index.js file, if I have many shared components. So I was wondering what other up and down sides each of these approaches has and if there any best practices?

As with any 'best practice', the preferable way to do this is deduced from possible problems that can appear in this situation.
angular.module('shared') module getter is generally an antipattern, especially in modular environment. It should be evaluated after the module was defined with angular.module('shared', []) module setter. Doing the opposite will result in race condition and error.
In the first case importing shared/alert/index.js in tests or another module will result in error because shared module wasn't defined yet.
In the second case there will be no such problem, but only if shared module is defined in shared/index.js.
The approach that plays well with ES modules is one module per file. The result is highly modular application with relatively low amount of boilerplate code. The benefits are reusability and testability, submodules can depend on each other but aren't coupled to share.
This is important consideration if some of submodules contain items that affect the entire application - decorators, config/run blocks, third-party modules that contain them (router).

Related

Dependancy injection using Tsyringe for multiple implementation of interface in Typescript

Context
I am currently working on a Typescript Lambda project where we are planning to refactor our code to make use of dependency injection using the Tsyringe library. We have a typical MVC structure for projects except instead of the Repo/Database layer we have a proxy layer which calls a third-party service over the rest API to fetch the required data.
The catch is that the proxy layer will have a single interface defined and it will have multiple implementations among which one needs to be injected depending upon the business decision. For example AuthProxy is an interface which contains a login method, and it has two different implementation classes KeycloakAuthProxyImpl and AuthZeroAuthProxyImpl. These two implementations will be in 2 separate folders say AuthZero and KeyCloak and while building we pass an argument like --folderName so only one implementation will be available in runtime for dependency injection.
The problem
The problem we are facing with Tsyringe (I have evaluated some other libraries too) is that interface-based dependency injection needs explicit token-based registration with ioc-container in the main.ts page(In my case, the handler function file). So as per theory, I should be registering it as follows.
.
But in our case, this is not possible. Because say we are building it as --keycloak as an argument, then AuthZearoAuthProxyimpl will be ignored while compiling and hence the code will break in line 14 at runtime.
We tried to move that dependency registration logic to the corresponding implementation class so that each implementation class will be self-contained and isolated so that there won't be any runtime issues. But then these are not even being registered for dependency injection and we get an error saying Attempted to resolve unregistered dependency token: "AuthProxy". This is expected as per the file loading of javascript.
KeycloakImpl class.
.
We even tried using #registry decorator which can be found commented in the images, but it also didn't make any difference.
Even though I haven't tried any other dependency injection libraries of Typescript, from my little research, most of them follow more or less the same pattern for interface-based dependency injection and I am anticipating the same issue in those also. Is there any other workaround through which I can resolve this issue, or is it even possible with typescript?
PS: I don't have much expertise in js and typescript, the above keywords are based on my experience with spring and java. Please ignore if I have misused any js specific terminologies while explaining the issue.
Code and project structure
I had similar problems with tsyringe and I found a much better approach.
Here is how I would solve this with a different DI lib iti and also remove many lines of code:
import { createContainer } from "iti"
import express from "express"
// import other deps
const container = createContainer()
.add({
authProxy: () =>
Math.random() > 0.5
? new KeycloakAuthProxyImpl()
: new AuthZearoAuthProxyImpl(),
})
.add((ctx) => ({
authService: () => new AuthServiceV3(ctx.authProxy),
}))
.add((ctx) => ({
server: () => {
const app = express()
app.all("/login", async (req, res) => handler(req, ctx.authService))
return app
},
}))
// start your server / lambda
const server = container.get("server")
server.listen(3000)
I've also refactor other parts of the app and got rid of singletons and made code IMO a bit simpler to read
I’ve created an interactive playground with a mock your app:
https://stackblitz.com/edit/json-server-ntssfj?file=index.ts
Here are some links to the lib:
https://github.com/molszanski/iti
https://itijs.org/

How do you export an interface into global namespace - angular

I have a file with defined interfaces I'd like to share with my app's global namespace. My first attempt to do this was that I imported the interfaces.ts file into my app.module.ts and placed the interface into the exports array, but types/interfaces cannot be used in this way, so I thought of a few questions related to this small issue.
Does app.module.ts need to be involved for exporting to the global namespaces?
Is there a different way to do it (probably a native javascript way)?
If app.module.ts is required to do this, how do I import an interface in a way that my app.module.ts shares it with the rest of my app, so that I wont have to constantly import it in my components?
Checkout the global namespace here: https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html
/*~ Note: If your global-modifying module is callable or constructable, you'll
*~ need to combine the patterns here with those in the module-class or module-function
*~ template files
*/
declare global {
/*~ Here, declare things that go in the global namespace, or augment
*~ existing declarations in the global namespace
*/
interface String {
fancyFormat(opts: StringFormatOptions): string;
}
}
Beware though:
This pattern is somewhat dangerous due to the possibility of runtime conflicts, but we can still write a declaration file for it.
It's better to have a SharedModules for everting that you want to use in the entire application more than one place according to the Angular official document. In this SharedModules you can have different directory app structure for all common things such as Models, Services, Interceptors, Pipe, components and etc.
Then Use this module on your main app.module.ts and import what you need.
For more information study this helpful & quick guide on PluralSight.

Dynamic loading of modules and components at runtime in Angular 4

I've been looking to develop a method for loading modules and/or components into an AOT-compiled Angular 4 application and been stymied by a variety of solutions that never quite seem to get me where I want to be.
My requirements are as such:
My main application is AOT compiled, and has no knowledge of what it is loading until runtime, so I cannot specifically identify my dynamic module as an entry component at compile time (which is explicitly necessary for the 'dynamic' component loading example presented on Angular.io)
I'd ideally love to be able to pull the code from a back end database via a GET request, but I can survive it simply living in a folder alongside the compiled site.
I'm using Webpack to compile my main application, breaking it into chunks - and so a lot of the SystemJS based solutions seem like dead ends - based on my current research, I could be wrong about this.
I don't need to know or have access to any components of my main application directly - in essence, I'd be loading one angular app into another, with the dynamically loaded module only perhaps having a few tightly controlled explicit interface points with the parent application.
I've explored using tools like SystemJsNgModuleLoader - which seems to require that I have the Angular compiler present, which I'm happy to do if AOT somehow allowed me to include it even if I'm not using it elsewhere. I've also looked into directly compiling my dynamic module using ngc and loading the resulting ngfactory and compiled component/module, but I'm not clear if this is at all possible or if so - what tools Angular makes available to do so. I have also seen references to ANALYZE_FOR_ENTRY_COMPONENTS - but can't clearly dig up what the limitations of this are, as first analysis indicates its not quite what I'm looking for either.
I had assumed I might be able to define a common interface and then simply make a get request to bring my dynamic component into my application - but Angular seems painfully allergic to anything I try to do short of stepping outside of it alltogether and trying to attach non-angular code to the DOM directly.
Is what I'm trying to do even possible? Does Angular 2+ simply despise this kind of on the fly modification of its internal application architecture?
I think I found an article that describes exactly what you are trying to do. In short you need to take over the bootstrap lifecycle.
The magic is in this snippet here.
import {AComponentNgFactory, BComponentNgFactory} from './components.ngfactory.ts';
#NgModule({
imports: [BrowserModule],
declarations: [AComponent, BComponent]
})
export class AppModule {
ngDoBootstrap(app) {
fetch('url/to/fetch/component/name')
.then((name)=>{ this.bootstrapRootComponent(app, name)});
}
bootstrapRootComponent(app, name) {
const options = {
'a-comp': AComponentNgFactory,
'b-comp': BComponentNgFactory
};
https://blog.angularindepth.com/how-to-manually-bootstrap-an-angular-application-9a36ccf86429

Is using an ES6 import to load specific names faster than importing a namespace?

I've found at least two ways to import functions in from a module like Ramda for example. There are probably a few more ways to do something very similar like const R = require('ramda');
Option 1 is to import certain functions:
import { cond, T, always, curry, compose } from 'ramda';
Option 2 is to import the whole module like:
import * as R from "ramda";
I would prefer to reference the module from which the function is being called like so:
R.T();
But if the 2nd option is used, does it bring in every Ramda function not just the ones used in a module I'm working in? Are there any impacts on actual memory use, or bandwidth use as far as what gets sent to the browser if option 2 is used?
Is it possible to somehow do this:
// invalid syntax below:
import R { cond, T, always, curry, compose } from 'ramda';
R.T();
My question is kinda related to this one, but it's a bit different
import R (ramda) into typescript .ts file
TL;DR: It does not matter.
import * as … from 'ramda';
import { … } from 'ramda';
will both by default always bring in the complete Ramda module with all its dependencies. All code inside the module would be run, and which syntax was used to reference the exported bindings doesn't matter. Whether you use named or namespaced imports comes down to preference entirely.
What can reduce the file size to download and the used memory is static analysis. After having evaluated the module, the engine can garbage-collect those bindings that are referenced from nowhere. Module namespace objects might make this slightly harder, as anyone with access to the object can access all exports. But still those objects are specified in a way (as immutable) to allow static analysis on their usage and if the only thing you're doing with them is property access with constant names, engines are expected to utilise this fact.
Any size optimisation involves guessing which parts of the module need to be evaluated and which not, and happens in your module bundler (like Rollup or WebPack). This is known as Tree Shaking, dropping parts of the code and entire dependencies when not needed (used by anything that got imported). It should be able to detect which imports you are using regardless of the import style, although it might have to bail out when are doing unusual things with the namespace object (like looping it or using dynamic property access).
To learn about the exact guesses your bundler can make, contact its documentation.
#Bergi is right in his comment, which I think should be the answer. I would also like to point out you can always try things out in Babel to see what it compiles to: click here to see what an example destructuring actually does
So basically even if you destructure just one function from the module, the whole module will be required. In the Babel example I gave, I just extracted Component from the 'react' module, but the compiled code actually just required the whole thing. :)
Adding to #Bergi, Also just for future reference if you want to shed unused functions and import only the desired function, use selective import like below
import isEmpty from 'ramda/src/isEmpty';
This way you can complement it with Webpack and get a better tree shaking. Hope it helps

Exposing application scripts to certain scripts only

uhh it's hard to come with a right title for this problem excuse me.
In a backbone.js application i am building. Models, Views, Templates are all in separate javascript, html files. I want to export the Models, Views and Templates to the application bootstapper file (app.js) without polluting the global variable i.e doing window.App.Model = myModel; that. By export i mean make the code inside the files available to app.js for initialization and running
How do i go about doing this?
Are there any patterns that will solve the problem? Could you provide me a example
Description
In cases where models,views and templates are split to many disparate files the application bootstrapper file app.js should have some means to access these M,V,C components. Hence common approach is to do below inside the model.js file
window.App.Model.PersonModel = Backbone.Model.extend({});
App.js
var instance = new window.App.Model.PersonModel();
var personView = new window.App.Views.PersonView({model:instance});
Finally you see that everything derives from the Global object App which i think is not safe, improper and weak way to build application dependencies
Suggestions
Just to the above question, could someone suggest a template loading library(javascript templates regardless of engine used) that can be used to load the templates
Take a look on RequireJS, which support asynchronous module definitions/loading. You would have to rewrite your modules to and app.js to satisfy AMD api, but it would take only few strings of code.

Categories

Resources