Configure feature module(s) sharing service - javascript

Three Modules as depicted...
|Elements| and |myForms| are imported into |App|.
Folder Structure
--app
|--Elements angular module (has ConfigService in it).
|--App angular module (angular imports Elements, and myForms (NPM PKG))
Elements module exports ConfigService using forRoot.
export class ElementsModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: ElementsModule,
providers: [ConfigService]
};
}
}
App module imports Elements and myForms modules.
import { Elements } from './local-dir';
import { myForms } from 'NPM PACKAGE';
#NgModule({
imports: [
Elements.forRoot(),
myForms
]
})
How would I give myForms access to the ConfigService in a component?
The forRoot method is working as I have access in the App module like so.
import { ElementsModule, ConfigService } form './local-dir';
I would like to give it to a myForms service that myForms already knows about. Or find a way in which I don't have to directly import the file into the myForms component I want to use it in.
If I go into the myForms NPM PACKAGE, I can do the following and it will work of course.
import { ConfigService } from './local-dir';
#Component({
selector: 'my-element',
providers: [ConfigService]
});
export class MyElementComponent {
constructor(private CS: ConfigService){}
}
Update - further clarification.
There is one app module.
The app module imports a myForms module. This module requires info from another module that will also be loaded.
The other module that will be loaded, is one of many that will be choses from. But in the end, only one will be chosen.
when the chosen module shows up in the app module, I then want to share something it has with the myForms module, inside of the app module. This is a one time share, the myForms modules does not require communication after this.
The chosen module hands over a key/value of { anyKey: chosenModuleClassNamesToken }.
Then in the myForms module, I can call the shared data to get class names from the chosen module like this: myFormsService[ chosenModuleDataKey ]. This would result in a value of a class from the chosen module, that I would then build in the view using ComponentFactoryResolver.

Create a barrel file index.ts
export * from '....' path to your module
export * from '....' path to your service
Assuming the below folder structure,
---modules
|---- elements
|---- configuration
Create a barrel file inside the elements folder naming it as index.ts
export * from './elements.module';
export * from '../configuration/configuration.service';
Use this file to import in a single line as
import { ElementsModule, ConfigService } from '../elements';

Related

How to change text in the component base on the project I runs?

I build auth lib in my nx monorepo.
The auth lib contains LoginComponent (smart) and LoginFormComponent (dumb).
And I load the lib as lazy by using loadChildren to load auth.module and inside this module I have a child route to the LoginComponent which have LoginFormComponent.
Inside the LoginFormComponent I have text: "Login to my awesome app".
<h1>Login to my awesome app</h1>
<form...>
The problem is when I want to add another angular application and use this auth components and functionality.
So when I use the auth from the new application, I'm getting the text: "Login to my awesome app".
For the existing app the text should be: "Login to my awesome app".
And for new app should be: "Login to ng app".
I have only one auth lib with one of login component.
So I looking for angular way to deal with such senario.
I was think about duplicate the component but it's not feel right. or to use i18n but not sure because it's for langs, or using forRoot and pass the providers with the text? using tokens with inject?
What the ways I can do to change the text? How to handle this issue? is it solution to angular for this senario?
I can understand your situation. Here is what you should do. I am assuming that you have created a #shared/auth lib under nx monorepo. The auth lib contains the LoginFormComponent.
You should exploit ForRoot and #Inject() to achieve the same.
auth.module.ts
#NgModule({
// all imports etc
})
export class AuthModule {
static forRoot(appName: string): ModuleWithProviders<AuthModule> {
return {
ngModule: AuthModule,
providers: [
{ provide: APP_NAME, useValue: appName }
],
};
}
}
To avoid any circular dependency while importing APP_NAME, create a file as
config.ts
export const APP_NAME = new InjectionToken<string>('APP_NAME_PARAM');
Lets go your LoginFormComponent
LoginFormComponent.component.ts
export class LoginFormComponent{
public appName: string;
constructor(#Inject(APP_NAME) clientAppName: string){
this.appName = clientAppName;
}
}
login.component.html
<h1>Login to {{appName}} app</h1>
Now, we are all set. Only thing left to do, is to use AuthModule in our app.module.ts
app.module.ts
#NgModule({
imports: [
AuthModule.forRoot("Super Awesome")
]
})
export class AppModule
This can help you reuse the AuthModule in other apps as well. You just need create a constant file for each app library and then move Super Awesome into that constant for better readability.
It may not be completely relevant but, you can check this article for more understanding of ForRoot and InjectionToken
I have extensively used it in my Nx monorepo projects. Its a standard way.

