I have a react application that went from storing config vars (urls, mostly) in config files to storing those same variables in window.envs, for reasons that are out of scope for this question.
Header contains a simple script tag:
<script src="env-vars.js"></script>
Variables are laded into window
// env-vars.js
window.envs = {
QA_URL: 'qa.catdog.com'
...
}
Variables are made available to the app:
// envs.js
export default window.envs;
Then envs.js is imported into whatever component needs them.
import envs from 'config/envs';
This solution works for the app itself. Unfortunately, the change broke a lot of unit tests.
Given that the the env vars have to live in window and cannot be imported directly (at least no by the app, the unit tests can do what they want), how can I go about making window variables available to unit tests?
EDIT: the unit test stack is Enzyme/Mocha/Chai. Sinon too.
Related
I have global constants stored in config.js
const WORKER_STATE_START = 0;
const WORKER_STATE_COMPLETE = 1;
// and other constants
...
I can use importScripts("config.js") make them accessable in my webworker, however constants in config.js are global constants, not just available for web worker, they should also be available in main thread scripts. I've tried things like export {WORKER_STATE_START} in config.js and reported error.
currently, I created another file "global.js" which just copy the constants in config.js and use "export" for other scripts in main thread, this works but.... tow copies of same constants looks rediculous....
so, what's the best way to use global constants(accessable for both mainthread and webworker thread)?
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.
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).
I need to add a hosted third-party JavaScript file in an Angular 2 component. This file is updated anytime a change is made in the associated third-party vendors proprietary system, so I cannot simply pull down a copy, include it locally, and import it into the project.
Typically I would include this file at a top level in a script tag, and then simply use declare <windowvar>: any to get access to it. However in this case, since the component itself is trying to load the script, I cannot declare the window variable because it does not exist on the window object at the time the component is loaded, which generates an error.
I can load the script by manually adding a script tag, and that works, however I need access to the window variable it creates in order to use it properly. And I cannot simply use an interval to look for it because typescript throws a fit that <windowvariable> does not exist on object window.
Is there any way I can 1) Load the hosted JavaScript file inside the component, and 2) Get access to the window variable created by the loaded JavaScript file?
Update 1: Based on the comments, the previous solution will be not help you.
You can do that by using OpaqueToken in Angular2
1. Create a Token that is used to find an instance as below in a separate ts file.
import { OpaqueToken } from '#angular/core'
export let name_of_The_Token = new OpaqueToken('name_Of_The_Window_Object');
2. In your App.module, you need to import and declare a variable that is the name of your window object which makes the Token as a angular2 service so that you can use properties, methods in that javascript file across your components.
import { name_of_The_Token } from '/* file_Path */';
declare let name_Of_The_Window_Object : any; //below your import statements
Step 3: Inject it to providers array of your module.
{ provide : name_of_The_Token , useValue : name_Of_The_Window_Object }
Guidance to use this token in components
Import the token just like any other service and #Inject from angular-core
import { name_of_The_Token } from '/* file_Path */';
import { Inject } from '#angular/core';
In constructor of the component
constructor(#Inject( name_of_The_Token ) private _serviceObject : any )
Any where in your component you can use the variables and methods of your javascript file as
this._serviceObject.method1()
this._serviceObject.variable1
.....
Note: One drawback is that you will not get intellisense.
Overcoming it:
If you are looking for intellisense you need to wrap the methods and variables inside an interface and use it in the type**(instead of any)** of your token as
export interface myCustom {
method1(args): return_Type;
method2(args): void;
.....
}
LIVE DEMO of ToasterService
Effectively, you're trying to pick up a "global module" javascript library from a CDN. (I assume the 3rd-party lib is not in CommonJS, AMD, UMD, or other module format, since it is accessed through a sole global variable.)
So the first question is where is the corresponding .d.ts file? It contains the names and interfaces that inform Typescript of the 'shape' of the library, as well as declaring that global variable will exist. If your 3rd-party doesn't provide it you'll need to write it yourself. It will contain not just the declaration of the global var, like
declare var theGlobalVarInQuestion: IInterfaceOfStuffInsideLibrary;
but also the declaration of said Interface, and its properties and their types, all the way down. Like this: https://github.com/catamphetamine/libphonenumber-js/blob/master/index.d.ts
You can include the .d.ts file in /node_modules/#types/nameOfSaidLibrary but you'd need to check it into your source repo (with possible .gitignore gymnastics) especially because a npm prune will remove it. Or if you put it elsewhere, modify the tsconfig.json typeroots property to look in both that place in addition to its usual /node_modules/#types/ folder.
Just to be clear, the .d.ts file doesn't (and shouldn't) actually create the variable; it just states that it will be there so the Typescript compiler won't complain. Whether or not it is there at runtime is decided by however you're loading the js.
If you're not loading it via script tag in the index.html, then either a Typescript import statement in the consuming component can do so (given the right config of SystemJS or whatever you're using) or the consuming component can dynamically create and append a new script tag to the index.html. Just make sure your module loader isn't trying to download and immediately bundle it with the rest of your app at buildtime. It sounds like you want the lib to be downloaded anew each time at runtime.
When I want to debug my requirejs app, I need to reference my application everytime with something like:
var App = require('app');
as stated here:
http://requirejs.org/docs/api.html#modulenotes
From that moment I can access everything via console because the App variable points to my application instance.. However, it is very annoying to have to do it after every page refresh. Is there any alternative that would help improve the development workflow?
You could deliberately export a reference to your application into the global space. This is actually what I do. I select a name that has very little likelihood of clashing. In your app module you could do this after your App object is created: window._myproject_app = App. This is then accessible as the global _myproject_app. You can start writing the first characters and use autocomplete rather than type the whole thing when you want to access it.
If you want the export to occur only in testing you can use RequireJS' config facility to pass configuration that tells the module responsible to export the instance whether it should export it or not.