Issue
I’m trying to figure how to pass value from one Angular component to another my-input.component
and my-button.component
:
I’m trying to receive from my-input.component.ts
and display @Input('name')
with button (click)="displayReceivedValue()"
from my-button.component.html
in my-button.component.ts
:
import { Component, Input, OnInit } from '@angular/core';
import { MyInput } from '../../interfaces/my-input';
@Component({
selector: 'app-my-button',
templateUrl: './my-button.component.html',
styleUrls: ['./my-button.component.scss'],
})
export class MyButtonComponent implements OnInit {
@Input('name')
public input!: MyInput;
constructor() {}
ngOnInit(): void {}
async displayReceivedValue() {
console.log('Received value: ', this.input);
}
}
which is <app-my-button name=""></app-my-button>
component in Login page
login.component.html
from my-input.component.ts
with this.myValue
from [(ngModel)]="myValue"
in input
of my-input.component.html
, but I’m not sure, how to pass it to name=""
:
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
@Component({
selector: 'app-my-input',
templateUrl: './my-input.component.html',
styleUrls: ['./my-input.component.scss'],
})
export class MyInputComponent implements OnInit {
public myValue: any;
constructor() {}
ngOnInit(): void {}
}
Also I’ve tried use @Output()
with <app-my-button (inputValue)="acceptData($event)"></app-my-button>
component in Login page
from my-input.component.ts
:
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
@Component({
selector: 'app-my-input',
templateUrl: './my-input.component.html',
styleUrls: ['./my-input.component.scss'],
})
export class MyInputComponent implements OnInit {
@Output() inputValue= new EventEmitter<string>();
public myValue: any;
constructor() {}
ngOnInit(): void {
this.inputValue.emit(this.inputValue);
}
}
in my-button.component.ts
:
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-my-button',
templateUrl: './my-button.component.html',
styleUrls: ['./my-button.component.scss'],
})
export class MyButtonComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
acceptData(data: any) {
console.log(data);
}
}
I got nothing if I add (inputValue)="acceptData($event)
to my-button.component.html
, or if I add (inputValue)="acceptData($event)
to login.component.html
I got error:
TS2339: Property ‘acceptData’ does not exist on type
‘LoginComponent’.
Solution
Option 1: Using ControlValueAccessor
Try to make it simple; Lets say you have below in your app component
app.component.html
<app-my-input [(ngModel)]='myValue'></app-my-input>
<app-my-button [name]='myValue'></app-my-button>
app.component.ts
myValue = 'MyValue'
Basically we are binding to the same value in the app.component.ts
. So the value will be updated to the button name
property when it changes.
Now if you try to run above you will receive an error
No value accessor for control with the name
app-my-input
That is because we are binding to the ngModel
that requires to implement ControlValueAccessor
. So we implement this like below
my-input.component.ts
@Component({
selector: 'app-my-input',
templateUrl: './my-input.component.html',
styleUrls: ['./my-input.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => MyInputComponent),
multi: true
},
]
})
export class MyInputComponent implements ControlValueAccessor {
myValue = '';
constructor() { }
writeValue(value: any): void {
if (value !== undefined) {
this.myValue = value;
}
}
onChanges: ($value: any) => void;
onTouched: () => void;
registerOnChange(fn: any): void {
this.onChanges = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
}
my-input.component.ts
<input [(ngModel)]='myValue' (ngModelChange)='onChanges(myValue)'>
Now our app-my-input
works just like a normal input and we can bind to the [(ngModel)]
directive
So now in our app-my-button
we can receive this using @Input
decorator
app-my-button.ts
@Input() name = "";
constructor() {}
ngOnInit(): void {}
displayReceivedValue() {
console.log("Received value: ", this.name);
}
app-my-button.html
<button (click)='displayReceivedValue()'>My button</button>
Now if you click on the button you will see a log in the console with the value of the input
Option 2: Using @Output
and @input
decorators
To use @input
and @Output
you would simply define you are input with something like
export class MyInputComponent implements OnInit {
@Input() input = '';
@Output() output: EventEmitter<string> = new EventEmitter()
myValue = '';
constructor() { }
onChanges = ($event) => {
this.output.emit($event)
}
ngOnInit() {
this.myValue = this.input
}
}
and in the html
<input [(ngModel)]='myValue' (ngModelChange)='onChanges(myValue)'>
Now we bind to (ngModelChange)
. When the value changes, we emit the value using the output
property defined as an EventEmitter<string>
We can then receive this with
<app-my-input [input]='myValue' (output)="myValue = $event"></app-my-input>