Trying to update input value in the same component but not able to update.Getting error like
ERROR
Error: Cannot read properties of undefined (reading 'pop')
So, How to resolve this issue?
table.component.ts:
export class TableComponent implements OnInit {
#Input() names: any;
constructor() {}
ngOnInit() {}
testFn() {
this.names.pop('Test22');
this.names = [...this.names];
console.log(this.names);
}
}
Demo : https://stackblitz.com/edit/angular-pass-table-data-to-input-property-dlsufy?file=src%2Fapp%2Ftable%2Ftable.component.ts
Jay Swaminarayan!
You are doing it slightly wrong while passing the Component Reference.
In ChangeComponent, it is not referencing the table component properly.
In AppComponent HTML the table component must be passed as reference input to the changecomponent.
You may look at this corrected code
https://stackblitz.com/edit/angular-pass-table-data-to-input-property-2ehcxs?file=src%2Fapp%2Ftable%2Ftable.component.html,src%2Fapp%2Ftable%2Ftable.component.ts,src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.ts,src%2Fapp%2Fchange%2Fchange.component.html,src%2Fapp%2Fchange%2Fchange.component.ts,src%2Fapp%2Ftable%2Ftable.component.css
Related
I like to explore the feasibility with using component Input as Event output, similar to how react does it.
Parent component
parentHandler(message){
console.log(message)
}
Parent html
<child [myEvent]="parentHanlder"></child>
Child component
#Input myEvent
ngOnInit(){
this.myEvent('hello')
}
I am wondering if there are disadvantage or potential issue with this approach e.g memory/performance
thank you
This will work perfectly as expected.
The behavior will be similar to #Output except the context of execution.
The scope of execution will be with child component
export class AppComponent {
version: number = 7;
messageMe(param){
alert("Message Me.."+param+this.version);
}
}
<framework
[messageHandler]="messageMe"></framework>
export class FrameworkComponent implements OnInit {
#Input()
messageHandler: Function;
name="Jone";
ngOnInit() {
this.messageHandler(this.name);
}
}
You can use setter in this situation
Parent:
<child [myEvent]="parentHanlder"></child>
Child (ts):
#Input()
set myEvent(parentHandler: string) {
this.myEvent('hello');
}
I have a component MyComponent and it is declared like this:
export class MyComponent implements IComponent {
...
#Input() Departments: any;
#Input() DropDownOptions: any;
#Input() Data: any[];
...
}
However, there is no property Data, when I try to access from PersonComponent component.
HTML of PersonComponent component:
<fieldset>
<my-comp #myGrid [Options]="ps.Options['myGrid']"></my-comp>
</fieldset>
TypeScript of PersonComponent component:
export class PersonComponent implements OnInit {
#ViewChild('myGrid') myGridComponent: MyComponent;
ngAfterViewInit() {
debugger;
let localData2 = this.myGridComponent.Data; // NO DATA PROPERTY. Undefined
}
ngAfterContentInit() {
debugger;
let localData1 = this.myGridComponent.Data; // NO DATA PROPERTY. Undefined
}
}
Variables that can be seen at debugger of Chrome:
How can I read values of Data property of MyComponent? What am I doing wrong?
#Input Data ... decorator "receives" data from the parent component. You set it via the attribute [Data] inside the parent template. If you don't set it it will be indefined. On the other hand you have [Options] attribute that doesn't have the corresponding #Input in the child.
You can fix it like so:
<fieldset>
<my-comp #myGrid [Data]="person.data"></my-comp>
</fieldset>
where person is an array with data field in parent component.
Please read thoughtfully the documention https://angular.io/guide/template-syntax#inputs-outputs and https://angular.io/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding
And it would be better to not use reserved/too generic name like Data, Options to avoid name collisions and also camel case them.
I am trying to use *ngFor to render a list of components:
<componentx
*ngFor="let a of data"
[viewName]="a.name"
[viewQuery]="a.prop2"
[viewValues]="a.prop3"></componentx>
// ...
export class App {
public data:any[];
constructor() { this.data = [{name: 'foo', prop2: 'bar', prop3: 'hello'}]}
}
This is the component itself:
export class ComponentX {
#Input() viewName:string;
#Input() viewQuery:string;
#Input() viewValues:any[];
}
When I run this code I get the following error:
Error: Uncaught (in promise): Template parse errors:
Can't bind to 'viewName' since it isn't a known native property ("
<componentx
*ngFor="let a of data"
[ERROR ->][viewName]="a.name"
[viewQuery]="a.prop2"
[viewValues]="a.prop3"></componentx>
What am I doing wrong?
This error generally means:
1- That specific #Input doesn't exits in your component , which in your case it does.
2- You've forgotten to declare your component in the root app module.
import {ComponentX} from 'its/path';
#NgModule({
declarations:[ComponentX]
})
export class AppModule {
}
Please make sure that you did import your component -'ComponentX' in class 'App'
I have this component:
export class MyComponent {
#Input() active:boolean;
constructor() {
console.log(this.active);
}
}
You will notice that I've declared an Input that I pass in like this:
<mycomponent
[active]="1 == 1"></mycomponent>
When this loads, the log statement in the constructor logs undefined. What am I doing wrong?
#Input property bindings are first only available after the ngOnInit,
So you should do :
export class MyComponent {
#Input() active:boolean;
ngOnInit() {
console.log(this.active);
}
}
Also FYI :
From the docs :
ngOnInit
Initializes the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.
Called once, after the first ngOnChanges.
More on Life Cycle Hooks
Below is an example of how to use this.active in your HTML template:
<div *ngIf='active'>
<span [ngClass]="{'glyphicon-play': active}">
<div>
I have a component which receives an array of image objects as Input data.
export class ImageGalleryComponent {
#Input() images: Image[];
selectedImage: Image;
}
I would like when the component loads the selectedImage value be set to the first object of the images array. I have tried to do this in the OnInit lifecycle hook like this:
export class ImageGalleryComponent implements OnInit {
#Input() images: Image[];
selectedImage: Image;
ngOnInit() {
this.selectedImage = this.images[0];
}
}
this gives me an error Cannot read property '0' of undefined which means the images value isn't set on this stage. I have also tried the OnChanges hook but I'm stuck because i can't get information on how to observe changes of an array. How can I achieve the expected result?
The parent component looks like this:
#Component({
selector: 'profile-detail',
templateUrl: '...',
styleUrls: [...],
directives: [ImageGalleryComponent]
})
export class ProfileDetailComponent implements OnInit {
profile: Profile;
errorMessage: string;
images: Image[];
constructor(private profileService: ProfileService, private routeParams: RouteParams){}
ngOnInit() {
this.getProfile();
}
getProfile() {
let profileId = this.routeParams.get('id');
this.profileService.getProfile(profileId).subscribe(
profile => {
this.profile = profile;
this.images = profile.images;
for (var album of profile.albums) {
this.images = this.images.concat(album.images);
}
}, error => this.errorMessage = <any>error
);
}
}
The parent component's template has this
...
<image-gallery [images]="images"></image-gallery>
...
Input properties are populated before ngOnInit() is called. However, this assumes the parent property that feeds the input property is already populated when the child component is created.
In your scenario, this is not the case – the images data is being populated asynchronously from a service (hence an http request). Therefore, the input property will not be populated when ngOnInit() is called.
To solve your problem, when the data is returned from the server, assign a new array to the parent property. Implement ngOnChanges() in the child. ngOnChanges() will be called when Angular change detection propagates the new array value down to the child.
You can also add a setter for your images which will be called whenever the value changes and you can set your default selected image in the setter itself:
export class ImageGalleryComponent {
private _images: Image[];
#Input()
set images(value: Image[]) {
if (value) { //null check
this._images = value;
this.selectedImage = value[0]; //setting default selected image
}
}
get images(): Image[] {
return this._images;
}
selectedImage: Image;
}
You can resolve it by simply changing few things.
export class ImageGalleryComponent implements OnInit {
#Input() images: Image[];
selectedImage: Image;
ngOnChanges() {
if(this.images) {
this.selectedImage = this.images[0];
}
}
}
And as another one solution, you can simply *ngIf all template content until you get what you need from network:
...
<image-gallery *ngIf="imagesLoaded" [images]="images"></image-gallery>
...
And switch flag value in your fetching method:
getProfile() {
let profileId = this.routeParams.get('id');
this.profileService.getProfile(profileId).subscribe(
profile => {
this.profile = profile;
this.images = profile.images;
for (var album of profile.albums) {
this.images = this.images.concat(album.images);
}
this.imagesLoaded = true; /* <--- HERE*/
}, error => this.errorMessage = <any>error
);
}
In this way you will renderout child component only when parent will have all what child needs in static content. It's even more useful when you have some loaders/spinners that represent data fetching state:
...
<image-gallery *ngIf="imagesLoaded" [images]="images"></image-gallery>
<loader-spinner-whatever *ngIf="!imagesLoaded" [images]="images"></loader-spinner-whatever>
...
But short answer to your questions:
When inputs are available?
In OnInit hook
Why are not available to your child component?
They are, but at this particular point in time they were not loaded
What can I do with this?
Patiently wait to render child component utul you get data in asynchronous manner OR learn child component to deal with undefined input state