简体   繁体   中英

Why is @Input property undefined in ngOnInit?

In this component I use a service to get some data from an API

@Component({
  selector: 'app-question-presenter',
  template: `
    <div class="question-presenter">
      <app-question [question]="question"></app-question>
    </div>
  `
})
export class QuestionPresenterComponent implements OnInit {
  private requestId: string;
  private userId: string;
  private question: Question;
  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private questionService: QuestionService
  ) { }

  ngOnInit() {
    this.route.params.pipe(
      switchMap((params: Params) => {
        return this.questionService.getQuestion(params.requestId);
      })
    ).subscribe((data: any) => this.question = data.data[0].question);
  }
}

In another component (the one that provides app-question ) I am trying to access question but getting undefined in the console.

@Component({
  selector: 'app-question',
  templateUrl: './question.component.html',
  styleUrls: ['./question.component.scss']
})
export class QuestionComponent implements OnInit {

  @Input()
  question: Question;

  constructor() {}
  ngOnInit() {
    console.log(this.question);
  }

I know that the data is making it to the template (because it renders as expected) but I thought that the Input would be loaded before calling ngOnInit - what am I doing wrong?

You can delay creation of the component until the value is ready.

<app-question *ngIf="question" [question]="question"></app-question>

You can also use an observable

 this.question$ = this.route.params.pipe(
      switchMap(({requestId) => this.questionService.getQuestion(requestId))
 )

Then in the template:

<app-question *ngIf="question$ | async as question" [question]="question"></app-question>

Looks like you have a race condition going on. If you're worried about it displaying only on the template, you can add an Async Pipe to the property you're binding to. It will only show once the property has a value. This will subscribe to the question property and unsubscribe appropriately.

html

<div>
 <p>{{question | async }}</p>
</div>

If you're looking to use the variable in the component itself, I would subscribe to the question property in your QuestionPresenterComponent.

To make it even cleaner, I would suggest putting that variable in a shared service, along with your API call, that way all of the subscribers would inherit the updates accordingly.

@Component({
  selector: 'app-question',
  templateUrl: './question.component.html',
  styleUrls: ['./question.component.scss']
})
export class QuestionComponent implements OnInit {

  @Input()
  question: Question;

  constructor(private sharedService: SharedService) {}
  ngOnInit() {
    this.sharedService.question.subscribe(question => {
        console.log(question)
    });
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM