An error is being thrown when trying to access a property of an initialized complex object in ngOnInit

Passing a parameter to a child component in the parent component:

<app-conf-name
    [inputName]='person.name'
    (updatedNameEvent)='updateName($event)'>
</app-conf-name>

Implemented within the TypeScript file of the component

@Input() inputName: Names;
@Output() updatedNameEvent: EventEmitter<Names> = new EventEmitter();
constructor() { }
editName: Names = new Names();

ngOnInit() {
  this.editName = this.inputName;
}

The Names class in the code sets default values for various fields like correctPerson.

export class Names {
  indivId: number;
  firstName: string;
  prefName: string;
  lastName: string;
  suffix: string;
  updated: boolean = false;
  correctPerson: boolean = false;
  correctAsIs: boolean = false;
  correctionDate: Date;
  addedDate: Date;
}

Encountering an error message:

ERROR TypeError: Cannot read property 'correctPerson' of undefined
    at Object.View_ConfNameComponent_0._co [as updateDirectives] (ConfNameComponent.html:9)
    at Object.debugUpdateDirectives [as updateDirectives] (core.es5.js:13056)
    at checkAndUpdateView (core.es5.js:12236)
    at callViewAction (core.es5.js:12601)
    at execComponentViewsAction (core.es5.js:12533)
    at checkAndUpdateView (core.es5.js:12242)
    at callViewAction (core.es5.js:12601)
    at execComponentViewsAction (core.es5.js:12533)
    at checkAndUpdateView (core.es5.js:12242)
    at callViewAction (core.es5.js:12601)

The object person is initialized through a service called in the parent component.

Parent Component

person: Person = new Person();
constructor(private peopleService: PeopleService) { }
state: string = 'one';
ngOnInit() {
    this.peopleService.getPersonById(this.Id).subscribe((data)=>{
      this.person = data;
      console.log(this.person);
      this.person.name.correctPerson = false;
      this.person.name.correctAsIs = false;
      this.person.email.correctAsIs = false;
      this.person.phone.correctAsIs = false;
      this.person.address.correctAsIs = false;
    });
}

Note that I've re-initialized the property causing the error with a new assignment.

The original code for the child component used the input variable as the working variable.

Despite multiple attempts to resolve issues and debug, the final child component remains problematic even after adding ngIf statements for validation.

For further details, refer to the complete GitHub Repository

Answer №1

It's recommended not to access @Input() parameters in the OnInit lifecycle hook, but rather in the OnChanges hook. Since it is an Observable, there is no guarantee that it will be resolved by the time OnInit is triggered.

For more information, you can refer to this link: Angular 2/4 @Input only moves intrinsics - not lists or objects

Answer №2

Here, you are engaging in lazy loading of data and initializing that data within the parent component's ngOnInit lifecycle hook. Consequently, it is necessary to consider the possibility of the child component's @Input() binding being undefined.

The reason for this potential scenario lies in the timing of property assignments with @ViewChild occurring before ngOnInit, while @ContentChild properties are set post initial rendering. As a result, the parent component's template is processed prior to the data being fully loaded, potentially resulting in passing undefined values to the child's @Input() binding.

Please note: I may need my morning coffee to verify the accuracy of the above explanation.

In any case, it is advisable to always handle cases of missing bindings within components.

@Input() inputName: Names;
@Output() updatedNameEvent: EventEmitter<Names> = new EventEmitter();
constructor() { }
editName: Names;

ngOnInit() {
  this.updateEditName();
}

ngOnChanges(changes: SimpleChanges) {
   if('inputName' in changes) {
       this.updateEditName();
   }
}

private updateEditName() {
      this.editName = this.inputName || new Names();
}

Answer №3

One issue with the code above is that the server call is conducted asynchronously. This signifies that when the child component is initialized, the name may not yet be defined.

The data flow unfolds in the following manner:

  • The parent component is first created with an empty People object (thus preventing any issues with calling people.name since people is never undefined).
  • During initialization, the parent component initiates the asynchronous call.
  • Following initialization, the child component is instantiated without waiting for the asynchronous call to complete.
  • Subsequently, the child component encounters an error because the parent object's name property (people) is still undefined (most likely accessed within the child component's HTML).

To address this problem, there are two potential solutions. The first involves adding an ngIf directive to the child component tag as shown below:

<app-conf-name 
    *ngIf='person.name' [inputName]='person.name'
    (updatedNameEvent)='updateName($event)'>
</app-conf-name>

The second solution entails utilizing a resolve guard, which resolves the data from the service before the parent component is initialized.

In the route definition:

{
  path: 'your-path', component: YourParentComponent,
  resolve: {
    person: PersonResolveGuard
  }
}

Your resolve guard will resemble this:

@Injectable()
export class BlockingResolveGuard implements Resolve<Person> {

    constructor(private peopleService: PeopleService) {}

    resolve(route: ActivatedRouteSnapshot):Observable<Person> {
       // Call to service
    }
}

Within your parent component, inject the current ActivatedRoute:

constructor(private route:ActivatedRoute, private peopleService: PeopleService) { }

In the ngOnInit() method:

