I often have the case that I want a component which renders to a DOM element and which needs some data for that. This data comes from objects I store in the parent component like that:
#Component({
template: '<child *ngFor="let child of children" [child]="child"/>'
})
export class ParentComponent {
children: Array<Child>;
constructor () {
this.children = [
new Child('Foo'),
new Child('Bar'),
]
}
}
class Child {
name: string;
constructor (name) {
this.name = name;
}
}
#Component({
selector: 'child',
template: 'My name is {{child.name}}'
})
export class ChildComponent {
#Input child: Child;
}
Here, Child and ChildComponent represent the same entity. But despite that I have to use two classes and two instances for each entity instance and I have to pass one object to the 'wrapping' component class. That seems unnecessarily complicated to me.
I would find it much easier if I could somehow 'merge' the classes Child and ChildComponent like that (imaginary syntax):
#Component({
template: '<child *ngFor="let this of children"/>'
})
export class ParentComponent {
children: Array<MergedChildComponent>;
constructor () {
this.children = [
new MergedChildComponent('Foo'),
new MergedChildComponent('Bar'),
]
}
}
#Component({
selector: 'child',
template: 'My name is {{name}}'
})
export class MergedChildComponent {
name: string;
constructor (name: string) {
this.name = name;
}
}
Is there a way to achieve that?
Related
I need to pass one variable, that is inside my child component, to parent page.
This variable that I am trying to pass, is the array result of Barcode Scanner.
And I need to pass it to parent to send to API.
childComponent.ts
this.consultList;
parentComponent.ts
export class ParentComponent implements OnInit {
#Input() consultList: any[] = [];
testCall() {
console.log('Test Consult: ', this.consultList;
}
Here is an example stackblitz project to test parent-child data transfer, using #Input() and #Output()mechanism
import { Component, EventEmitter, Input, Output } from '#angular/core';
#Component({
selector: 'child',
template: `
<h1>Hello {{ name }}! This is child component</h1>
<button (click)="sendEventToParent()">Send data to parent</button>
`,
styles: [
`
h1 {
font-family: Lato;
}
`
]
})
export class ChildComponent {
#Input() name: string;
#Output() eventFromChild: EventEmitter<string> = new EventEmitter();
sendEventToParent(): void {
this.eventFromChild.emit('data from child');
}
}
here is the parent component html called child
<child name="{{ name }}" (eventFromChild)="onEvent($event)"></child>
<h1>This is parent component</h1>
<p>{{dataFromChild}}</p>
and event bindin like that
import { Component, VERSION } from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
name = 'Angular ' + VERSION.major;
dataFromChild = '';
onEvent(event): void {
this.dataFromChild = event;
}
}
What you are thinking of is called an abstract class. An abstract class can define abstract properties just like an interface, abstract methods just like an interface, and unlike an interface it can actually implement methods. You cannot initialize an abstract class, but you can inherit code for re-use from it.
https://codesandbox.io/s/patient-breeze-h4s3t?file=/src/index.ts
abstract class Parent {
abstract someProperty: string;
someCall() {
console.log(this.someProperty);
}
}
class ChildOne extends Parent {
someProperty = "I am child one";
}
class ChildTwo extends Parent {
someProperty = "I am child two";
}
const one = new ChildOne();
const two = new ChildTwo();
one.someCall(); // "I am child one";
two.someCall(); // "I am child two";
I am trying to pass an array to a child component. The array is being defined in the subscribe method of a parent component's onInit lifecycle hook.
Parent component:
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'selector-parent',
templateUrl: 'parent.component.html'
})
export class ParentComponent implements OnInit {
array: [];
constructor() { }
ngOnInit() {
this.appDataService.getValues()
.subscribe(
value => {
value.item
.filter(loaded => !this.array.some(existing => existing.key === loaded.key))
.forEach(loaded => { this.array.push(loaded) })
}
)
}
}
Child component:
import { Component, OnInit } from '#angular/core';
#Component({
selector: '[selector-child]',
templateUrl: 'child.component.html'
})
export class ChildComponent implements OnInit {
#Input() inputArray: [];
constructor() { }
ngOnInit() {
console.log(this.inputArray)
}
}
Binding is: <tr selector-child [inputArray]="array"></tr>
Unfortunately, the console log on the inputArray is turning up undefined. I've tried using ngOnChanges in the child component but for some reason, it won't recognize the change in the parent. Thinking of using a service to pass the data if there isn't a simpler way to solve the problem.
You have to initialize the array in your parent component:
array: [] = [];
I don't see ngOnChanges in your child component. Also, make sure you do it as how it specified in the angular docs. https://angular.io/guide/component-interaction#intercept-input-property-changes-with-ngonchanges
In my app-root component I have router-outlet in container with some styles.
I have route:
{
path: 'some-path',
component: ChildContainer,
data: { variable:'variable' },
}
And I can to get variable in ChildContainer, but I need it in AppRoot. So, from documentation I can get it from child, but if I do this in AppRoot constructor:
const state: RouterState = router.routerState;
const root: ActivatedRoute = state.root;
const child = root.firstChild;
and console.log(root, child) - child is null, and root contains correct child (invoke property getter).
So, how can I get variable in AppRoot?
You may tap into activate event to get reference of instantiated component inside the router outlet.
Check This SO question
#Component({
selector: 'my-app',
template: `<h3 class="title">Basic Angular 2</h3>
<router-outlet (activate)="onActivate($event)" ></router-outlet>
`
})
export class AppComponent {
constructor(){}
onActivate(componentRef){
componentRef.sayhello();
}
}
#Component({
selector: 'my-app',
template: `<h3 class="title">Dashboard</h3>
`
})
export class DashboardComponent {
constructor(){}
sayhello(){
console.log('hello!!');
}
}
Here is the Plunker!!
Update
expose ActivatedRoute as a public property and once you have the routed component reference, subscribe to data,
onActivate(componentRef){
componentRef.route.data.subsribe(data => {
console.log(data);
});
}
I am new to Angular and am running into an issue with the constructor on a child component being called twice, and the second time it is called it is clearing the properties set the first time.
This is the parent component:
#Component({
selector: 'parent-item',
templateUrl: '...',
providers: [ItemService]
})
export class ParentItemComponent {
public parentItemId;
public model: ParentItem;
constructor(itemService: ItemService, elm: ElementRef) {
this.parentItemId = elm.nativeElement.getAttribute('parentItemId');
itemService.getParentItem(this.parentItemId).subscribe(data => this.model = data);
}
}
And in the template the child component is referenced:
<child-items [parentItemId]="parentItemId">Loading...</<child-items>
This is the child component:
#Component({
selector: 'child-items',
templateUrl: '...',
providers: [ItemService]
})
export class ChildItemsComponent {
#Input() public parentItemId: number;
public items: Observable<ChildItem[]>;
constructor(private itemService: ItemService) {
console.log("constructor");
}
ngOnInit() {
if (this.parentItemId) {
this.items = this.itemService.getChildItems(this.parentItemId);
}
else {
console.log("Parent Id not set!");
}
}
}
And finally the child component template:
<tr *ngFor="let item of items | async">
<td>...</td>
</tr>
The child components constructor is being called twice, and the second time it is called the parentItemId is set to null and the items property is cleared. If I hardcode the parentId instead of using the input the data is being properly received and displayed in the template, but using the input value the template shows no results.
I have created a plunker which shows the same behavior here: http://embed.plnkr.co/xaJtfNgbWCUPap2RCJUA/
Your problem is that in the app.module you bootstrap both parent and child component:
bootstrap: [ ParentItemComponent, ChildItemsComponent ]
It has to be
bootstrap: [ ParentItemComponent]
child-items is not closed properly. Probably because of this error
This
<child-items [parentItemId]="parentItemId">Loading...</<child-items>
should be:
<child-items [parentItemId]="parentItemId">Loading...</child-items>
In my application I have Home as root component and another generic component named as list which i'm rendering inside Home.
I want to pass data as property to my list component which is coming from XMLHttpRequest.
home.ts
import {Component} from 'angular2/core';
import {DashboardService} from '../../services/dashboard';
import {List} from '../contact/list';
#Component({
selector: 'home',
template:
`
<h3>Home</h3>
<List type="{{type}}"></List>
`
providers: [DashboardService],
directives: [List],
})
export class Home {
private _type: any;
constructor(private _dashboardService: DashboardService) {
this._dashboardService.typeToDisplay()
.subscribe((type) => {
this._type = type;
});
}
}
List.ts
#Component({
selector: 'List',
properties: ['type'],
template: `
<h2>list</h3>
`,
providers: [DashboardService]
})
export class List {
private type: any;
constructor(#Attribute('type') type:string) {
this.type = type;
console.log(type);
}
}
I'm getting string data from typeToDisplay() method its an Http request & assigning to type variable. but when I passed as property to list component I'm getting null in List constructor.
I tried too but i'm getting "type" string same way.
Hope my question is Clear.
This syntax
<List type="{{type}}"></List>
is setting a property not an attribute.
To set an attribute use either
<List attr.type="{{type}}"></List>
or
<List [attr.type]="type"></List>
If you just want to have the value available in List use
#Input() type: any;
instead of the attribute injection.
This way the value is not availabe yet inside the constructor, only in ngOnInit() or later.