how to share data between two component using Event Emitter? - javascript

Could you please suggest how to add item in list when something enter in input field and then enter press.But these are different component .I want to share input field value to different component using #Input ,#Output ,EventEmitter.
I make one component .
<todo></todo>
here is ts file
import { Page,NavController,Modal } from 'ionic-angular/index';
import { Component, Input, Output, EventEmitter} from 'angular2/core';
#Component({
selector:'todo'
templateUrl:"addtodo.html",
})
export class AddToDO{
constructor() {
}
addItem(v){
alert(v.value)
}
}
here is my html
<label class="item item-input">
<span class="input-label" >Add Todo</span>
<input type="text" #todo placeholder="Add todo" (keyup.enter)="addItem(todo)">
</label>
I want to add item list when enter is press .so need to share data between component
http://plnkr.co/edit/N6aXDZXUr99MC6w77H6d?p=preview

I would leverage an #Output in your AddToDO component to trigger that an element was added. So you can catch it from the parent component and add the corresponding data in the list.
#Component({
selector:'todo'
templateUrl:"addtodo.html",
})
export class AddToDO{
#Output()
todoAdded:EventEmitter = new EventEmitter();
constructor() {
}
addItem(v){
alert(v.value)
this.todoAdded.emit(v);
}
}
and in the parent component template:
<todo (todoAdded)="addTodoInList($event)"></todo>
Here is the content of the addTodoInList method:
addTodoInList(todo) {
this.Todo.push(todo);
}
See this plunkr: http://plnkr.co/edit/mQtQIXIJwMfJSfNVHvac?p=preview.

Related

Disable a form field from another component in angular

I have three angular material based radio buttons namely male,female and other. So these three exist in a component.I also have three form fields namely A,B,C in another component named page1. When I press the male radio button and go to the page1 component the form field A and B in page1 component should not be visible. When I press female or other radiobutton all three form fields in page1 component should exist. I have tried for one day but nothing worked. Pls help if u know
Note:I am doing it using angular material(mat form field and mat radio button). In stackblitz I was unable to install those so pls do it in angular material way
I have attached a sample stackblitz link:
https://stackblitz.com/edit/angular-button-routerlink-mmn77s?file=src%2Fapp%2Fapp.component.html
you can use BehaviorSubject or Subject to trigger the function call
TriggerService.ts
import { Injectable } from "#angular/core";
import { BehaviorSubject } from "rxjs/internal/BehaviorSubject";
#Injectable()
export class TriggerService {
constructor() {}
private _trigger = new BehaviorSubject<string>("");
castTrigger = this._trigger.asObservable();
trigger(value) {
this._trigger.next(value);
}
}
parent.component.ts
export class AppComponent {
constructor(private triggerService: TriggerService) {
document.title = "Home";
}
changeType(value) {
this.triggerService.trigger(value);
}
}
child.component.ts
ngOnInit() {
this.triggerService.castTrigger.subscribe(value => {
this._type = value;
});
}
stackblitz example

How do you access the for loop of the parent component within a child component which has the inputs?

So I am stuck on this. I am trying to get the Parent component to talk or integrate with the child component.
Here is the parent component which basically has the for loop used to iterate or generate more links if a user wants to add more or presses the button to add more.
<div class="section url-wrapper">
<div *ngFor="let url of urls; let i = index;" class="link input-wrapper">
<childComponent></childComponent>
<button class="button bad icon-only" (click)="removeURL(i)">
<i class="far fa-times"></i>
</button>
</div>
</div>
The parent component should only be able to register and display the output of the child component.
This is an example of the child component
<div class="section url-wrap">
<input aria-label="URL Title" placeholder="Title" type="text" [value]="urls[i].title" (ngModel)="urls[i].title" name="url.title.{{ i }}"
(input)="updateTitle(i, $event.target.value)">
<input aria-label="URL" placeholder="https://example.com" type="text" [value]="urls[i].url" (ngModel)="urls[i].url" name="url.url.{{ i }}"
(input)="updateUrl(i, $event.target.value)">
</div>
I need help both allowing the parent component to register input from the child component and being able to iterate from the for loop from the parent if it is possible.
Please let me know if you need more information such as the component files or clarification
The below code & example will demonstrate how data flows from parent -> child -> parent by using the #Input() and #Output() directives.
Working Example Here
parent.component.ts
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
<div class="section url-wrapper">
<div *ngFor="let url of urls" class="link input-wrapper">
<app-child [url]="url" (updateUrl)="onUrlUpdate($event)"></app-child>
</div>
</div>
`
})
export class ParentComponent implements OnInit {
public urls = [
{url: "https://example.com", title: "Example1"},
{url: "https://example.com", title: "Example2"},
{url: "https://example.com", title: "Example3"},
]
constructor() { }
ngOnInit() {
}
onUrlUpdate($event) {
// completely overkill, but just used to demonstrate a point
var url = this.urls.find(_url => {
// we can see here that the $event.url is actually the same object as the urls[i] that was
// passed to the child. We do not lose the reference when it is passed to the child or back
// up to the parent.
return $event.url === _url
});
if (url) {
url[$event.prop] = $event.newValue;
}
console.log(`Updated URL's "${$event.prop}" property with the value "${$event.newValue}"`);
}
}
child.component.ts
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
#Component({
selector: 'app-child',
template: `
<div class="section url-wrap">
<input aria-label="URL Title" placeholder="Title" type="text" [value]="url.title"
(input)="handleUrlUpdate($event, 'title')"/>
<input aria-label="URL" placeholder="https://example.com" type="text" [value]="url.url"
(input)="handleUrlUpdate($event, 'url')"/>
</div>
`,
})
export class ChildComponent implements OnInit {
#Input() url; // passed in from parent via [url] property on <app-child>
#Output() updateUrl = new EventEmitter();
constructor() { }
ngOnInit() {
// this.url is now available for the life of the child component (assuming it was passed by the parent)
}
handleUrlUpdate($event, propToUpdate) {
// overkill, but used to demonstrate a point
this.updateUrl.emit({url: this.url, prop: propToUpdate, newValue: $event.target.value});
}
}
The stardard way to let components speack each others is with input-output:
You can pass values from parent to children with #Input for example:
Parent code:
<childComponent [someInputValue]="hello"></childComponent>
Children code:
#Input() someInputValue; //this property will be "hello"
and you can pass values (after being triggered) from children to parent:
Children code:
#Output() itemSelectedOutput: EventEmitter<any> = new EventEmitter();
buttonClicked() {
this.itemSelectedOutput.emit("clicked");
}
Parent code:
<childComponent [someInputValue]="hello" (itemSelectedOutput)="someParentMethod($event)"></childComponent>
someParentMethod(event: any) {
console.log(event);
}
You can reach the same thing with ISubscription but I suggest you to use the way above
Hope it can help
I wouldn't do it this way in particular. If the children have to know about the parents, then your architecture should be adjusted

