Unable to import my locally developed Angular module into Angular app - javascript

I'm looking at using this Yeoman generator as a starter for a mini project containing a few reusable form components I can publish. The generator builds a module and an example component, directive, pipe and service configured for use (you can see the template files for the generator here).
I then used npm link to allow me to install the generated project in my Angular app, which appears to work fine, as demonstrated below taken from the project's node_modules directory;
I have then imported the module into my module within my Angular app;
import { SampleModule } from 'my-test-library';
#NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
NgbModule,
MomentModule,
SampleModule // <-- Imported Here
],
declarations: [],
providers: []
})
export class TasksModule { }
This module import causes the error Uncaught Error: Unexpected value 'SampleModule' imported by the module 'TasksModule' and I cannot figure out why.
There are two things I notice when comparing the library I have imported with another third party library (e.g. ng-bootstrap)
As I have used npm link to import the library to allow me to test during development the entire folder structure has been imported (rather than just the dist directory. I assume this means the non-dist code is being imported by the TasksModule. If I attempt to import my-test-library/dist I get a module cannot be found error.
Unlike ng-bootstrap my library appears to be missing *.metadata.json files in the output.
My library's tsconfig.json file is as follows (unchanged from generator install)
{
"compilerOptions": {
"noImplicitAny": true,
"module": "commonjs",
"target": "ES5",
"emitDecoratorMetadata": true, <-- Checked to ensure this was true
"experimentalDecorators": true,
"sourceMap": true,
"declaration": true,
"outDir": "./dist"
},
"files": [
"typings/index.d.ts",
"index.ts"
],
"exclude": [
"node_modules",
"dist"
]
}
Based on these items, or something else, what am I missing to get this working? Thanks!
Edit: sample.*.d.ts files (as default after installation)
// sample.component.d.ts
export declare class SampleComponent {
constructor();
}
// sample.directive.d.ts
import { ElementRef } from '#angular/core';
export declare class SampleDirective {
private el;
constructor(el: ElementRef);
}
Edit 2
So I tried symlinking the dist directory (my-test-library/dist) to the root of node_modules and importing the module from there and it worked fine.
Is there a way to, using npm link, only import the dist directory (at the root)?
I also don't understand why updating my original import to import { SampleModule } from 'my-test-library/dist'; does not work?

Did you try to run watch on your code (something like "npm run watch")? That would give a better error message.
Assuming this is your index.ts file or similar to this (https://github.com/jvandemo/generator-angular2-library/blob/master/generators/app/templates/index.ts) What I am guessing is that either your module is not being exported properly or there are some circular dependencies among your exports. First, try changing the following four lines in your index.ts from:
export * from './src/sample.component';
export * from './src/sample.directive';
export * from './src/sample.pipe';
export * from './src/sample.service';
to
export {SampleComponent} from "./src/sample.component";
export {SampleDirective} from "./src/sample.directive";
export {SamplePipe} from "./src/sample.pipe";
export {SampleService} from "./src/sample.service";
If that doesnt work, then try changing the order of your exports. If still no luck, then please trying sharing the whole folder somewhere. Thanks,

In your file listing I do not see a sample.module.ts, only a component, directive, pipe and service. If you do not have a file with the #ngModule decorator which imports/provides these, then you are not truly importing a SampleModule.
the file should look something like this:
sample.modeule.ts
import { NgModule } from '#angular/core';
import { SampleDirective } from './sample.directive';
import { SampleComponent } from '../sample.component';
import { SamplePipe } from './sample.pipe';
import { SampleService } from './sample.service';
#NgModule({
imports: [ ],
declarations: [
SampleDirective,
SampleComponent,
SamplePipe
],
providers:[SampleService],
exports: [
SampleDirective,
SampleComponent,
SamplePipe
]
})
export class DayModule { }

Related

how to bundle a module/component to js and use in another angular project?

