Export an angular element as module - javascript

I have created a widget using Angular element successfully, which is working fine in HTML.
But how do I export it as a module so that it can be used in another Angular, React, Vue web applications as well to import import { acmaModule } from package-name instead const acmaModule = require('widge.js')
Angular
export class AppModule {
initWidget(options: any){
console.log('options received', options)
}
constructor(private injector: Injector) {
// Expose Window function
(<any>window).acmeWidget = { initWidget: this.initWidget };
// Survey Widget
const surveyComp: any = createCustomElement(SurveyComponent, { injector: this.injector });
customElements.define('acme-survey', surveyComp);
}
ngDoBootstrap() {}
}
HTML
<html>
....
<script src="../survey-widget.js">
<script>
acmeWidget.initWidget({
test: "Hello world"
})
</script>
....
</html>

You can create an export file and declare the exporting for all modules you want to package for external references in there.
For example, you can add a file export.ts to the src folder and then add something like this to the content of the file, assuming your AppModule is under src\AppModule.ts:
//content of export.ts
export * from './AppModule';
...
then in your ng-package.json file, you need to declare export.ts as entry file there. Something like:
//content of ng-package.json
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/<your built package name>",
"lib": {
"entryFile": "src/export.ts"
}
}
then after you build your project, you can run npm publish to create the artifact that you can install through npm and import later. Assuming your project's name is test-project, it will be something like:
ng build test-project --prod
# after build is done:
cd dist/test-project
npm publish
see references for npm publish

Related

How to access method of Project A into Project B in nodejs, i am using nestjs

i have MailEvent class and want to use SendEmail method in client project. i use NPM link to share code for client project. but i can't access this method. how can i access this method in client project. i am using nestJS framework. thanks
#Injectable()
export class MailEvent {
constructor(private eventEmitter: EventEmitter2) {}
// store email details to RabbitMQ
SendEmail() {
amqplib.connect(
'amqp://localhost',
(connectionError, connection) => {
if (connectionError) {
throw connectionError;
}
connection.createChannel((channelError, channel) => {
if (channelError) {
throw channelError;
}
const queue = 'Test_emails_queue';
channel.assertQueue(queue, { durable: false });
channel.sendToQueue(queue, Buffer.from(JSON.stringify(email)));
// create event for sending email
this.eventEmitter.emit('send_email', email);
});
},
);
}
In nestjs we can create a reusable module. Then, this module can be imported to another module. If the module is stored in separated repo, then we can build the module first before installing it to another repo using NPM.
Steps to do:
generate nestjs resource, e.g. MailEventModule on the terminal. It will also create the controller and service class
nest g resource mail-event
export the service via the module file mail-event.module.ts
import { Module } from '#nestjs/common';
import { MailEventService } from './mail-event.service';
#Module({
providers: [MailEventService],
exports: [MailEventService]
})
export class MailEventModule { }
Build the module. It will create a new folder named DIST containing the module ready to be installed in another repo/project
npm run build
upload the mail-event module to npm
install mail-event in the client project using npm command, e.g. npm install mail-event
Import the module to the client project in client-project.module.ts
import { Module } from '#nestjs/common';
import { MailEventModule } from 'mail-event';
#Module({
imports: [MailEventModule]
})
export class ClientProjectModule { }
Add MailEventService as dependency to the client project service in client-project.service.ts
import { Injectable } from '#nestjs/common';
import { MailEventService } from 'mail-event';
#Injectable()
export class ClientProjectService {
constructor(private mailEventService: MailEventService) { }
myFunction() {
this.mailEventService.sendEmail();
}
}
If you have tried this steps, please give us the feedback whether it works or not. Thank you

Set up Angular 6 environment variables from .env

