How are do you handle dependencies for nested components in Angular2? - javascript

I'm running into this error:
browser_adapter.js:76 Error: Cannot resolve all parameters for NestedComponent(undefined). Make sure they all have valid type or annotations.
at NoAnnotationError.BaseException [as constructor]
Here's the breakdown:
I have a service
#Injectable()
export class MyService {
doSomething() {
console.log("This is a service, and it's doing stuff");
}
}
That can be injected into components, like this, without issue:
#Component({
selector: 'parent-component',
template: '<p>This works great!</p>',
providers: [MyService]
})
export class ParentComponent {
constructor(private _myService: MyService) {}
ngOnInit() {
this._myService.doSomething();
}
}
I have problems, however, when I try to inject the service into nested components, like this:
#Component({
selector: 'nested-component',
template: "<p>This doesn't work. :(</p>",
providers: [MyService]
})
export class NestedComponent {
constructor(private _myService: MyService) {}
ngOnInit() {
this._myService.doSomething();
}
}
When I try to plug the nested component into the parent component, I get the error up there ^. How can I achieve this.
#Component({
selector: 'parent-component',
template: '<nested-component></nested-component>',
directives: [NestedComponent]
})
export class ParentComponent {
}
For what it's worth, I still run into that error, even when I include MyService in the bootstrap function of my app:
function main() {
return bootstrap(App, [
// These are dependencies of our App
HTTP_PROVIDERS,
ROUTER_PROVIDERS,
MyService,
ELEMENT_PROBE_PROVIDERS // remove in production
])
.catch(err => console.error(err));
}
document.addEventListener('DOMContentLoaded', main);

If you want to share single instance of service,
dont use,
providers: [MyService]
in each component. Look into this example which doesn't use providers:[...] ,shared instance, not providers used, registered into bootstrap
And if you don't want to share,
remove ,
providers: [MyService]
from nested-component.
Look into this example which uses providers:[...],not shared instance, not registered into bootstrap, used with providers in parent only and not in child,

Related

Angular programmatic navigation - Why router does not in providers array

In Angular Dependency Injector when ever we injecting a Type , we will includes them in providers array (May be in #NgModule decorator or #Component decorator. But in the following instance when we navigate programmatically we inject Router instance to constructor, But we don't provide in providers array as normally we do in Angular Dependency Injector.
What is the different here than Angular Dependency Injector ? for your reference I will attach both code
Programmatic Navigation - Code
import { Router } from '#angular/router';
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor(private router:Router) { }
ngOnInit() {
}
onLoadServer(){
this.router.navigate(['servers']);
}
}
Angular Dependency Injector way - Code
import { Component,Input} from '#angular/core';
import { LoggingService } from '../logging.service';
#Component({
selector: 'app-account',
templateUrl: './account.component.html',
styleUrls: ['./account.component.css'],
providers:[LoggingService]
})
export class AccountComponent {
#Input() account: {name: string, status: string};
#Input() id: number;
constructor(private logginService:LoggingService){
}
onSetTo(status: string) {
this.logginService.loggingStatusChange(status);
}
}
RouterModule.forRoot already injects Router in root injector, thats why you don't have to include it in providers array any where. you can read about it here :-
https://angular.io/api/router/RouterModule#forroot

Problem calling one Angular component from another component

At work, I have run into a problem using Angular. I have this kind of Angular component:
#Component({
selector: 'foo',
templateUrl: 'foo.html'
})
export class FooComponent {
#Input() data: string;
content: string;
ngOnInit() {
this.content = this.data;
}
setValue(data) {
this.content = data;
}
}
This is initialized from my main Angular component in a code block such as this:
this.components = [FooComponent, BarComponent, BazComponent, QuuxComponent];
Now this works so far. But if I try to call the setValue() function with this.components[0].setValue("Hello world!"); I get an error "this.components[0].setValue is not a function."
What is the reason for this and how can I fix it?
This seems like a very very weird way to work with components in angular.
You really don't want to break encapsulation by calling methods inside one component from another component.
I personally haven't seen this kind of component referencing anywhere (and have doubts it is a correct approach).
There is no reason to duplicate the data property in the content.
You can pass values in the template. Or use a service if you don't have direct access to the template.
Here is a very basic example on how to modify data from the parent using a template and #Input.
app.component.ts
import { Component } from "#angular/core";
#Component({
selector: "my-app",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
message = "I am a message from the parent";
}
app.component.html
<app-child [content]='message'></app-child>
child.component.ts
import { Component, OnInit, Input } from "#angular/core";
#Component({
selector: "app-child",
templateUrl: "./child.component.html",
styleUrls: ["./child.component.css"]
})
export class ChildComponent implements OnInit {
#Input("content") public content: string;
constructor() {}
ngOnInit() {}
}
child.component.html
<p>{{content}}</p>

How to use Angular Decorator to reduce repetitive code?

I have a I18nService that needs to be initialized in 90% of my components. The procedure to do this is importing the service, importing the translation file, implementing ngOnInit() and calling the services init() function.
Now I am trying to reduce the repetitive code with the help of Class Decorators.
The problem I'm currently facing is using my I18nService inside the decorator since Decorators seem to run at compile time. I was trying to solve the problem with injectors and following this article: https://netbasal.com/inspiration-for-custom-decorators-in-angular-95aeb87f072c
but got AppModule undefined.
How can I solve the problem? Are Decorators the right choice to achieve this to begin with?
You can store Injector in constructor AppModule and then use it inside patched ngOnInit method to get some Service registered in your app
app.module.ts
#NgModule({
...
providers: [AnalyticsService],
})
export class AppModule {
constructor(private injector: Injector) {
AppModule.injector = injector;
}
static injector: Injector;
}
page-track.decorator.ts
import { AnalyticsService, AppModule } from './app.module';
export function PageTrack(): ClassDecorator {
return function ( constructor : any ) {
const ngOnInit = constructor.prototype.ngOnInit;
constructor.prototype.ngOnInit = function ( ...args ) {
let service = AppModule.injector.get(AnalyticsService);
service.visit();
ngOnInit && ngOnInit.apply(this, args);
};
}
}
app.component.ts
#Component({
selector: 'my-app',
templateUrl: `./app.component.html`
})
#PageTrack()
export class AppComponent {}
Plunker Example

