I have a superclass which contains common functionality for components.
export class AbstractComponent implements OnInit {
public user: User;
constructor(public http: HttpClient) {
}
ngOnInit(): void {
this.http.get<User>('url').subscribe(user => {
this.user = user;
});
}
}
I have a subclass which implements this superclass.
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent extends AbstractComponent {
constructor(public http: HttpClient) {
super(http);
}
}
In the headers template I am trying to access the user
<mat-toolbar color="primary">
<span *ngIf="user">Welcome {{user.username}}!</span>
</mat-toolbar>
But the user field is not being resolved. How can I access a superclass's fields from a subclass?
You are getting an error because the user object is not available at load.
Either initalise it or use the safe navigation operator (?.) inside your template
initalise:
public user: User = new User();
safe navigation:
<span>Welcome {{user?.username}}!</span>
This approach works but it is not a good practice. In such cases it would be better to use async pipe:
export class AbstractComponent {
user$;
constructor() {
// your real http request should be here
this.user$ = Observable.of({name: 'John Doe'});
}
}
#Component({
selector: 'my-app',
template: `
<div>Hello {{(user$ | async).name}}</div>
`,
})
export class App extends AbstractComponent {
constructor() {
super();
}
}
Related
This is parent.html
<child *ngFor="let detail of listContact; let i = index"
[detailsItem]="detail" [index]="i">
</child>
This is child.compenent
export class ChildComponent implements OnInit {
#Input() index: number;
#Input() detailsItem: any;
constructor() {
}
ngOnInit() {
}
saveChild(){
console.log('index', this.index);
}
}
And I want to call the method saveChild in parentComponent like this:
export class ParentComponent implements OnInit {
private child: ChildComponent;
#Input() index: number;
#Input() detailsItem: any;
constructor() {
}
ngOnInit() {
}
save(){
this.child.saveChild();
}
}
you can use #ViewChild to run that.
import { AfterViewInit, Component, ViewChild } from '#angular/core';
import { ChildComponent } from '../child/child.component';
#Component({
selector: 'app-parent',
templateUrl: './parent.component.html',
styleUrls: ['./parent.component.css']
})
export class ParentComponent implements AfterViewInit {
#ViewChild(ChildComponent) public child!:ChildComponent
constructor() { }
ngAfterViewInit(): void {
this.child.childMethod();
}
}
It's going to let you access the properties and methods inside the ChildComponent. However remember to use ngAfterViewInit so that the child is initialised when you try and access it.
I tried to use instance of parent component in child component via constructor. In other words, I create instance of parent component class as private property and use its properties, methods etc.
Besides that, I can affect to values of parent component properties directly without using Input, Output decorators, event listeners etc.
Parent
#Component({
selector: 'parent-component',
templateUrl: './parent-component.component.html',
styleUrls: ['./parent-component.component.scss']
})
export class ParentComponent implements OnInit {
someParentProperty: number = 10;
constructor() {}
ngOnInit() {}
someParentMethod = (num) => num**2;
}
Child
import { ParentComponent } from '../parent-component';
#Component({
selector: 'child-component',
templateUrl: './child-component.component.html',
styleUrls: ['./child-component.component.scss']
})
export class ChildComponent implements OnInit {
someChildProperty: number;
constructor(pc: ParentComponent) {}
ngOnInit() {
this.someChildProperty = this.pc.someParentMethod(this.pc.someParentProperty);
}
}
That's rather comfortable, but I'm not sure, that it's a best practice and right approach.
Could someone explain minuses of this one?
Why don't you use a service?
So that you can access your required method from both child and parent components.
E.g: A common service:
#Injectable({
providedIn: 'root',
})
export class CommonService {
someParentMethod(num) {
return num**2;
}
}
At ParentComponent:
export class ParentComponent implements OnInit {
constructor(private commonService: CommonService) {}
ngOnInit() {
console.log(commonService.someParentMethod(2));
}
}
You can do the same at ChildComponent you can do the same.
It's the best way for sharing.
You can find detail about services here.
I am using Angular 4 and I was wondering how to pass a parameter value to a Service.
For example:
<button (onClick)="doSomething('myParameter')">Send this to Service</button>
Then the service would get it.
I currently have this:
import { Injectable } from '#angular/core';
#Injectable()
export class MessageService {
constructor() { }
message() {
return 'This data goes to the component';
}
}
and then get is like this:
export class AppComponent implements OnInit {
constructor(private messageService: MessageService) {}
ngOnInit() {
console.log(this.messageService.message);
}
}
but this only sends data to the component.
How do I do this?
Your template should talk to your component class and your component class should talk to your service.
I see you have a doSomething method in your template that is not defined in your component?
You need something like this:
Component 1
export class AppComponent implements OnInit {
constructor(private messageService: MessageService) {}
ngOnInit() {
}
doSomething(message: string): void {
this.messageService.message = message;
}
}
Service
import { Injectable } from '#angular/core';
#Injectable()
export class MessageService {
message: string;
constructor() { }
}
Component 2
export class AppComponent implements OnInit {
get message(): string {
return this.messageService.message;
}
constructor(private messageService: MessageService) {}
ngOnInit() {
}
}
Why is there a problem in binding a property on the same component? I already added Input() but still doesn't work. Do i need to put Input() even though it is on the same component when binding?
//output.component.ts
import { Component, OnInit} from '#angular/core';
import { DataService } from '../data.service';
#Component({
selector: 'app-output',
templateUrl: './output.component.html',
styleUrls: ['./output.component.css']
})
export class OutputComponent implements OnInit {
data: {name: string};
datas = [];
constructor(private dataService: DataService) { }
ngOnInit(){
this.datas = this.dataService.datas;
}
}
//output.component.html
<p *ngFor="let data of datas"></p>
<p>{{data.name}}</p>
//data.service.ts
export class DataService {
datas= [];
addData(name: string){
return this.datas.push({name: name});
}
}
For same component #input API is not required. It is used when you want to pass the data from Parentcomponent to a child component.
//output.component.html
<p *ngFor="let data of dataService.datas" > // removed [data]="data" and added dataService.datas
<p>{{data?.name}}</p>
</p> //changed the position of </p>
export class OutputComponent implements OnInit {
constructor(private dataService: DataService) {}
}
export class DataService {
datas= [];
addData(name: string){
return this.datas.push({name: name}); //return keyword was missing
}
}
Just for your reference
DEMO: https://plnkr.co/edit/XlJM2LHFwlAYpQe2ancM?p=preview
In Angular 4 to dynamically create a component you can use ngComponentOutlet directive: https://angular.io/docs/ts/latest/api/common/index/NgComponentOutlet-directive.html
something like this:
Dynamic component
#Component({
selector: 'dynamic-component',
template: `
Dynamic component
`
})
export class DynamicComponent {
#Input() info: any;
}
App
#Component({
selector: 'my-app',
template: `
App<br>
<ng-container *ngComponentOutlet="component"></ng-container>
`
})
export class AppComponent {
this.component=DynamicComponent;
}
How do I pass #Input() info: any; information in this template <ng-container *ngComponentOutlet="component"></ng-container> ?
Such a feature was discussed in the pull request for ngComponentOutlet but was dropped for now.
Even the componentRef shown currently in https://angular.io/docs/ts/latest/api/common/index/NgComponentOutlet-directive.html is not public and therefore not available https://github.com/angular/angular/blob/3ef73c2b1945340ca6bd21f1790260c88698ae26/modules/%40angular/common/src/directives/ng_component_outlet.ts#L78
I'd suggest you create your own directive derived from https://github.com/angular/angular/blob/3ef73c2b1945340ca6bd21f1790260c88698ae26/modules/%40angular/common/src/directives/ng_component_outlet.ts#L72
and assign values to inputs like shown in Angular 2 dynamic tabs with user-click chosen components
this.compRef.instance.someProperty = 'someValue';
With the help of the post of #Günter Zöchbauer I solved a similar problem this way - I hope you can adapt it somehow.
First I defined some interfaces:
// all dynamically loaded components should implement this guy
export interface IDynamicComponent { Context: object; }
// data from parent to dynLoadedComponent
export interface IDynamicComponentData {
component: any;
context?: object;
caller?: any;
}
then I implemented them inside of the dynamically loaded component
dynamicLoadedComponentA.ts
// ...
export class DynamicLoadedComponentA implements IDynamicComponent {
// ...
// data from parent
public Context: object;
// ...
After that I built a new component which is responsible for the magic. Important here is that I had to register all dyn. loaded components as entryComponents.
dynamic.component.ts
#Component({
selector: 'ngc-dynamic-component',
template: ´<ng-template #dynamicContainer></ng-template>´,
entryComponents: [ DynamicLoadedComponentA ]
})
export class DynamicComponent implements OnInit, OnDestroy, OnChanges {
#ViewChild('dynamicContainer', { read: ViewContainerRef }) public dynamicContainer: ViewContainerRef;
#Input() public componentData: IDynamicComponentData;
private componentRef: ComponentRef<any>;
private componentInstance: IDynamicComponent;
constructor(private resolver: ComponentFactoryResolver) { }
public ngOnInit() {
this.createComponent();
}
public ngOnChanges(changes: SimpleChanges) {
if (changes['componentData']) {
this.createComponent();
}
}
public ngOnDestroy() {
if (this.componentInstance) {
this.componentInstance = null;
}
if (this.componentRef) {
this.componentRef.destroy();
}
}
private createComponent() {
this.dynamicContainer.clear();
if (this.componentData && this.componentData.component) {
const factory: ComponentFactory<any> = this.resolver.resolveComponentFactory(this.componentData.component);
this.componentRef = this.dynamicContainer.createComponent(factory);
this.componentInstance = this.componentRef.instance as IDynamicComponent;
// fill context data
Object.assign(this.componentInstance.Context, this.componentData.context || {});
// register output events
// this.componentRef.instance.outputTrigger.subscribe(event => console.log(event));
}
}
}
here the usage of this shiny new stuff:
app.html
<!-- [...] -->
<div>
<ngc-dynamic-component [componentData]="_settingsData"></ngc-dynamic-component>
</div>
<!-- [...] -->
app.ts
// ...
private _settingsData: IDynamicComponent = {
component: DynamicLoadedComponentA,
context: { SomeValue: 42 },
caller: this
};
// ...
I think for now you can use
https://www.npmjs.com/package/ng-dynamic-component
It is made specifically for this issue