There's an angular 6 project using environment variables from ./project/src/environments/environment.prod.ts
export const environment = {
production: true,
testVar: 'gg',
};
The backend for this project also has env variables in a .env file, so a lot of variable duplicate angular env variables. It would be nice to have something like
export const environment = {
production: true,
testVar: process.env.TEST_VAR
};
, so I didn't have to duplicate variables.
ie
I'd like to parse variables from a .env file and assign their values to angular env variables during typescript compilation on the server.
How can this be done?
Maybe with webpack?
UPDATE
Some clarification. My .env file contains no json. It looks like this:
TEST_VAR=1
UPDATE
Since ng eject is not available for Angular 6, I don't seem to be able to hack into webpack config. Looks like deadend here.
ng eject
Overview
Temporarily disabled.
Ejects your app and output the
proper webpack configuration and scripts.
This question becomes also more and more important, when we want to containerize angular applications.
My research lead me to an idea, where I have to write a little node.js or typescript program, using dotenv for reading .env file and create the environment.ts file at buildtime, before starting ng serve.
You can create entries in the package.json like this:
...
"config": "ts-node set-env.ts",
"server": "npm run config && ng serve"
...
and run it with
npm run server
Here is a good explanation with an example typescript file:
https://medium.com/#ferie/how-to-pass-environment-variables-at-building-time-in-an-angular-application-using-env-files-4ae1a80383c
You can create a config file and populate in Run-time.
1) create a File(app-config.json) in assets folder with your variables
{ "servicesUrl": "https://localhost:8080/api"}
2) create a service (AppConfigService ) to read the file.
#Injectable()
export class AppConfigService {
private appConfig;
constructor (private injector: Injector) { }
loadAppConfig() {
let http = this.injector.get(HttpClient);
return http.get('/assets/app-config.json')
.toPromise()
.then(data => {
this.appConfig = data;
})
}
get config() {
return this.appConfig;
}
3) Next we need to tell our application to execute the loadAppConfig() method of our service.
import { NgModule, APP_INITIALIZER } from '#angular/core';
import { AppConfigService } from './services/app-config.service';
#NgModule({
...,
providers: [
AppConfigService,
{
provide: APP_INITIALIZER,
useFactory: appInitializerFn,
multi: true,
deps: [AppConfigService]
}
],
...
})
export class AppModule { }
4) create a function called "appInitializerFn" to call our service in AppModule (app.module.ts)
const appInitializerFn = (appConfig: AppConfigService) => {
return () => {
return appConfig.loadAppConfig();
}
};
...
#NgModule({
...
})
export class AppModule {}
5) import environment and use it :example
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { AppConfigService } from './services/app-config.service';
#Injectable()
export class DataContextService {
basePath: string;
constructor (private environment: AppConfigService, private http: HttpClient) {
this.basePath = environment.config.servicesBasePath;
}
getNames() {
return this.http.get(this.basePath + '/names/');
}
}
for more information please see:
link
If you want to use variables in build time you could use dotenv
As early as possible in your application, require and configure dotenv.
require('dotenv').config()
Create a .env file in the root directory of your project. Add environment-specific variables on new lines in the form of NAME=VALUE. For example:
DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3

Expose two (or more) Node.js Babel classes in a NPM scoped package

I have two Node.js Babel classes in a public scoped package and want to expose them both.
Classes in the public scoped NPM:
Index.js
export default class Index {
constructor() {
console.log("abc!");
}
}
SecondClass.js
export default class SecondClass {
constructor() {
console.log("SecondClass! SecondClass!! SecondClass!!!");
}
}
In the public scoped NPM I'm able to set only one main file:
"main": "./dist/index.js",
And in the usage project I'm trying to import them both but I'm failing badly!
import { Index, SecondClass } from '#my-scope/my-package';
new Index();
new SecondClass();
Dependencies:
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1"
}
How can I do it? Can I do it somehow?
UPDATE 1: Even using the files in the package didn't work and make it worse, once I use it all JS files are gone when I install my scoped package in the usage project.
"files": [
"./dist/index.js",
"./dist/SecondClass.js"
],
UPDATE 2: I tried to use the "files":[ ... ] as mentioned before and once I was not able to find any workaround for this I posted an issue in the NPM Github: https://github.com/npm/npm/issues/18481
After many hours researching I was able to find a workaround using vanilla code but keeping the Babel usage.
For the main JS file in your scoped NPM package e.g. index.js you must export all classes like:
import SecondClass from './SecondClass';
import ThirdClass from './ThirdClass';
export {
SecondClass,
ThirdClass
};
And in your usage project you can import like:
import { SecondClass, ThirdClass } from '#my-scope/my-package';
new SecondClass();
new ThirdClass();
The package.json from your scoped NPM package only need to expose the index.js like "main": "./dist/index.js".
Dependencies and versions:
"babel-cli": "^6.26.0"
"babel-preset-es2015": "^6.24.1"
$ npm -v: 3.10.10
$ node -v: v6.11.3
The common approach is to create a file named index.js such that it reexports the different modules in the folder:
/src
/foo.js
/bar.js
/index.js
// index.js
export * from './foo';
export * from 'bar';
This will allow you to import directly from the folder instead of an specific file.
import {foo, bar} from './src';
It is possible, try this:
Index.js
import SecondClass from './SecondClass';
class Index {
constructor() {
console.log("abc!");
}
}
export { Index, SecondClass };
Usage
import { Index, SecondClass } from '#my-scope/my-package';
new Index();
new SecondClass();

