I have 2 simple components. One is parent the other is child. The parent component has an Array, For each element in the Array, It renders the child component.
parent.component.ts
export class parent implements OnInit {
data: CustomType[] = [
{
id: "child1",
records: [] // array of string
},
{
id: "child2",
records: [] // array of string
}
];
ngOnInit() {}
}
parent.component.html
<section>
<ChildComponent *ngFor="let child of data | async" [obj]="child"/>
</section>
child.component.ts
export class child implements OnInit {
// The data is passed from the parent component
#Input() obj: CustomType;
ngOnInit() {}
}
child.component.html
<div>
{{ obj.id }}
</div>
The Problem
The current code works just fine. But the issue is if the records of an element change in the array, It re-renders all the children components. I want to re-render the exact component only.
I am wondering how to use the onPush Change Detection here.
For Example:
If data[0].records changes, It should re-render the data[0]'s child component only.
add the trackBy function so that it does not render everthing but only renders the one where the trackBy function is changed!
html file
<section>
<ChildComponent *ngFor="let child of data | async; trackBy: trackBy" [obj]="child"/>
</section>
ts file
trackBy(index, item) {
return item.id;
}
reference here
Related
I use changeDetection strategy and it works ok but when my component is destroy when I come back I have the last value saved.
Example I props 3 values to child component and went to another component when i try again to prop data i see my last values ..
Example props values 1 , 2 and 3.
I see my last values 3.
How to destoy it ?
Check code and parent component:
<div class="row">
<app-poi-address [poiPin]="pinedAddress"></app-poi-address>
</div>
this.pinedAddress = $event;
Child component:
#Component({
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PoiAddressComponent implements OnInit, OnChanges {
#Input () poiPin!: Object
public allPin: any[] = [];
constructor() { }
ngOnInit(): void {
console.log('datica' , this.poiPin);
}
ngOnChanges(): void {
console.log('HelloComponent: Change Detection count = ' , this.poiPin);
this.allPin.push(this.poiPin)
}
}
<div *ngFor="let d of allPin ">
<p> {{ d?.coords?.lat }} </p>
</div>
Saved last values. I want to clear all array of allPin...
This would clear your all pin variable on when recreating the component after desctruction.
ngOnInit(): void {
allPin = [];
console.log('datica' , this.poiPin);
}
I have a parent component that is passing information to a child component, but it is undefined. I apologize if this is Angular 101, but how can I delay the child component from rendering or running any javascript until it has the input that it needs?
Parent component ngOnInit that pulls in data:
public ngOnInit(): void {
this.productService.getProductById(1).subscribe(product => {
this.product = product;
});
}
Parent components HTML:
<child-component [product]="product" id="myProduct"></child-component>
Child component input declaration:
#Input() public product: Product;
Child component ngOnInit which required the product to be there:
public ngOnInit(): void {
// It is returning as undefined
console.log(this.product);
}
This is because the products are loaded asynchronously, so when the view renders initially, the product is undefined
<child-component *ngIf="product" [product]="product" id="myProduct"></child-component>
also you can use Onchanges
ngOnChanges(changes: SimpleChanges) {
if (changes['product']) {
let variableChange = changes['product'];
}
}
I am starting with Angular and I have some doubts about how exactly works this example related to comunication between a parent and a child component.
So I have this PARENT COMPONENT. This component is used to show a list of items (each item is represend by the child component). Interacting with this component I can add and remove items from this list of items.
This is the parent Component :
#Component({
selector: 'app-products',
templateUrl: './products.component.html'
})
export class ProductsComponent {
productName = 'A Book';
isDisabled = true;
products = ['A Book', 'A Tree'];
constructor() {
setTimeout(
() => {
this.isDisabled = false;
}, 3000)
}
onAddProduct() {
this.products.push(this.productName);
}
onRemoveProduct(productName: string) {
this.products = this.products.filter(p => p !== productName);
}
}
And this is its template :
<h1>My Products</h1>
<input *ngIf="!isDisabled" type="text" [(ngModel)]="productName">
<button *ngIf="!isDisabled" (click)="onAddProduct()">Add Product</button>
<app-product
(productClicked)="onRemoveProduct(product)"
*ngFor="let product of products"
[productName]="product">
</app-product>
Then I have the child component representing a single item of the list handled by the parent component. This is the child Component :
#Component({
selector: 'app-product',
templateUrl: './product.component.html',
styleUrls: ['./product.component.css']
})
export class ProductComponent {
#Input() productName: string;
#Output() productClicked = new EventEmitter();
onClicked() {
this.productClicked.emit();
}
}
And this is te children template :
<article class="product" (click)="onClicked()">
<div>{{ productName }}</div>
<p>An awesome product!</p>
</article>
Ok, now I have some doubts about how these 2 components interact together:
In the parent component template :
<app-product
(productClicked)="onRemoveProduct(product)"
*ngFor="let product of products"
[productName]="product">
</app-product>
that is referring to the child component (labeled by app-product).
It seems to me that basically I am iterating on the products list defined into the parent component class (an array of strings) and that each of these string is passed to the productName variable defined into the child component class, to do it I am using the #Input() decorator on this property in the child component class:
#Input() productName: string;
Basically this #Input() decorator hallow the parent component to "inject" the value into the child component property at each iteration.
Is it? or am I missing something?
Then I have the behavior that handle the item removal from the items list: when an element of the list is clicked (implemented by the child component). This element is removed from the list (and so from the page).
How it works (my idea):
Each element is represented by this code in the child component view:
<article class="product" (click)="onClicked()">
<div>{{ productName }}</div>
<p>An awesome product!</p>
</article>
When an object is clicked it is performed the onClicked() event into the child component class. I can't remove the selected object directly from the child component class because the array of items is defined into the parent component class. So this method emit an event:
onClicked() {
this.productClicked.emit();
}
having "type" productClicked (is it an event type or what?). This event is received in the parent component view:
(productClicked)="onRemoveProduct(product)"
that call the onRemoveProduct(product) method that remove the object having this name from the array.
Is it correct or in my reasoning am I missing something?
Another doubt is: is it a neat and correct way to handle events and this kind of situation?
You are right in both cases !
And according to the angular guide, this is a correct way to handle communication between parent/child components.
I'm having to following setup with AngularJs 1.5.8, using Typescript:
I have a parent component wich contains a catalog (2D array of items). From where I pass each list in the catalog to a child component:
...
export class ParentController {
private catalog = []; //contains a list of lists
...
private printCallback(item: any): void {
console.log(item);
}
}
and the template:
<div ng-repeat="list in $ctrl.catalog">
<child content="list" callback="$ctrl.printCallback(item)"></child>
</div>
Now in the child component I iterate again over every item in the list. And whenever I click on an item from the list, I want the parent to know the item I clicked on:
export class ChildComponent implements IComponentOptions {
template: any = require('./child.component.html');
public bindings: any = {
content: '<',
callback: '&'
};
controller: any = ChildController;
}
export class ChildController {
public content: Array<any>;
public callback;
...
public onTrigger(item: any): void {
this.callback(item);
}
}
And the child template:
<div ng-repeat"item in $ctrl.content">
<button ng-click="$ctrl.onTrigger(item)">{{item.name}}</button>
</div>
Now I can't seem to print the item in the printCallBack() of my parent component. I don't know exactly what I'm doing wrong cause the only thing that's printing is undefined.
I've looked around and couldn't find a working solution. So I hope you can help me.
While callback, do it like: this.callback({item : item});, pass as object.
Currently having issue with Child component trying to remove Parent component's Array.
Parent Component:
#Component({
selector: 'parent',
templateUrl: 'app/templates/parent.html'
})
export class ParentComponent {
public items = [];
}
Parent HTML
<child [items]="items"></child>
<product *ngFor="let item of items><product>
Child component
#Component({
selector: 'child',
templateUrl: 'app/templates/child.html'
})
export class ChildComponent{
#Input() items;
emptyItems() {
this.items = [];
}
addItem() {
this.items.push({'title': 'foo'});
}
}
However when I call emptyItems/addItem function, the items array in the child view will reflect on changes, however on the parent component it doesnt change.
Do I need to use Output?
The right way is to use Output https://angular.io/docs/ts/latest/cookbook/component-communication.html#
However we can use two-ways binding on your items, this will reflect changes on both sides:
<child [(items)]="items"></child>
See more details https://angular.io/docs/ts/latest/guide/cheatsheet.html
Update:
Try to empty you array differently How do I empty an array in JavaScript?
Output should work https://angular.io/docs/ts/latest/api/core/index/Output-var.html
The idea is that you have to pass updated things from child to parent and manually update member items of the parent