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.
Related
I am using webpack to transpile/bundle/etc my js files. Im using the import/export syntax ( as opposed to the CommonJS way of require and export.module ). This means i need to import each class and all subclasses of it however many times if i need to use them in the context of a specific script.
The question:
Even though classes arent natively supported in js, why do we need to import them all the time? Wouldnt it be easier if ( and im only speaking for classes ) they were available to all scopes?
EDIT: To avoid polluting the global scope one could do something like global.myLibs and be done with that issue. I personally prefix my classes with something unique but this method would serve even those that dont i suppose.
For example:
window.myClasses could serve as a container for all my classes. I come from an iOS background where all the classes in a main "bundle", in java i think that would be a "package" are available to everyone. Re-importing the class itself doesnt seem like it serves any purpose.
See here:
Why do i need to import modules everytime in webpack bundle?
and here: Bundling js files with webpack class undefined
Adding things in the global scope can lead you to naming conflicts. What if you created a class named Node, add it to global scope by doing window.Node = Node ? You lose the reference to the browser's global Node object.
You can argue that you will never use names that are already used for global objects. But now, what if in a year or so, a new object is added to the spec with the same name as one of yours, and you want to use it alongside your own object ? You have to make a choice, or rename your own object. This is not future proof.
Importing the same module in every module that uses it is a best practice. Because by doing it, you never pollute the global scope. Don't be afraid that it imports the code of this module n times in your final bundle. Webpack imports it only one time, then uses a reference to the module everytime you are importing it.
See these resources :
JavaScript Modules: A Beginner’s Guide by Preethi Kasireddy on Medium
Eloquent Javascript Modules chapter
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.
About a year ago I have used Meteor, and now I want to use it again, but many things have changed.
When I follow the Blaze tutorial on Meteor.com, they add imports on top of their files:
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { ReactiveDict } from 'meteor/reactive-dict';
I got the app working. But when I comment the imports out, the app keeps working like it should work. Why are these imports needed?
I am still using the regular Javascript, not ES6.
Thanks!
The import statement is used to import functions, objects or primitives that have been exported from an external module, another script, etc.
The name parameter is the name of the object that will receive the exported members. The member parameters specify individual members, while the name parameter imports all of them. name may also be a function if the module exports a single default parameter rather than a series of members. Below are examples to clarify the syntax.
Import an entire module's contents. This inserts myModule into the current scope, containing all the exported bindings from "my-module.js".
For more detail about the different ways we can use import along with their usage, please check this.
They still use the old globals for backwards compatibility. However it is recommended to use the imports so if in some future release they remove the globals your code will still work. You can read more in the appropriate section of the guide.
Ok you know import is to import an exported object from another file already.
The point that you may have missed is that MDG heard the need to stop loading everything by default, or at least to provide a mean to control what is loaded in memory and what is not.
Look for the /imports special directory.
Files in that folder are no longer loaded automatically, but only through import statement.
As for the tutorial, I guess they did not explained this functionality, and because it imports only standard functionalities which are still loaded eagerly for backward compatibility, it does not change anything removing those statements.
How do I assign a namespace using ES6 modules? I'd like to do with for example jQuery does, where the namespace is $ but doing it the intended ES6 way. All my modules are structured in separate files which export the classes/functions/whatever as default (e.g. export default class Pikachu). How do I import it into another (main) file so that a user can access this class using e.g. Namespace.Pikachu?
I have come to understand that it might have to do with named exports but I'm not quite totally sure how. Any help please?
If you use modules, you don't need namespaces.
The point of namespaces is to prevent conflicts between different files that define the same names.
Modules eliminate this problem entirely by letting the callsite choose a name to give each module it needs.
You just export a simple object with the things you want, and other files can import that to any name they choose.