I was just trying to build a module/component and serve it as a JS bundle. it builds:
#Component({
selector: 'app-component-overview',
template: '<button><ng-content></ng-content></button>',
})
export class ButtonComponent {}
#NgModule({
declarations: [ButtonComponent],
})
export class AppModule {}
the issue is that after building it to a javascript bundle. when I try to import it in another angular project. I get:
//ERROR in ... error TS2306 '...' is not a module.
loadChildren: () => import('...').then(m=>m.ButtonModule)
maybe I am missing the point here and things are different in angular, having webpack control makes it a lot easier but I want to avoid bringing custom builders and fiddling with the settings as little as possible.
Question is, is there a well documented way to bundle a module or even a standalone component to be used outside my project as a JS bundle? I could not find anything useful other than high level explanation of reusing and lazyloading thing already inside the same project.
It seems you are building this module as part of an application which is supposed to run in a browser. But what you are looking for is to build this module as part of a library which can be re-used in other projects.
See this official Angular guide on how to create a library.
After a few hours browsing around. I figured it out.
The answer that #json-derulo gave is part of the solution but there are more steps to make this work.
follow the guide: https://angular.io/guide/creating-libraries
here is the tricky part if you import the lib inside the workspace it will work. but that does not make much sense. You likely have another repository with a angular app that you want to consume the lib.
now to be able to import the this component as a lazy loaded route you will need to add "#angular/router" to the lib peerDependecies and run npm install again
now create a routing module and add the empty path to point to the component in the lib.
//my-lib-routing.module.ts
import { NgModule} from "#angular/core";
import { RouterModule } from "#angular/router";
import { MyLibComponent } from "./my-lib.component";
const routes= [
{ path: '', component: MyLibComponent },
];
#NgModule({
imports: [
RouterModule.forChild(
routes,
)
// other imports here
],
// providers: [RouterModule],
exports: [RouterModule]
})
export class myLibRoutingModule { }
change the libmodule to import the routing module
//
import { NgModule } from '#angular/core';
import { myLibRoutingModule } from './my-lib-routing.module';
import { MyLibComponent } from './my-lib.component';
#NgModule({
declarations: [MyLibComponent],
imports: [
myLibRoutingModule
],
})
export class MyLibModule { }
on the lib project root run 'npm run build'.
create a separate repo and "ng new app"
add the lib to the package.json of the main app, something like "my-lib": "../my-lib-ws/dist/my-lib".
open angular.json and add "preserveSymlinks": true to the build options.
run npm i to link the lib as a dependency.
add the libmoudle as a lazy loaded route
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
const routes: Routes = [
{path: 'test', loadChildren: () => import('my-lib').then(m=>m.MyLibModule)}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
run npm run start. and try to access the 'test' route. you should see the component loaded in the main app router outled
Now, this is more of learning exercise the bundles almost gets doubled. and you would profit more using module federation. But it is a use case we have. Anyhow the steps are here if anyone can't reproduce let me know.

Why is element-ui importing the entire library? | Large bundle size

So, I am trying to import a few components from Element-ui on demand, but for some reason it keeps importing the entire library (docs about importing on demand here). See image below.
I already followed this post on how to import individual elements from ElementUI.
I installed the element-ui library with npm.
Then in my main.js file I import components on demand as follows:
import 'element-ui/lib/theme-chalk/button.css';
import 'element-ui/lib/theme-chalk/popover.css';
import { Button } from 'element-ui';
import { Popover } from 'element-ui';
Vue.use(Button)
Vue.use(Popover)
I installed the babel-plugin-component dependency. It uses the following config in .babelrc
{
"presets": [
[
"es2015",
{
"modules": false
}
]
],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
I also use the Message function quite a lot. I used to import this in every component I use the function like they have stated in their documentation:
import { Message } from 'element-ui';
But I've read somewhere that that also imports the entire lib. So I changed that to this:
import Message from 'element-ui/packages/message';
But whatever I do, my bundle (and specifically ElementUI) keeps getting bigger...
What am I missing here?

Angular class is not an Angular module

When I build my Angular library, publish it to npm, and use it as a dependency in another project, whenever I try to import on of my module classes into my app.module.ts, and get this error Class TekButtonModule is not an Angular module. I have followed steps from multiple different sites on how to create, build, and publish angular libraries, and I can't figure out why it won't recognize the class as a valid module class.
In my app.module.ts, this is how I am importing the module:
import { TekButtonModule } from "tek-angular-uimodules";
#NgModule({
imports: [
TekButtonModule
]
})
export class AppModule { }
My library project follows the standard directory structure outlined by Angular. I did not change anything with the build, or path settings when I setup the library project. My built library project has a bundles, esm5, esm2015, fesm5, fesm2015, lib (where my custom modules, and components are), as well as a package.json, public-api.d.ts (exports everything in the lib directory), README.md, tek-angular-uimodules.d.ts (exports the public api), and a tek-angular-uimodules.metadata.json file.
Is there some extra configuration that it needed that isn't setup by default to get modules to work correctly?
Here is my button module class:
import { CommonModule } from "#angular/common";
import { NgModule } from "#angular/core";
import { TekButton } from "./button";
#NgModule({
declarations: [
TekButton
],
exports: [
TekButton
],
imports: [
CommonModule
]
})
export class TekButtonModule {}
Here are some of the files that are generated when I build the project:
tek-angular-uimodules.d.ts:
/**
* Generated bundle index. Do not edit.
*/
export * from './public-api';
public-api.ts
export * from "./lib/button";
export * from "./lib/button-group";
export * from "./lib/nav-bar";
./lib/button/button-module.d.ts
export declare class TekButtonModule {
}
If I import the generated javascript module file from the esm5 directory manually in my app.module.ts file, then it works just fine. But I have to manually do that, when it should just work with the standard module import that WebStorm auto imports for me like any other package.
This is the generated module js file under the esm5 directory:
/**
* #fileoverview added by tsickle
* #suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
import { CommonModule } from "#angular/common";
import { NgModule } from "#angular/core";
import { TekButton } from "./button";
var TekButtonModule = /** #class */ (function () {
function TekButtonModule() {
}
TekButtonModule.decorators = [
{ type: NgModule, args: [{
declarations: [
TekButton
],
exports: [
TekButton
],
imports: [
CommonModule
]
},] }
];
return TekButtonModule;
}());
export { TekButtonModule };
//# sourceMappingURL=data:application/json;base64
Any help is appreciated. I can provide more code, and screenshots if needed.
This is the error I get when trying to import my module:
So I figured out what was causing my specific issue. It was two things. Point two is the direct reason for my issue, point one was making much more confusing to debug.
In order to test my built package, I would run ng build, and then cd into the built project in the dist directory, and run npm pack. I would then install my built package as a file dependency in an entirely separate project to ensure I did everything correctly. What I found out was (or I'm assuming) that there was a caching mechanism going on even when doing local file dependency installations. This caching seemed to be tied to the file name of the tgz file that is generated from npm pack. This caching was causing the very first version to be constantly reinstalled not matter how many changes I made to the built library. The fix, for me, was to simply rename the tgz file to something different each time. You should also change the library version number, and that should also work.
My file structure looked like this:
src
- public-api.ts
- lib
-- button
--- index.ts
--- public-api.ts
--- button.component.ts
--- button.module.ts
-- another-module
-- yet-another-module
The public-api.ts file directly in src looked like this:
export * from './lib/button'
// Other exports
The public-api.ts, and the index.ts file under src\lib\button looked like this:
// public-api.ts
export * from './button.component.ts'
export * from './button.module.ts'
// index.ts
export * from './public-api.ts'
I had assumed that by adding the index.ts files to each directory, the export line in the root public-api.ts would find the index file, and export the files accordingly, but somewhere along the line, this implicit resolution does not work, and, instead, an explicit export is needed. The fix was to change export * from './lib/button' to export * from './lib/button/index.ts' or export * from './lib/button/public-api.ts'.
Point two was the direct reason for my issue, point one just made it really confusing when trying to debug.
So I had the same issue with a different solution, so I thought I would share it here.
I had just updated my library to Angular 9. And everything seemed fine except the strange "Class my-module is not an Angular module" error.
What worked for me was to up tsconfig.lib.ts file with:
...
"angularCompilerOptions": {
...,
"enableIvy": false
}
Don't know if this will help anyone, but for me it was caused by the NgModule import path autocompleting with #angular/core/src/metadata/*, rather than #angular/core.

SystemJS error when importing modules into AngularJS - Anonymous System.register calls can only be made by modules loaded by SystemJS.import

I'm upgrading an AngularJS app to Angular, using the standard ngUpgrade module. I've converted a service to Angular, and I need to downgrade it so I can use it in an existing AngularJS module. I'm trying to import 'downgradeInjectable' and the Angular service like this:
import { downgradeInjectable } from '#angular/upgrade/static';
import { MyService } from './app/my-service';
(function () {
var myCtrl = ['myService', function (myService) {
this.myService = myService;
...
}];
... stateConfig here
angular
.module('myModule')
.config(['$stateProvider', '$urlRouterProvider', stateConfig])
.controller('myController', myCtrl)
.factory('myService', downgradeInjectable(MyService))
})();
The imports are recognised as valid by the TypeScript compiler, and it all compiles fine, however I now get this error in the console:
Invalid System.register call. Anonymous System.register calls can only be made by modules loaded by SystemJS.import and not via script tags.
In the transpiled code, the import is handled like this by SystemJS:
System.register(["#angular/upgrade/static", "./app/my-service"], function (exports_1, context_1) {
"use strict";
...
My other AngularJS code isn't using any imports, this is the first time I've introduced an 'import' in the existing AngularJS application. What do I need to do to import these modules successfully? I've reviewed similar errors however there's nothing that helps me or that matches this particular situation.
Update:
I'm loading the application by importing a main module using SystemJS in my main html file like this:
<script>
System.import('/assets/js/app/main.js').catch(function (err) { console.error(err); });
</script>
The original TypeScript for the main.js file bootstraps the root Angular 4 module:
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
... and the root module is like this:
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { UpgradeModule } from '#angular/upgrade/static';
import { AppComponent } from './app.component';
... other imports
#NgModule({
imports: [BrowserModule, UpgradeModule ],
declarations: [AppComponent],
entryComponents: [AppComponent],
providers: [ WindowRef, MyService]
})
export class AppModule {
constructor(private upgrade: UpgradeModule) {
}
ngDoBootstrap() {
var ngApp = <HTMLInputElement>document.getElementById('ngApp');
this.upgrade.bootstrap(document.documentElement, [ngApp.value]);
}
}
... which then bootstraps the AngularJS app, within the context of Angular.
My tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": false,
"suppressImplicitAnyIndexErrors": true,
"noStrictGenericChecks": true
},
"include": [ "**/*.ts" ],
"exclude": [
"node_modules"
]
}
Further update:
Following the advice from #Aluan Haddad I'm now attempting to no import the root AngularJS declarations like this:
main.ts
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
import { AppModule } from './app.module';
**import root AngularJS declarations**
import '../app.js';
platformBrowserDynamic().bootstrapModule(AppModule);
The original app.ts for the AngularJS declarations in app.js looks like this:
declare var angular: any;
(function () {
angular
.module('shared', ['shared.primaryNavigation', ...otherDependencies]);
angular
.module('shared.ui.router', ['ui.router']);
angular
.module('ngApp1', ['dependency']);
angular
.module('ngApp2', ['dependency']);
There's nothing in here that's exporting anything though, so I wouldn't expect the attempted import to work, and honestly I now have no idea what's going on with the module loading as I only know about importing named classes and the AngularJS app doesn't use any exports! I now get these errors in the console:
[$injector:nomod] Module 'shared' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
So how should I be structuring this to successfully load the app? My script inclusions look like this:
<!-- Angular JS -->
<!-- Root Module Declarations -->
<!-- now commenting this out -->
<!--<script src="/assets/js/app.js"></script>-->
<!-- Shared Config -->
<script src="/assets/js/shared/shared.config.js"></script>
<!-- Shared Directives -->
<script src="/assets/js/shared/directives/myDirective1.js"></script>
<script src="/assets/js/shared/directives/myDirective2.js"></script>
<!-- Shared Components -->
<script src="/assets/js/shared/modules/myModule1.js"></script>
<script src="/assets/js/shared/modules/myModule2.js"></script>
<!-- Shared Services -->
<script src="/assets/js/shared/services/myService1.js"></script>
<script src="/assets/js/shared/services/myService2.js"></script>
You are receiving the error because, a top level import or export makes a file into a module.
That causes TypeScript to transpile it as such.
An anonymous System.register module must be loaded by a loader that understands the format, typically SystemJS.
Now that it is a module, import it like any other
import './my-module';
and remove script tags that load it.
This can be improved
import { downgradeInjectable } from '#angular/upgrade/static';
import { MyService } from './app/my-service';
class MyCtrl {
static $inject = ['myService'];
constructor(readonly myService) {...}
}
... stateConfig here
angular
.module('myModule')
.config(['$stateProvider', '$urlRouterProvider', stateConfig])
.controller('myController', MyCtrl)
.factory('myService', downgradeInjectable(MyService));
To explain the error in more detail, SystemJS also defines a named System.register module format. This is the output format used (by default) in the SystemJS Builder to emit bundles.
See the excellent answer from #Aluan Haddad which explains exactly why this error occurs. In my case I had to import my root Angular.js file via my main Angular 'main.ts' file so that everything including the module that's now been created will now load through SystemJS:
import { platformBrowserDynamic } from '#angular/platform-browser-dynamic';
// importing root AngularJS module here
import '../app.js';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

Referencing a file (module) in Typescript using AMD

I am new to Typescript and module handling. In my project I am coding in Typescript for developing a browser library. I am therefore using AMD. Following is the tsconfig.json file.
{
"compilerOptions": {
"target": "ES5",
"outDir": "out",
"module": "amd"
},
"files": [
"src/main.ts",
"src/communication.ts",
...
]
}
File communication.ts is:
export module MyProj.DataExchange {
export interface Communication {
connect(uri: string): void;
close(): void;
status: int;
}
}
Referencing components from other files
I want to use Communication in main.ts:
import communication = require('./communication');
export module MyProj {
export class Communicator
implements communication.MyProj.DataExchange.Communication {
...
}
}
I would like to avoid using the whole signature communication.MyProj.DataExchange.Communication. So I tried something like:
import communication = require('./communication').MyProj.DataExchange;
But it did not work.
My questions
I have a feeling I am doing something wrong in here. Here my questions:
Am I doing things the way they are supposed to be done?
How to avoid using the whole name for an imported component?
In case you might tell me not to use module, I need to separate my components into namespaces. So how to correctly set namespaces in case I am doing it wrong?
In Typescript 1.4 have been introduced Type Aliases.
Your code might be adapted to use aliases like this:
import communication = require('./communication')
type Communication = communication.MyProj.DataExchange.Communication;
export module MyProj {
export class Communicator
implements Communication {
...
}
}

Categories

Resources