Using #Output() with dynamically added components

I'm creating a shopping list.
It will be made out of two components: shopping-cart and shopped-item.
The shopping-cart has a button that dynamically adds a new shopped-item in a <div>.
The shopped-item after being added can be marked as active or unmarked so I created an EventEmmiter that changes the value marked/unmarked.
But since the component is added dynamically I don't know where to add it in shopping-cart component...
How can I make it work like this:
After the shopped-item is added it appears in an array with marked/unmarked value that changes when it's clicked in the shopped-item component?
Cheers!
Shopped-item.ts file:
export class ShoppedItemComponent implements OnInit {
_ref:any;
removeObject(){
this._ref.destroy();
}
#Output() statusChange = new EventEmitter<{status: boolean}>();
marked;
unmarkItem () {
this.marked = !this.marked;
this.statusChange.emit({status: this.marked});
}
constructor() {
}
}
Shopping-cart.ts file:
export class ShoppingCartComponent implements OnInit {
#ViewChild('boughtItems', { read: ViewContainerRef }) boughtItems:
ViewContainerRef;
constructor(
private resolver: ComponentFactoryResolver
) { }
isMarked = [];
shoppedItemStatus (statusChange: {status: boolean}) {
this.isMarked.push({
status: statusChange.status
})
}
addItem() {
const shoppedItem =
this.resolver.resolveComponentFactory(ShoppedItemComponent);
const component = this.boughtItems.createComponent(shoppedItem);
component.instance._ref = component;
}
}
Shopping-cart.html file:
<div #boughtItems>
<button (click)="addItem()">ADD</button>
</div>
Why are you creating the components by hand?
I would use a *ngFor in the view
<div #boughtItems>
<button (click)="addItem()">ADD</button>
<div *ngFor="let item of items">
<app-item-bought xxxx="item" (markToggle)="myFunction(item)"></app-item-bought>
</div>
</div>
where xxxx is a field of your class ShoppedItemComponent decorated with Input('xxxx').
(markToggle) is the name of the emitter in ShoppedItemComponent and myFunction(item) is a function defined in the Shopping-cart that will receive the item that has fired the event.
Hope it helps!

Get child component form value in parent component's form submission?

Note: since the problem is a little complex, the code is abstracted for readability
We have a <child-component> component template like this:
<select name="someName" #someID ngDefaultControl [(ngModel)]="someModel" (ngModelChange)="onChange(someID.value)">
<option *ngFor="let a of list" [ngValue]="a.key">{{a.value}}</option>
</select>
And the ts file is with output configuration:
#Component({
moduleId: module.id,
selector: 'child-component',
templateUrl: 'child-component.html',
outputs: ['someChildEvent']
})
export class ChildComponent {
someChildEvent = new EventEmitter<string>();
onChange(value) {
this.someChildEvent.emit(value);
}
}
Which we're calling in a <parent-component> like this:
<form #f="ngForm" (submit)="submit(f)" >
<child-component (childEvent)="childData=$event"></child-component>
<input type="submit" value="submit">
</form>
with .ts like:
export class ParentComponent {
childData;
submit(f) {
//How to access childData here?
}
}
Is it the correct implementation of the output configuration in Angular 2?
How do we access the value of the select's option from <child-component> on <parent-component>'s form submit to submit(f) function?
<child-component (childEvent)="childData=$event"></child-component>
the event name in here needs to match your method name so it should be:
<child-component (someChildEvent)="childData=$event"></child-component>
and if you like to send the object selected in your select box, change ngValue to that object and the change the model event accordingly:
[ngValue]="a"
(ngModelChange)="onChange($event)"

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