Access Superclass Fields from Angular 5 Component - javascript

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

How can i call the same child method when i have the list of same child component in parent component in angular

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.

Does this approach have a chance, when I use instance of parent component as property of child component?

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.

Angular 4 - (onclick) pass parameter to a service

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() {
}
}

Can't Bind Property in Angular 4

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

angular 4+ assign #Input for ngComponentOutlet dynamically created component

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

Categories

Resources