goneos
goneos

Reputation: 319

Angular Forms - What is the correct way to initialize

I have a basic form that I have built with 3 fields. Name, description and id. The data is meant to be populated by a service that calls out to a C# API that returns JSON that is then loaded to the form.

When it opens I get the correct data but I get this error. If I striped out the name input, the errors just move to the description field then the id.

enter image description here

I am still learning Angular, but have read a heap to try and fix this. I think I've got it all right, but I'm guessing it's something to do with initialisation because if I add this code before getting the data from the scenario service, the errors go.

  this.scenario = { 'id': 0,
    'name': '',
    'description': '',
    'dateCreated': '',
    'createdBy': '',
    'dateModified': '',
    'modifiedBy': '',
    'active': true };

So what is the correct way to code this so my interface is initialised without having to hard code it?

test.component.html

<h1>
  Edit Scenario - {{ name.value }}
</h1>
<div class='panel-body'>
<form novalidate 
      #scenarioForm="ngForm" 
      (ngSubmit)="saveForm()">
    <div class='row'>
      <div class='col-md-1'><label for="scenarioName" class="control-label">Name</label></div>
      <div class='col-md-6'>
        <div class="form-group" [class.has-error]="name.invalid && name.touched">
        <input class="form-control" 
               #name="ngModel" 
               name="scenarioName" 
               type="text" maxlength="50" placeholder="Scenario name" required 
               [(ngModel)]="scenario.name">
        </div>
      </div>
    </div>
    <div class='row'>
      <div class='col-md-1'><label for="descption">Descption</label></div>
      <div class='col-md-6'>
        <div class="form-group" [class.has-error]="description.invalid && description.touched">
          <textarea #description="ngModel" 
                    ngControl="description" 
                    class="form-control" 
                    rows="4" maxlength="500" 
                    placeholder="What is this scenario for?" 
                    required 
                    name="description" 
                    [(ngModel)]="scenario.description"></textarea>
           <div *ngIf="description.invalid && description.dirty" class="alert alert-danger">
              Description is required, and must be at least blah blah... 
          </div>
        </div>
      </div>
    </div>
  <div class='panel-footer'>
    <button class="btn btn-default" type="submit">Save</button>
  </div>
  <br><br>
  <input #id type="text" name="id" [(ngModel)]="scenario.id" #id="ngModel">
</form>
<div id="debugging">
  <br>
  Valid? {{scenarioForm.valid}}<br>
  Model: {{ scenario | json }} <br>
  Model: {{ result | json }}<br>
  Form: {{ scenarioForm.value | json }}
  <br>
</div>

test.component.ts

import { Component, OnInit, NgModule } from '@angular/core';
import { SharedModule } from './../shared/shared.module';
import { FormsModule, NgForm, FormGroup } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { ScenarioService } from './scenario.service';
import { IScenario } from './scenario';
import { Router, ActivatedRoute } from '@angular/router';

 @NgModule({
   imports: [
    FormsModule
   ],
   exports: [FormsModule]
 })

@Component({
  selector: 'app-scenario-form',
  templateUrl: './test.component.html',
})

export class TestComponent implements OnInit {
  // tslint:disable-next-line:no-inferrable-types
  private pageTitle: string = 'New Scenario';
  scenario: IScenario;
  result: IScenario;

  constructor(private _scenarioService: ScenarioService,
              private _route: ActivatedRoute) {}

  ngOnInit() {
    const scenarioId = this._route.snapshot.paramMap.get('id');
    if (scenarioId) {
      this._scenarioService.getScenario(+scenarioId).subscribe(
        scenario => this.scenario = scenario);
    }
  }
}

Upvotes: 2

Views: 1219

Answers (1)

Sajeetharan
Sajeetharan

Reputation: 222582

Since you are getting the response from API asynchronously, you need to handle the null initially, you can do that with safe navigation operator

 <input #id type="text" name="id" [(ngModel)]="scenario?.id" #id="ngModel"> 

same applies for all your fields.

As you mentioned above, other way is to fix via initializing the object Scenario

Since you need to accomplish two way data binding, you need to initialize the object by creating an instance

scenario: IScenarioUpdate = {modifiedBy: ''}

Upvotes: 1

Related Questions