Issue related to adding component into another in Angular6

i have web application with Angular 6.
I have following structure:
src
-app
-auth - folder
-auth.component.html
-auth.component.scss
-auth.component.ts
-auth.module.ts
-car-details - folder
-binning-table - folder
-binning-table.component.html
-binning-table.component.ts
-binning-table.component.scss
-car-detail.component.html
-car-detail.component.ts
-car-detail.component.scss
-car-detail.module.ts
-import-binning - folder
-import-binning.component.html
-import-binning.component.scss
-import-binning.component.ts
-app.component.html
-app.component.scss
-app.component.ts
-app.module.ts
now car-detail module registerd into auth.module.ts and authModule is registered into app.module.
i want to load binning-table component into import-binning.html
what changes should make in order to load one component into another.
what kind of connection i need registerd in which module
Thanks
We don't register a module we import it where ever required, a module can be imported into multiple modules(e.g shared module).
But We can only declare a component in one module, if we need to use the same component in another module, we export it from the same module and import that module into the module where it is required
e.g if we have a component by name A declared in module name module1, and if we need to use the same component in some other module ie module 2, we do something like this.
#NgModule({
declarations: [
Acomponent
],
exports: [
Acomponent
]
})
export class Module1 {}
#NgModule({
imports: [Module1]
})
export class Module2
By using the above syntax we can use AComponent in module1 as well as module2,
generally, the components which are shared throughout the applications we generally put them in the shared module.
Instead of declaring 'BinningTableComponent' inside 'app.module.ts', you need to declare it inside 'car-details.module.ts'. You need to include this same component 'BinningTableComponent' as part of exports array.
So car-details.module=>auth.module=>app.module. You can access 'BinningTableComponent' inside app.component.ts.
Refer the stackblitz code https://stackblitz.com/edit/angular-ivnodq

How should I include model classes in an Angular module?

I've a few classes I want to be just a plain bean/DTO class, they aren't display #component classes, they're not #Pipe classes nor should they be #Directive (at least I don't think it should be!).
I want to be able to bundle them into a module (they will be used across other modules), but despite several incantations I keep getting errors like this:
When I startup my Angular app (ng serve) it compiles ok, but in the browser (Chrome) console I get an error....
Uncaught Error: Unexpected value 'Accreditation' declared by the module 'ServiceModule'. Please add a #Pipe/#Directive/#Component annotation.
How should I be bundling these classes into a Module for use by other Modules? I don't mind if they are in my service module or in another new 'beans' modules.
Given this module definition (service.module.ts):
import { NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import {MemberService} from "./member/member.service";
import { Accreditation } from "./accreditation";
import {Player} from "./player";
import {Club} from "./club";
import {Coach} from "./coach";
#NgModule({
imports: [CommonModule],
declarations: [Accreditation, Club, Player, Coach],
exports: [Accreditation, Club, Player, Coach],
providers: [MemberService]
})
export class ServiceModule { }
accreditation.ts:
export class Accreditation {
uuid: string;
name :string;
lastComplete: Date;
expires: Date;
inLast6Months: boolean;
type: String;
displayName: String;
hasCompleted: boolean;
}
You don't need to import neither declare your DTOs in app.module.ts. It's available for a direct import in your components, directives, etc..
Simply import it in whichever component/service/directive,pipes you need with other imports:
import { Accreditation } from "./accreditation";
If you wish to share your DTO among several modules, put it in a shared folder and access it with a relative path, i.e.
import { Accreditation } from "./shared/accreditation";

How do I declare a dependency on an ng-metadata module?

