Is it possible to use JSON Schema faker as a third party dependency in Angular. I tried to use the dependency injection to Angular however in the providers I am not able to import jsonSchemaFaker.
angular.json
"scripts": [
"./node_modules/json-schema-faker/dist/json-schema-faker.bundle.min.js"
]
jsonSchemaFaker.service.ts
import { InjectionToken } from '#angular/core';
export const JSF_Token = new InjectionToken ('jsonSchemaFaker');
app.module.ts
providers: [
{ provide: JSF_Token, useValue: jsf }
]
...
declare let jsf: any;
This is what I tried to Inject json schema faker as a dependency in my angular app.. I am getting .. Uncaught ReferenceError: jsf is not defined
That is not how you use npm packages in an angular application.
First off, navigate to the directory of your package.json in your application and install the package:
npm install json-schema-faker
Then, inside your components or your services, use it as such:
// at the top of your file, next to other imports
import jsf from 'json-schema-faker';
// json-schema-faker is now available in the rest of your file as jsf
// you can, for example, have a service method that returns that:
jsf.generate({type: 'string'})
I had to change my providers and the declaration to
providers: [
{ provide: JSF_Token, useValue: JSONSchemaFaker }
]
declare let JSONSchemaFaker: any;
Reason: the global name for JSON Schema Faker mentioned in that library is "JSONSchemaFaker". It was a mistake on my part to declare it as jsf.
Related
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.
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.
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
What is the recommended best practice for loading environment specific configuration during runtime of an Angular application? The Angular documentation mentions the use of APP_INITIALIZER, but that is still not early enough in the load process for things such as runtime configuration of imported modules that make use of the .forRoot() convention.
In my use case, I have an authentication service built and imported via a Core module, which is imported by the App module. The authentication library I am using (the angular-oauth2-oidc library) allows for configuration of the automatic appending of access tokens during when importing the module (see this segment). Since there are constraints in the build environment I am working with that only allows me to produce one common build package to deploy to all environments, I am unable to dynamically set values by using different environment.ts files.
One initial idea is to use the fetch API on the index.html page to load a JSON file containing the configuration onto a global variable, but since the call is asynchronous, there is a chance the configuration will not be fully loaded when the import of the Core module occurs.
This was part of my config setup to bring my app through the build pipeline and took me days. I ended up in a solution using the APP_INITIALIZER loading a REST service and build a AppConfigService for my App. I am using the same angular-oauth2-oidc library.
My Solution for this issue was not to setup the OAuthModule in its forRoot() method. It is called before any configs via APP_INITIALIZER are available - this results in undefined values when applied to the config object given to the forRoot() Method.
But we need a token in the http header. So I used a http interceptor for the attaching of the token like described here. The trick is to setup the OAuthModuleConfig in the factory. Obviously this is called after the app is initialized.
Configure Module
#NgModule({
imports: [
// no config here
OAuthModule.forRoot(),
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useFactory: authenticationInterceptorFactory,
deps: [OAuthStorage, AuthenticationErrorHandler, OAuthModuleConfig],
multi: true
}
]
})
Factory for interceptor
const authenticationInterceptorFactory = (oAuthService: OAuthStorage, authenticationErrorHandler: AuthenticationErrorHandler, oAuthModuleConfig: OAuthModuleConfig) => {
const config = {
resourceServer: {
allowedUrls: [
// Include config settings here instead
AppConfigService.settings.apiURL,
AppConfigService.settings.anotherApiURL,
]
sendAccessToken: true
},
}
return new AuthenticationInterceptor(oAuthService, authenticationErrorHandler, config);
};
I have created a library angular-runtime-config for runtime configuration loading for Angular.
Simple usage example
Your custom Configuration class:
export class Configuration {
readonly apiUrl!: string; // only example
readonly apiKey?: string; // only example
// add some other configuration parameters
}
Registering angular-runtime-config module with declaring which configuration files to load. For example, you can determine it by application URL or you can even use Angular injector in the factory or make the factory asynchronous.
import { AngularRuntimeConfigModule } from 'angular-runtime-config';
#NgModule({
...
imports: [
...
AngularRuntimeConfigModule.forRoot(Configuration, {
urlFactory: () => [ 'config/config.common.json', 'config/config.DEV.json' ]
})
],
}
Then request your Configuration class in any injection context.
#Injectable({...})
export class SomeService {
constructor(private readonly config: Configuration) {}
}
I am trying to create npm library with one directive and test it locally with the help of npm link.
But the problem is when I am including my directive in the declarations array I am getting this error:
Unexpected value 'MyDirective' declared by the module 'AppModule'
My library structure:
package.json
{
"name": "my-directive",
"main": "./dist/index.js",
"typings": "dist/index.d.ts"
}
My src folder:
index.ts
export * from "./myDirective";
myDirective.ts
import {Directive} from '#angular/core';
#Directive({
selector: "my-directive"
})
export class MyDirective {
constructor() {
console.log('directive works!');
}
}
Now in my Angular 2 app that I'm linking this npm package:
import { MyDirective } from "my-directive";
// This line indeed log the constructor function...
console.log(MyDirective);
// function MyDirective() {
// console.log('directive works!');
// }
#NgModule({
declarations: [
AppComponent,
MyDirective
]
})
What am I missing?
I was facing this issue and I don't know exactly why, but in the package.json file, remove the Angular 2 dependencies from the dependencies field to devDependencies solve this problem and my npm module worked.
You should give it a try.
There is a discussion and a PR regarding similar issues here:
https://github.com/angular/angular-cli/pull/2291
See if this might help.