How to lazyload library in Angular 4 module?

I have an app with multiple modules.
One of the modules is to visualize pdf. I use pdf.js which is pretty greedy and the vendor.js is somehow big because of this.
Is there a way to lazyload the library at the same time I lazy load the pdf-module ?
I've noticed this answer, but it doesn't feel as right.
Load external js script dynamically in Angular 2
I am not trying to lazyload a module but an external library.
If you want to lazy load external libraries such as jquery, jspdf you can create some service like:
lazy-loading-library.service.ts
import { Injectable, Inject } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { DOCUMENT } from '#angular/platform-browser';
#Injectable()
export class LazyLoadingLibraryService {
private loadedLibraries: { [url: string]: ReplaySubject<any> } = {};
constructor(#Inject(DOCUMENT) private readonly document: any) { }
public loadJs(url: string): Observable<any> {
if (this.loadedLibraries[url]) {
return this.loadedLibraries[url].asObservable();
}
this.loadedLibraries[url] = new ReplaySubject();
const script = this.document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = () => {
this.loadedLibraries[url].next('');
this.loadedLibraries[url].complete();
};
this.document.body.appendChild(script);
return this.loadedLibraries[url].asObservable();
}
}
And whenever you need some external library just use this service that will load library only once:
app.component.ts
export class AppComponent {
constructor(private service: LazyLoadingLibraryService) {}
loadJQuery() {
this.service.loadJs('https://code.jquery.com/jquery-3.2.1.min.js').subscribe(() => {
console.log(`jQuery version ${jQuery.fn.jquery} has been loaded`);
});
}
loadJsPdf() {
this.service.loadJs('https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.5/jspdf.min.js').subscribe(() => {
console.log(`JsPdf library has been loaded`);
});
}
Plunker Example
If you're looking for lazy loading angular module then these questions might be helpful for you:
How to manually lazy load a module?
How to lazy load Angular 2 components in a TabView (PrimeNG)?
I hope you're using Angular CLI.
Install pdfjs-dist package:
npm install pdfjs-dist
Install types for it:
npm install #types/pdfjs-dist --save-dev
Add the following import statement to your lazy loaded module file:
import 'pdfjs-dist';
The last step will embed pdf.js source code in the lazy loaded bundle when you run ng build.
You should be able to access the global PDFJS variable from your code.
Hope this helps.

Auto-completion in Webstorm for my custom npm module (ES6/Babel)

When I use material-ui package I get nice auto-completion in Webstorm (ctrl+space):
I thought it might have something to do with the fact the package includes an index.es.js file:
import _AppBar from './AppBar';
export { _AppBar as AppBar };
import _AutoComplete from './AutoComplete';
export { _AutoComplete as AutoComplete };
import _Avatar from './Avatar';
export { _Avatar as Avatar };
import _Badge from './Badge';
export { _Badge as Badge };
import _BottomNavigation from './BottomNavigation';
...
So I generated my own index.es.js in my custom npm module and put it next to the transpiled index.js:
import {ActionTypes as _ActionTypesElements} from './reducers/elements/elements';
export { _ActionTypesElements as ActionTypes };
import {ActionTypes as _ActionTypesAppState} from './reducers/appState/appState';
export { _ActionTypesAppState as ActionTypesAppState };
import _appStateActions from './reducers/appState/appState_actions';
export { _appStateActions as appStateActions };
...
And yet I get no auto-complete:
Any idea why?
Found the answer:
Had to add a jsnext:main field to the package.json of the npm module:
package.json:
...
"module": "./index.js",
"jsnext:main": "./index.es.js",
...
Webstorm recognizes the package's inner exports.
In WebStorm 2019.3, here are the steps I follow to force Code Completion (including auto-import) to work for a custom, self-published NPM package:
Ensure that the project, itself, has a package.json file at the root of the project, and that package.json includes the desire package in the "dependency" object. For example:
{
"name": "testproject",
"version": "1.0.0",
"dependencies": {
"#yourname/yourpackage": "latest"
}
}
In WebStorm, select File > Invalidate Caches / Restart...
To enable auto-import for package contents, ensure that the JavaScript file in which the package is being used has AT LEAST ONE export statement. For example, in the following code, an export is present, so Code Completion auto-imports the package function isNil():
export function init () {
isNil
}
By comparison, the following code does not contain an export statement, so isNil() is not automatically imported:
function init () {
isNil
}
For me, all three of the preceding steps are necessary for Code Completion to work for my own NPM packages in WebStorm.

Categories

Resources