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
Related
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.
I'm using angular. I already know that when an appmodule is importing modules which declares providers, the root injector gets them all and the service is visible to the app - globally. (I'm not talking about lazy loaded modules)
But is it possible that each module will have its own instance of the service?
I thought of maybe something like this :
#NgModule({
providers: [AService]
})
class A {
forRoot() {
return {
ngModule: A,
providers: [AService]
}
}
forChild() {
return {
ngModule: A,
providers: [AService]
}
}
}
But I don't know if it's the right way of doing it
Question
How can I accomplish service per module ?
STACKBLITZ : from my testing , they are using the same service instance
When we provide a service in a feature module that is eagerly loaded
by our app's root module, it is available for everyone to inject. - John Papa
So looks like there is no way to inject at feature-module level, as there are no module level injectors other than the one at root module.
But angular has nodes at each component level for the injector, so such a scenario will have to use coponent level-injectors I guess.
You can also have a parent component inject the service for different children sharing the same instance.
One way is to provide the services at component level. Not sure if that will work for you.
Also, check the multiple edit scenario in the docs
https://angular-iayenb.stackblitz.io
import { Component, OnInit } from '#angular/core';
import {CounterService} from "../../counter.service"
#Component({
selector: 'c2',
templateUrl: './c2.component.html',
styleUrls: ['./c2.component.css'],
providers:[CounterService]
})
export class C2Component implements OnInit {
constructor(private s:CounterService) { }
ngOnInit() {
}
}
Question
How can I accomplish service per module ?
Not with the default Injector. Default Injector keeps nodes at root level and component level, not at feature-module level. You will have to have a custom Injector if there is a real scenario.
Edit: the previous answer from me is not completely correct. The correct way to make this work is to use lazy loaded modules, provide the services there. The given service should not be provided with a static forRoot() method somewhere because then the lazy loaded module will access the root injector.
There is no actual reference to do so because that is not how angular is designed but if you want it that way you have to the opposite of
https://angular.io/guide/singleton-services#providing-a-singleton-service
and
https://angular.io/guide/singleton-services#forroot
Old not completely correct:
You simple have to declare the service you want to be single instance for each and every module within the providers meta data of each and every module. Then Angular will not reuse any instance of the service.
Giving scenario: You have two modules, ModuleA and ModuleB, both need the service but different instance, then you will declare them in the providers section of ModuleA and ModuleB.
Reference:
https://angular.io/guide/singleton-services
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';
In the below code from ../src/app/app.module.ts,
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
Component consists:
view(../src/app/app.component.html)
logic(../src/app/app.component.ts)
style(../src/app/app.component.css)
Angular application is a tree of components. Good components have high cohesion, i.e. each component contains only elements with related functionality. They are also well encapsulated and loosely coupled.
How modules are different from components?
A component is just a class with the #Component() annotation. Note that .html and .css files might be referenced by the component, certainly not mandatory. The component template might very well be 'inlined' directly in the component configuration, or there simply might not be any html template at all for a given component.
A module is a structural element of an Angular application (and maybe other classes and interfaces). It is also "just a class" with the #NgModule() annotation.
It acts as a logical 'container' for your components, directives, services, pipes, etc... to help you structure your overall source code better.
You can have a look at this existing question : What's the difference between an Angular component and module
A module is something that has components. It wraps them up so you can import and manage them.
Notice when you make a component you can put anything that's decorated as #Injectable in your constructor:
#Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
constructor(private myService: MyService) { }
ngOnInit() {
}
}
And magically you will have a myService to use. This is dependency injection, which is built into Angular - but it's managed on a Module level. In your module you import what other modules you want to be able to use:
imports: [
BrowserModule,
FormsModule
],
define what your module includes:
declarations: [
AppComponent,
HeroesComponent,
MyService
],
export any components (so other modules can import them)
exports: [
HeroesComponent
],
They help organize an application into blocks of functionality. Components are things that tell angular how to render something. Modules compose Components, Pipes, Services etc into 'blocks' that can be compiled by angular or imported and used by others.
Edit to address comment
Taking your specific question about HttpClient. The HttpClient is the service you are using to perform the actions. The HttpClientModule is the module you import into your module, so you can use the service it contains.
You import the module:
#NgModule({
imports: [
BrowserModule,
// Include it under 'imports' in your application module
// after BrowserModule.
HttpClientModule,
],
})
And use the service:
#Component(...)
export class MyComponent implements OnInit {
// Inject HttpClient into your component or service.
constructor(private http: HttpClient) {}
...
}
The HttpClientModule contains within it all you need for the HttpClient to work, and packages it up so you can use it in your own projects.
This particular module only wraps up that one service, but the module could contain a bunch of related services, components, pipes or directives. For example, the RouterModule allows you to use the RouterOutlet and RouterLink directives.
Module in angular is set of Components, Services, Filters, or some another smaller modules too, or we can say where you import all these in order to use later in the app for future use. in a single app there can be one or more than one module may exist.
Whereas, A component controls a patch of screen called a view.
You define a component's application logic—what it does to support the view—inside a class. The class interacts with the view through an API of properties and methods.
Refer this guide for more details:
https://angular.io/guide/architecture
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