ngOnInit() {
    this.route.data.resolve((data:{person:Person}) => {
       this.person = data.person;
    }
}

This Angular documentation provides a detailed explanation on how resolve guards and other guards function https://angular.io/guide/router#resolve-guard

It is important to note that with the second solution, you must resolve the ID of the person for whom you are retrieving data from the route parameters using ActivatedRouteSnapshot in the resolve guard.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the method for declaring personalized environment variables in Angular?

I am looking to utilize a different environment variable called test. The purpose behind this is to set up an http interceptor for my integration tests. This interceptor should specifically return a mocked response with predefined data and is intended for ...

There was an issue retrieving the metadata for the bootstrap package using the git+ssh protocol

While attempting to install angular devkit and other dependencies using npm install, I encountered the following error message: Could not retrieve metadata for bootstrap@git+ssh://example@example.com/gios/Web-Standards-Bootstrap.git#05a343dddce91dd157702 ...

The Angular project was functioning properly when tested locally, but encountered an error in the Quill Editor during the building process

I have encountered an issue when deploying my Angular 8 + Quill project. Everything works fine locally with 'ng serve', but upon deployment, I am facing the following error. Despite trying various solutions like updating Angular or deleting &apos ...

Troubleshooting the issue of having multiple menu items in Material UI

Every time I attempt to add the Menu component multiple times, I encounter an issue with the popup list displaying incorrectly. To demonstrate this problem, you can view it through the link provided on codesandbox below. I have included data-id attributes ...

Enhance the window.top functionality with TypeScript, enhance Visual Studio Code suggestions

In the previous project, an iframe was used for development. The top-level window contains several private attributes, and other pages interact through it. I now need to enhance the type of top in the iframe page using TypeScript. How should I modify the c ...

Steps for executing a single test across multiple URLs using Playwright

My goal is to run a test for over 1000 URLs as quickly as possible. However, I am encountering a timeout error when the number of URLs exceeds 10. It seems like the tests are running sequentially, causing delays. Is there a way to run these tests in parall ...

Should I call `complete()` on the takeUntil Subject within the ngOnDestroy method?

To prevent memory leaks caused by Observables inside Components, I always use the takeUntil() operator before subscribing. Here is an example of how I implement it in my components: private stop$ = new Subject(); ngOnInit(): void { this.http .get( ...

Angular encountering issues with loading external JavaScript files due to the error: ENOENT - indicating that the specified file or directory does

I am attempting to incorporate a bootstrap template into Angular. The template requires some external JavaScript and CSS files that need to be linked. I have placed these files in the assets folder and referenced them in the styles and scripts arrays of an ...

Tips for enhancing functionality with dependencies in Angular 2

I am working with a ParentService that has dependencies like: @Injectable() export class ParentService{ constructor(private http:Http, private customService:CustomService){} } Now, I want to create a ChildService that extends the ParentService: @Injec ...

The Angular compiler encounters an error when working with jsonwebtoken

Having some trouble with adding jsonwebtoken to my code. VS Code seems to think the code is fine, but the compiler keeps failing Any ideas on why this might be happening? Thanks for any help! Here's a snippet of my code: this.http .po ...

Struggling to successfully pass a function as an argument to the setTimeout method within an array in node.js using TypeScript

Here is an example that successfully demonstrates a function being called using setTimeout: function displayMessage(msg: string){ console.log(msg); } setTimeout(displayMessage, 1000, ["Hi!"]; After one second, it will print out "Hi!" to the console. ...

Issue: The specified entry point "@angular/flex-layout" is lacking necessary dependencies

Whenever I attempt to launch my Angular app, I encounter the following error message: Error: The target entry-point "@angular/flex-layout" is missing dependencies: - ./flex-offset/flex-offset at TargetedEntryPointFinder.findEntryPoints (file:///C:/Users/r ...

Modifying elements in an array using iteration in typescript

I'm trying to figure out how to iterate over an array in TypeScript and modify the iterator if necessary. The TypeScript logic I have so far looks like this: for (let list_item of list) { if (list_item matches condition) { modify(list_ite ...

Encountering issues with 'ng test' command following the update to @Angular/cli version 1.1.0

Angular version that was recently installed: List of errors encountered while running ng test: Development dependencies: "devDependencies": { "@angular/cli": "^1.1.0", "@angular/compiler-cli": "^4.1.3", "@types/jasmine": "2.5.38", "@type ...

How can I simulate a callback function that was not tested?

Currently experimenting with the method below: startScriptLoad(): void { const documentDefaultView = this.getDocumentDefaultView(); if (documentDefaultView) { const twitterData: ICourseContentElementEmbedTweetWidgetData = this.getTwitterWid ...

Is there a way in Angular 13 to incorporate specific conditions for including HTTP_INTERCEPTORS depending on the location of the HTTP call being made?

Currently, I'm delving into Angular13 and ngrx 13. When it comes to making API calls, I've been implementing them from app.module.ts providers: [ { provide: HTTP_INTERCEPTORS, useClass: MyHttpInterceptor, mult ...

What exactly is the meaning of `react-text`?

Can you explain the functionality of react-text? Although it is not present in the code, it shows up in the HTML after rendering. https://i.sstatic.net/s650q.png ...

I'm interested in utilizing Angular 2 to parse a CSV file and showcase the data in a table

Hello everyone, I am new to Angular 2/4 and I am trying to parse a CSV file and display the data in a table on a webpage. I have attempted some code, but the data only gets displayed in the console, not on the webpage itself. Can anyone help me out? hand ...

Alert: TypeScript Linter Warning

When I execute the npm lint command in Circle CI, a warning message is displayed. The following rules specified in the configuration could not be found: templates-use-public no-access-missing-member invoke-injectable template-to-ng-templa ...

TS - The 'new' keyword can only be used with a void function

I'm encountering a strange TypeScript error: "Only a void function can be called with the 'new' keyword." What in the world? https://i.sstatic.net/bKAUT.png The constructor function looks like this: function Suman(obj: ISumanInputs): ...