Create a Dynamic Component in Angular 2

Hey I am new to angular 2, I have played with angular 2 for the past week and wondered if it possible to generate a dynamic component with a dynamic template and dynamic styles. This means that the follwoing should be like this
#Component({
// 2a
selector: 'dynamic placeholder',
// 2b
styles: [`dynamic styles`]
// 2c
template: `dynmic template`
})
is it possible to do it in angular 2, I remember that such this is maybe possible in angular 1.
Any Help will be appreciated(Escpecially plunkers with simple code)
This is what I have achieved so far: try using ComponentFactoryResolver:
#NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {
}
#Component({
selector: 'my-app',
template: `
<div>Hello, world!</div>
`
})
export class AppComponent {
}
#NgModule({
declarations: [HomeComponent],
exports: [HomeComponent]
})
export class HomeModule {
}
#Component({
selector: 'home',
template: `
<div>This is home</div>
`
})
export class HomeComponent {
}
#Component({
selector: 'hello-world',
template: `
<div>
Hello, world!, {{name}}
The answer is: {{getAnswer()}}
</div>
`
})
export class HelloWorldComponent implements AfterViewInit {
private name:string = 'You';
constructor(private helloWorldService: HelloWorldService) {
}
ngAfterViewInit(): void {
this.name = 'Me';
}
private getAnswer() {
return this.helloWorldService.giveMeTheAnswer();
}
}
#NgModule({
declarations: [HomeComponent, HelloWorldComponent],
providers: [HelloWorldService],
exports: [HomeComponent]
})
export class HomeModule {
}
#Component({
selector: 'home',
template: `
<button (click)="sayHello()">Say hello</button>
<div>This is home</div>
`
})
export class HomeComponent {
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef) {
}
private sayHello() {
const factory = this.componentFactoryResolver.resolveComponentFactory(HelloWorldComponent);
const ref = this.viewContainerRef.createComponent(factory);
ref.changeDetectorRef.detectChanges();
}
}
Here is a plunker which enables to created dynamic component, I don't know if creating dynamic css is possible,I would be pleased if some can I answer my question:
http://plnkr.co/edit/ZXsIWykqKZi5r75VMtw2?p=preview
With TypeScript and latest version of Angular2 (I believe that feature has been released in 2.4.0) you can create 1 base component and then extend it. All decorators/annotations on properties (#Input/#Output/#ViewChild) will be copied. However, you must specify for each ancestor #Component properties, i.e. you cannot overwrite only selector, but everything. That is RIGHT approach.
Another approach -> use reflect-metadata to update decorators on Component classes (probably that is what you are looking for, as it that case you can overwrite 1 property at time), but be careful to export (i.e. make public) all Components/Directives/Services that are used inside of Component that you want to overwrite. For example, some libraries have multiple Components, and some of them are supposed to be used only internally (i.e. there is no way to import it into your module in normal way... however, you can try providers). If you try to "overwrite", say, css with reflect-metadata and it uses internal components -> angular/typescript will crash, as it cannot resolve "internal" stuff. You can start with this answer: StackOverflow

Load dynamic component created on the fly in angular2 final

Previously with DynamicComponentLoader I was able to write like this:
import {Directive, Component, ViewContainerRef, DynamicComponentLoader} from '#angular/core';
#Directive({
selector: '[some-directive]'
})
export class SomeDirective {
costructor(dcl: DynamicComponentLoader, viewContainerRef: ViewContainerRef) {
// fetch template from the server
fetch(...).then((template) => {
#Component({
selector: 'div[some-relatively-unique-attribute-name]',
template: template
})
class AdHocComponent {}
dcl.loadNextToLocation(AdHocComponent, viewContainerRef).then(() => {
console.log('success');
});
});
}
}
Now with angular2 final and NgModules I see examples like this: http://plnkr.co/edit/P0spNzu8JbQad2aKACsX?p=info
(Discussed here https://github.com/angular/angular/issues/10735)
To dynamically load a HelloComponent but it requires the HelloComponent to be declared up front when the root NgModule is being created.
How can I load an ad-hoc created component into my view?
I found this: http://plnkr.co/edit/wh4VJG?p=preview
But it is an insane amount of code to achieve a simple task like that.
This might be that what you're looking for:
export class App {
#ViewChild('placeholder', {read: ViewContainerRef}) viewContainerRef;
constructor(private compiler: Compiler) {}
addItem () {
#NgModule({declarations: [HelloComponent]})
class DynamicModule {}
this.compiler.compileModuleAndAllComponentsAsync(DynamicModule)
.then(({moduleFactory, componentFactories}) => {
const compFactory = componentFactories
.find(x => x.componentType === HelloComponent);
const cmpRef = this.viewContainerRef.createComponent(compFactory, 0);
});
}
}
See also live Plunker
Related question:
Angular2 RC6 - Dynamically load component from module

Categories

Resources