Passing a parameter through output binding AngularJS - javascript

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.

Related

How to clear last #Input variable values ? Angular 2+

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);
}

How exactly works this Angular 5 example handling comunication between a parent and child component?

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.

Two Way Binding on an Angular 2+ Component

I have an Ionic application where I have created a component to show some data of an object. My problem is that when I update the data in the parent that hosts the component the data within the component does not update:
my-card.component.ts
#Component({
selector: 'my-card',
templateUrl: './my-card.html'
})
export class MyCard {
#Input('item') public item: any;
#Output() itemChange = new EventEmitter();
constructor() {
}
ngOnInit() {
// I do an ajax call here and populate more fields in the item.
this.getMoreData().subscribe(data => {
if (data.item){
this.item = data.item;
}
this.itemChange.emit(this.item);
});
}
}
my-card.html
<div class="comment-wrapper" *ngFor="let subitem of item.subitems">
{{subitem.title}}
</div>
And in the parent I use the component like this:
<my-card [(item)]="item"></my-card>
And the ts file for the parent:
#IonicPage()
#Component({
selector: 'page-one',
templateUrl: 'one.html',
})
export class OnePage {
public item = null;
constructor(public navCtrl: NavController, public navParams: NavParams) {
this.item = {id:1, subitems:[]};
}
addSubItem():void{
// AJAX call to save the new item to DB and return the new subitem.
this.addNewSubItem().subscribe(data => {
let newSubItem = data.item;
this.item.subitems.push(newSubItem);
}
}
}
So when I call the addSubItem() function it doesnt update the component and the ngFor loop still doesnt display anything.
You are breaking the object reference when you are making the api request. You are assigning new value, that is overwriting the input value you get from the parent, and the objects are no longer pointing to the same object, but item in your child is a completely different object. As you want two-way-binding, we can make use of Output:
Child:
import { EventEmitter, Output } from '#angular/core';
// ..
#Input() item: any;
#Output() itemChange = new EventEmitter();
ngOnInit() {
// I do an ajax call here and populate more fields in the item.
this.getMoreData(item.id).subscribe(data => {
this.item = data;
// 'recreate' the object reference
this.itemChange.emit(this.item)
});
}
Now we have the same object reference again and whatever you do in parent, will reflect in child.
If the getMoreData method returns an observable, this code needs to look as follows:
ngOnInit() {
// I do an ajax call here and populate more fields in the item.
this.getMoreData().subscribe(
updatedItem => this.item = updatedItem
);
}
The subscribe causes the async operation to execute and returns an observable. When the data comes back from the async operation, it executes the provided callback function and assigns the item to the returned item.
You declared item with #Input() decorator as:
#Input('item') public item: any;
But you use two-way binding on it:
<my-card [(item)]="item"></my-card>
If it is input only, it should be
<my-card [item]="item"></my-card>
Now if you invoke addSubItem() it should display the new added item.
this.item = this.getMoreData();
The getMoreData() doesn't make sense if you put it in your card component as you want to use the item passed via #Input()
Your component interactions are a little off. Check out the guide on the Angular docs (https://angular.io/guide/component-interaction). Specifically, using ngOnChanges (https://angular.io/guide/component-interaction#intercept-input-property-changes-with-ngonchanges) or use a service to subscribe and monitor changes between the parent and the child (https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service).

ngFor doesn't fires after update depending variable in Angular2

I have 2 components: CommandListComponent and CommandLineComponent. Inside of a CommandListComponent template i handle a click event on a text string:
CommandListComponent template:
<li *ngFor="#command of commandList" class="b-command-list__command"><span (click)="checkCommand(command)" class="b-command-list__text">{{command}}</span></li>
commandlist.component.ts
import {CommandLineComponent} from "./commandline.component";
...
export class CommandListComponent {
commandLineComponent: any;
constructor(private _commandLine: CommandLineComponent) {
this.commandLineComponent = _commandLine;
}
checkCommand(command: string): void {
this.commandLineComponent.add(command);
}
}
When click is fired i pass choosen command to add method of a CommandLineComponent:
export class CommandLineComponent {
commands: string[] = [];
add(command: string): void {
if (command) this.commands.push(command);
console.log(this.commands);
}
}
And within a template of a CommandLineComponent i print a list of a commands with *ngFor:
<li *ngFor="#command of commands" class="b-command-textarea__command">{{command}}</li>
But *ngFor doesn't fires when i choose a command and commands array of a CommandLineComponent updated. So, data binding is not working. commands array updates successfully:
Thank you for help.
The problem is the way you reference the commandLineComponent component. If there is a relation between them you could use the ViewChild decorator
class CommandListComponent {
#ViewChild(CommandLineComponent)
commandLineComponent: any;
(...)
}
If not, you need to use a shared service to share the commands list between these two components. Something like that:
export class CommandService {
commands:string[] = [];
commandAdded:Subject<string> = new Subject();
add(command: string): void {
if (command) {
this.commands.push(command);
this.commandAdded.next(command);
}
console.log(this.commands);
}
}
You need to define the service when bootstrapping your application and both components can inject it.
class CommandListComponent {
constructor(private commandService:CommandService) {
}
}
checkCommand(command: string): void {
this.commandService.add(command);
}
The CommandLineComponent component will be notified of a new command like this and can update the view accordingly:
class CommandLineComponent {
constructor(private commandService:CommandService) {
this.commandService.commandAdded.subscribe(command => {
// Update the list displayed in the component...
});
}
}

Update the data in one component based on what is clicked in another component in Angular 2

I have two components let's call them CompA and CompB. I would like for the clicked item object in CompA to appear in CompB. Here is what I have done so far.
CompA:
import {Component} from 'angular2/core';
import {CompB} from './compB';
#Component({
selector: 'comp-a',
template: '<ul>
<li *ngFor="#item of items" (click)="show(item)">
{{item.name}}
</li>
</ul>',
providers: [CompB]
})
export class CompA {
constructor(public _compB: CompB){}
show(item){
this._compB.displayItem(item);
}
}
CompB:
import {Component} from 'angular2/core';
#Component({
selector: 'comp-b',
template: '<div>
{{item.name}}
</div>'
})
export class CompB {
public newItem: Object;
constructor(){
this.newItem = {name: "TEST"};
}
displayItem(item){
this.newItem = item;
}
}
The problem is that when I click an item it doesn't change anything in CompB. I did a console log on CompB and I am getting the item object just fine but I view doesn't update with the clicked item's name. It just stays as 'TEST'.
Even if I set this.newItem in the displayItem function to a hardcoded string, it still doesn't change.
Update:
Both components are siblings in a main.html like this...
main.html
<div class="row">
<div class="col-sm-3">
<comp-a></comp-a>
</div>
<div class="col-sm-9">
<comp-b></comp-b>
</div>
</div>
Thats because the Component B you got injected in the constructor is not the component B used in the application. Its another component B that the hierarchical injector created, when Component B was added to the list of providers.
One way to do it is to create a separate injectable service, and inject it in both components. One component subscribes to the service and the other triggers a modification. For example:
#Injectable()
export class ItemsService {
private items = new Subject();
newItem(item) {
this.subject.next(item);
}
}
This needs to be configured in the bootstrap of the Angular 2 app:
boostrap(YourRootComponent, [ItemsService, ... other injectables]);
And then inject it on both components. Component A sends new items:
export class CompA {
constructor(private itemsService: ItemsService){}
show(item){
this.itemsService.newItem(item);
}
}
And component B subscribes to new items:
export class CompB {
constructor(itemsService: ItemsService){
itemsService.items.subscribe((newItem) => {
//receive new item here
});
}
Have a look at the async pipe, as its useful to consume observables in the template directly.
If you get a CompB instance passed to
constructor(public _compB: CompB){}
it's not the instance you expect but a different (new) one.
There are different strategies to communicate between components. This depends on how they are related in the view. Are they siblings, parent and child or something else. Your question doesn't provide this information.
For parent and child you can use data binding with inputs and outputs.
For siblings you can use data binding if you include the common parent (use it as mediator)
You always can use a shared service.
For data-binding details see https://angular.io/docs/ts/latest/guide/template-syntax.html

Categories

Resources