How to receive the input values of variable number of input boxes?
I searched a long time in Google but I'm not able to do it.
ParentComponent.html:
<my-Input [interfaces]="interfaces"> {{ title }}</my-Input>
ParentComponent.ts:
interface single_input{
title: string,
get_value(event: string): void;
}
interfaces: single_input[] = [
{
title: "something",
get_value(event){ console.log(event)}
}
];
ChildComponent.html:
<div *ngFor="let i of interfaces">
<Input (keyup)="i.get_value($event.target.value)"> {{ title }}</Input>
</div>
To log this in the console works, but I just want to bind the input values to the given interface and give it back by eventemitter to the parent.
How to do this? Do I need a class for it?
I got it now.
But it looks like a workaround. is there a better solution than following code?
Code-Update:
ParentComponent.ts:
interface single_input{
title: string;
value: string;
}
interfaces: single_input[] = [
{
title: "something",
}
];
ChildComponent.html:
<div *ngFor="let i of interfaces">
<Input (keyup)="i.value = $event.target.value"> {{ title }}</Input>
</div>
It is far simpler than what you are trying to do. Angular have specific mechanism to achieve this goal, one of them being the decorators #Input and #Output. You are already using the #Input decorator to pass in the data to the child component, now you just need to use the #Output decorator to pass the data back to the parent. I recommend looking at the docs and this example to have a better understanding on how this works. Below is an example for your use case.
ChildComponent.html
<div *ngFor="let i of interfaces">
<input (keyup)="onKeyUp(i, $event.target.value)"> {{ title }}</input>
</div>
ChildComponent.ts
import { Component, EventEmitter } from '#angular/core';
#Component({ ... })
export class ChildComponent {
...
#Input() interfaces;
#Output() childValue: EventEmitter<any> = new EventEmitter<any>();
// ^ Use a proper datatype ^
onKeyUp(source, value) {
this.childValue.emit({
source: source,
value: value
});
}
...
}
ParentComponent.html
<my-input [interfaces]="interfaces" (childValue)="onChildValueChange($event)"> {{ title }}</my-input>
ParentComponent.ts
import { Component} from '#angular/core';
#Component({ ... })
export class ParentComponent {
...
onChildValueChange(event) {
// Do what you want with it
const interface = this.interfaces.find(interface =>
interface.title === event.source.title);
console.log('[ParentComponent] Value from child component:', event);
console.log('[ParentComponent] Changed interface:', interface);
console.log('[ParentComponent] Changed value:', event.value);
}
...
}
Extra tip: Just for completeness, an equally popular approach, would be using a service to contain the data, where the child changes the data on the service and the parent read from it.
Related
So I am following the Traversy Media new [Angular crash course]: https://www.youtube.com/watch?v=3dHNOWTI7H8 around 39:45 and getting object undefined errors constantly.
Here is the picture of the error which says object is possibly 'undefined':
object is possibly 'undefined
This worked in task-item.component.html
<p *ngFor="let task of tasks">
{{task.text}}
</p>
This object is possibly 'undefined' task-item.component.html
<div class="task">
<h3>{{task.text}}</h3>
<p>{{task.day}}</p>
</div>
task-item.component.ts
import { Component, OnInit, Input } from '#angular/core';
import {Task} from 'src/Task';
#Component({
selector: 'app-task-item',
templateUrl: './task-item.component.html',
styleUrls: ['./task-item.component.css']
})
export class TaskItemComponent implements OnInit {
#Input() task?: Task;
constructor() {
}
ngOnInit(): void {
}
}
I have put a tsconfig.json rule "strictPropertyInitialization": false
It's because you set it as a an optional using ?
You can either remove ? which is not a good practice if your item can be null, or you can just do this in the html
<div class="task">
<h3>{{task?.text}}</h3>
<p>{{task?.day}}</p>
</div>
It will display the value if the variable is initialized, and don't throw an error
The ? here indicates that this is a nullable object.
#Input() task?: Task;
If you always pass task to app-task-item then just remove the ?
#Input() task: Task;
Or you can add a conditional in your html, and remove the ? in your .ts
#Input() task: Task;
...
<div class="task">
<h3>{{task?.text}}</h3>
<p>{{task?.day}}</p>
</div>
If you're not passing a value into task when you instantiate the component, which than it would be undefined.
If are ever going to instantiate task-item without a task input you could add something like this to your ngOnInit
const blankTask: Task = {
text: 'placeholder text',
day: 'placeholder day',
};
...
ngOnInit() {
this.task = this.task || blankTask;
}
This means you do not always have to pass in a task to the component.
Working StackBlitz
To solve this I add *ngIf
<div class="task" *ngIf="task">
<h3>{{task.text}}</h3>
<p>{{task.day}}</p>
</div>
I'm very new to Angular, and I'm really struggling to find a concise answer to this problem. I have a Form Component Here:
(I'm excluding the directives and imports as they're not really relevant)
export class JournalFormComponent implements OnInit {
public entries: EntriesService;
constructor(entries: EntriesService) {
this.entries = entries;
}
ngOnInit(): void {
}
}
The EntriesService service just stores an array of entries:
export class Entry {
constructor (
public num: number,
public name: string,
public description: string,
public text: string
) { }
}
The Form Component template renders a <h2> and a <app-input> Component for each entry in the EntriesService, which works. That looks like this:
<div *ngFor="let entry of entries.entries">
<h2> {{ entry.num }}. {{ entry.name }} </h2>
<app-input id="{{entry.num}}"></app-input>
</div>
Here's the <app-input> Input Component:
#Component({
selector: 'app-input',
template: `
<textarea #box
(keyup.enter)="update(box.value)"
(blur)="update(box.value)">
</textarea>
`
})
export class InputComponent {
private value = '';
update(value: string) {
this.value = value;
}
getValue () {
return this.value;
}
}
The InputComponent stores the user's text perfectly, but I don't know how to pass that data to the Form Component's EntriesService to update the Entry in order to Export it or Save it later. How is this done?
I think I'm phrasing this question well, but I'm not sure. If you need clarification I'll provide it.
Not sure if it matters, but I'm using Angular 9.1.11
There are many ways to update the data from one component to another.
component to component using service or subjects
parent~child component data exchange using Input() and Output() decorators. Or by using #ViweChild() interactions.
and many more
But please do check the angular docs https://angular.io/guide/component-interaction .
Use the below simple code, u might need to include modules like FormsModule. and import Input(), Output etc
#Component({
selector: 'app-journal-form',
template: `
<div *ngFor="let entry of entries.entries; let i=index">
<h2> {{ entry.num }}. {{ entry.name }} </h2>
<app-input id="{{entry.num}}" [entry]="entry" [arrayIndex]="i" (updateEntry)="updateEntry($event)" ></app-input>
</div>`
})
export class JournalFormComponent implements OnInit {
constructor(private entries: EntriesService) {
this.entries = entries;
}
ngOnInit(): void {
}
updateEntry(event){
console.log(event);
this.entries[event.arrayIndex] = event.entry;
}
}
#Component({
selector: 'app-input',
template: `
<textarea [(ngModel)]="name"
(keyup.enter)="update()"
(blur)="update()">
</textarea>
`
})
export class InputComponent {
#Input() entry: any;
#Input() arrayIndex: number;
#Output() updateEntry: EventEmitter<any> = new EventEmitter();
name:string;
constructor() {
console.log(entry);
this.name = entry.name;
}
update(){
this.entry.name = this.name;
this.updateEntry.emit({entry: this.entry, arrayIndex});
}
}
Output event will help in this situation.
<div *ngFor="let entry of entries.entries">
<h2> {{ entry.num }}. {{ entry.name }} </h2>
<app-input id="{{entry.num}}" (entryChange) = "entry.text = $event"></app-input>
</div>
app-input component
export class InputComponent {
private value = '';
#Output() entryChange = new EventEmitter<string>();
update(value: string) {
this.value = value;
this.entryChange.emit(value);
}
}
Instead of entry.text = $event you can also pass it to any save function, like saveEntry($event);
Following what is documented here: Dynamic Component Loader.
I want to know how is it possible to handle the data inside this HeroJobAdComponent class:
import { Component, Input } from '#angular/core';
import { AdComponent } from './ad.component';
#Component({
template: `
<div class="job-ad">
<h4>{{data.headline}}</h4>
{{data.body}}
</div>
`
})
export class HeroJobAdComponent implements AdComponent {
#Input() data: any;
}
As you can see, data is the object holding the data received. I want to be able to define a constructor for my HeroJobAdComponent class but if I do, the object data is undefined inside my constructor. I tried using ngOnChange instead which supposedly executes once input is changed from undefined to defined but it also did not execute at all.
Can someone please explain first why is the object undefined even though the data is defined in my main component calling it, and what's the workaround for this issue?
This is the constructor I am using:
constructor()
{
this.values = this.data.values;
this.spec_name = this.data.spec_name;
}
if you want to use any operation when you receive data in your component , you can use setter
export class HeroJobAdComponent implements AdComponent {
_data;
#Input() set data (data: any){
//operation on data goes here
this._data=data
};
get data() {
return this._data;
}
}
I want to access the text I have in a text area in my child component to put it on the parent component and keep it updated.
I was told that #input in angular 4 is supposed to perform two-way binding. But I can't do that that way, and I don't understand why.
I found a workaround for this issue. It includes an #Output to send the info to the parent component. But if Input already does that (in some way I don't know), I want to avoid it.
For example, this is my Parent Component
import { Component } from '#angular/core';
#Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
})
export class SettingsComponent {
private studyDesignText = 'Text';
constructor() {
}
public handleStudyDesignUpdated(designText: any) {
this.studyDesignText = designText;
}
}
It's html
<div class="section section-trials-settings-parent light rounded">
<div class="section section-trials-settings-child">
<div class="pure-g">
<div class="pure-u-1-1">
<app-settings-study-design
[studyDesignText]="studyDesignText">
</app-settings-study-design>
</div>
</div>
</div>
</div>
My child component:
import { Component, OnInit, Input } from '#angular/core';
#Component({
selector: 'app-settings-study-design',
templateUrl: './settings-study-design.component.html',
})
export class SettingsStudyDesignComponent implements OnInit {
#Input() studyDesignText: string;
constructor() {
}
ngOnInit() {
super.onInit();
loadControls();
}
loadControls(): void {
this.startAllTextAreas();
}
private startAllTextAreas() {
this.startTextArea('study-design-textarea');
}
private startTextArea(htmlId: string) {
// code to configure my text area; it's right...
}
If I change the value in the text area and send a signal with #Output so my parent component can be notified and console log the value, the printed value is the initial one. My friend did the same thing and it worked.
What am I missing?
#Input() is always one way binding from parent->child. Two way binding happens in this case, only when you have object as an input property. This is because, the reference for objects remain the same. And when one of the object updates, the other will also get updated. This is not true for string or number. It is always one way binding.
Say I have a component that will display a name property, so it roughly goes like this:
import {Component, Input} from 'angular2/core';
#Component({
selector: 'demo',
template: `<div>{{name}}</div>`,
styles: [``],
})
export class Demo {
#Input() name: string;
}
The problem is, how could I display [noname] when someone using this component but not passing any name property?
The only solution comes to mind is using logic operator inside template like {{ name || '[noname]' }}.
try
#Input() name: string = 'noname';
with angular 8 and the default tslint settings your IDE will notice:
so it´s okay to just write:
#Input() addclass = '';
without any "type annotation".
In the component you should initialize like:
#Input () name:String='';
In the HTML you can use:
{{ name ===''? 'empty string': name }}
You can intercept #Input() with a setter and have it backed by a private field. In setter, you do a nullcheck so field gets set only to a non-null value. As last, you bind your template to a private fied in which you have set initial value.
I think you can use your idea of using the template. So it would be:
In Component:
#Input () name:String;
In Template:
<div>{{ name != '' ? name : '[no name]' }}</div>
That would check to see if the name is empty, and use '[no name]' or insert the name if the name is passed.
Here is the proper solution to this. (ANGULAR 2 to 9)
Addressing solution: To set a default value for #Input variable. If no value passed to that input variable then It will take the default value.
Example:
I have an object interface named bike:
export interface bike {
isBike?: boolean;
wheels?: number;
engine?: string;
engineType?: string;
}
You made a component named app-bike where you need to pass the properties of bike with #input decorator of angular. But you want that isBike and Wheels properties must have a default value (ie.. isBike: true, wheels: 2)
export class BikeComponent implements OnInit {
private _defaultBike: bike = {
// default isBike is true
isBike: true,
// default wheels will be 2
wheels: 2
};
#Input() newBike: bike = {};
constructor() {}
ngOnInit(): void {
// this will concate both the objects and the object declared later (ie.. ...this.newBike )
// will overwrite the default value. ONLY AND ONLY IF DEFAULT VALUE IS PRESENT
this.newBike = { ...this._defaultBike, ...this.newBike };
// console.log(this.newBike);
}
}
For more detailed article refer to this.
Refer Destructuring assignment from here