Angular Reactive Forms: how to pre load form inputs with information

5.9k Views Asked by At

I am trying to create a forum within my website. After struggling to implement using template driven forms, I decided to switch to Reactive forms. I have been switching between the two approaches throughout my site and realize there is one draw back to reactive forms which leads me to choose template driven forms, and I suspect that there is a workaround that is worth figuring out. The problem I have is this, when a user wants to edit something they have aleady created, I use the same form used for creating, but with the document ID appended to the route, like so:

localhost:4200/create-thread/some-thread-uid

when this route is followed, it loads the create-thread component but preloads the data already provided into the appropriate form-controls. This is accomplished by using two-way-binding to attach the thread object to ngModel for the form in question. like so:

<!-- <form #finished="ngForm" (ngSubmit)="save(finished.value)">

<div class="form-group">
    <label for="author">Author of thread:</label>
        <input
            #author="ngModel"
            [(ngModel)]="thread.author"
            name="author"
            id="author"
            class="form-control"
            required
            type="text">                
    </div>
</form>

My question is, is there a way to accomplish this with Reactive forms (load the already provided info from an object stored in cloud db into input fields for editing) ? I have tried a few ways and could not figure it out, including injecting the 'thread.author' to the appropriate field in the form builder. any help is appreciated.

2

There are 2 best solutions below

1
On BEST ANSWER

There are two ways to do what you're saying, one way involves populating the fields when the form is created, and the other involves adding the values after the form is created. The former seems to fit what you want to do here, while the latter is good for building a form out of calculated values or out of an amalgamation of service calls.

Before doing anything with reactive forms make sure you've imported the ReactiveFormsModule in your module, like so:

  imports: [
    ReactiveFormsModule
  ]

The first way Creating a form that contains values (i.e. "Edit")

First, create the form in the component.ts, like so:

export class YourEditComponent implements OnInit {

  // First you'll specify your form variable
  form: FormGroup;

  // In your constructor, be sure to inject FormBuilder
  constructor (private formBuilder: FormBuilder) {}

  // I like to create the form in OnInit, but for testing
  // purposes I prefer to call a function that creates the form,
  // rather than doing the logic in this function
  ngOnInit(): void {
    this.createEditFormGroup();
  }

  // Here's where you create your form. I assume you'll
  // be getting your form data object from a service, but
  // for the sake of demonstration I'll create the object here
  createEditFormGroup(): void {
    // Here you'll have the object already, so don't do this
    const thread = {
      author: 'breadman0',
      email: '[email protected]'
    }

    // Now simply create the form, passing this object (in this
    // case, the object "thread")
    this.form = this.formBuilder.group(thread);
  }

  // Now when you save, you don't need to pass in the value, you
  // can just access the form's value
  save(): void {
    console.log(this.form.value);
  }

}

In your component.html, you'll just need to add some directives and handle the "submit" event. See your html below, modified to work:

<form [formGroup]="form" (ngSubmit)="save()">
    <div class="form-group">
        <label for="author">Author of thread:</label>
        <input
            formControlName="author"
            class="form-control"
            required
            type="text">                
    </div>
    <div class="form-group">
        <label for="email">Author's email:</label>
        <input
            formControlName="email"
            class="form-control"
            required
            type="text">                
    </div>
</form>

Note that "required" isn't really very helpful, as that can be modified in devtools. You'd be better off setting the fields as required in the form.

The second way Populating an existing form with values

This way is very similar, except we just create the form with a new instance of whatever object we're using

this.form = this.formBuilder.group({
  author: '',
  email: ''
});

//or

this.form = this.formBuilder.group(new Thread());

And then later we call patchValue:

// This can be done for any field in the form. I choose "author" here
this.form.patchValue({author: 'anonymous'});
1
On

It appears that my problem actually has nothing to do with what i anticipated. For some reason, when I use reactive forms, the observable 'thread' that I am fetching from the data store is undefined. I am going to create a second question with source code in the hopes someone might be able to spot if I am doing something wrong. As far as I can tell, everything form-wise looks good with either template driven or reactive forms, and i am unsure why this would effect observable loading or not loading any way. Likewise, the way i subscribe to and access the observable is the same with either implementation, so I am very stumped as to what the true problem is.