angularangular-reactive-formsangular-template-form

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


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.


Solution

  • 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: 'breadman0@gmail.com'
        }
    
        // 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'});