I have a project that is using ng-metadata (https://github.com/ngParty/ng-metadata) to build a handful of Angular 1.5 modules. I have a test module/component that looks like this:
import { NgModule, Component, Inject, Input, Output, EventEmitter } from 'ng-metadata/core'
import { platformBrowserDynamic } from 'ng-metadata/platform-browser-dynamic'
#Component({
selector: 'test',
template: require('./test.template.html')
})
class TestComponent {
#Input() type: string;
constructor() {
console.log(`test: ${this.type}`)
}
}
#NgModule({
declarations: [TestComponent]
})
class HeroModule {}
platformBrowserDynamic().bootstrapModule(HeroModule)
Everything seems happy when compiled and I'm now attempting to use the module in another project (that is not using ng-metadata but has a compatible version of Angular).
I'm simply including the shims as directed by the ng-metadata docs and the JavaScript file that contains the module described above (built by webpack). I have a new module in this project that wants to list the HeroModule as a dependency. I've tried a few things:
// attempt 1:
angular.module('my-consuming-module', ['ui.router', 'hero'])
// attempt 2:
angular.module('my-consuming-module', ['ui.router', 'heroModule'])
// attempt 3:
angular.module('my-consuming-module', ['ui.router', 'hero-module'])
All always end up with the same Error: $injector:nomod Module Unavailable error from Angular.
If I'm using ng-metadata to build my modules, what are the names I use to list them as dependencies in another project?
Finally figured this out! It's amazing what happens when you carefully read documentation...
Found in the Manual Angular 1 Bootstrap section of ng-metadata's docs:
You can still leverage ng2 way of components registration without ng-metadata bootstrap, but you have to manually create your Angular 1 module from an ng-metadata #NgModule using the bundle helper function.
I ended up being able to do the following:
// REMOVED platformBrowserDynamic().bootstrapModule(HeroModule)
const Ng1AdminModule = bundle(HeroModule).name;
export const AppModule = angular.module('hero', [Ng1AdminModule]);
And then my hero module becomes accessible to the my-consuming-module just as I expected. The bundle helper function was the key to figuring this out.
You need to import those module from their respective locations and inject it inside your angular module
//ensure `angular`, `angular-ui-router` should be there in `map` of systemjs.config.js
import * as angular from 'angular';
import * as uiRouter from 'angular-ui-router';
import { heroModule} from './hero.module';
angular.module('app',[ uiRouter, heroModule]);
Check references here

TypeScript: How to import a class from a submodule?

I have a TypeScript app using both .ts and .d.ts files. References are made using the triple slash notation /// <reference path=...>. My app has the following definition file:
declare module APP {
class Bootstrap {
constructor();
}
}
I then declare a module named "app" so I can import it in other files:
declare module "app" {
export = APP;
}
Assuming my Bootstrap class exists, I then import the Bootstrap class using:
import { Bootstrap } from 'app';
This works.
Now I create a submodule on APP, like so:
declare module APP.Service {
class Async {
constructor();
}
}
I make another declaration for the submodule:
declare module "app/service" {
export = APP.Service;
}
Now, when I import the class Async like so:
import { Async } from 'app/service';
I get the following error message:
Module '"app/service"' resolves to a non-module entity and cannot be imported using this construct.``
How do I be import a class from a submodule?
NOTE
I have found a workaround by declaring a global var:
declare var APP_SERVICE: APP.Service.IService; // IService exists
And exporting that on my module:
declare module "app/service" {
export = APP_SERVICE;
}
The downside of this is that my global namespace get's polluted with var's I don't use, because I will use Service through App.Service, not APP_SERVICE.
If you care about creating a nice, reusable, maintainable declaration file for a module, the preferred way is by using typings, as it handles aspects such as type dependency management and submodules very well for you.
Create a separate repository for the typings your are writing.
Add typings.json file with the name and path to your main typings. eg:
{
"name": "my-main-module",
"main": "index.d.ts"
}
Add your the type declarations for your main module to index.d.ts and the type declarations for submodules to submodule-name.d.ts.
submodule.d.ts
export interface submoduleInterface {
someProp: number
}
index.d.ts
import * as submodule from './submodule'
export interface mainInterface {
someProp: number
}
Run typings bundle index.d.ts -o bundle.d.ts. This will bundle all your typings into one type declaration file, declaring the proper submodules and respecting all the necessary dependencies between modules, submodules and even external modules.
Either copy this file to a custom-typings directory in your original project, or submit this repo to the typings registry so other people can also profit from it 🙂, and pull it in with a normal typings i my-module-name.
Here is the gist with all the code, and the resulting bundle.d.ts. Here is a repo that uses both submodules (redux-persist/constants) as external dependencies (redux), and that got ultimately submitted to the typings registry.

Categories