Search code examples
angulartypescriptangular-reactive-forms

How to pass strictly typed reactive form data to HttpClient?


I have a FormGroup and want to map it to an interface to ensure type safety. Below are code snippets.

1.seller.ts

export interface Seller {
    id: Number;
    username: String;
    email: String;
    password: String;
}

2.seller-auth.component.html

<div class="seller-auth">
    <div class="signup">
        <h1>Register Seller</h1>
        <form class="common-form" [formGroup]="sellerRegistrationForm" (ngSubmit)="registerSeller()">
            <input class="form-input" type="text" placeholder="Enter username" formControlName="username">
            <input class="form-input" type="text" placeholder="Enter Email" formControlName="email">
            <input class="form-input" type="text" placeholder="Enter Password" formControlName="password">
            <button class="form-button">Register</button>
        </form>
    </div>
</div>

3.seller-auth.component.ts

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { SellerRegistrationRequest } from 'src/app/models/seller-registration-request';
import { SellerService } from 'src/app/services/seller.service';

@Component({
  selector: 'app-seller-auth',
  templateUrl: './seller-auth.component.html',
  styleUrls: ['./seller-auth.component.css']
})
export class SellerAuthComponent implements OnInit {

  constructor(private sellerService: SellerService) { }

  ngOnInit(): void {
  }

  sellerRegistrationForm = new FormGroup(
    {
      username: new FormControl<string>("", { nonNullable: true }),
      email: new FormControl<string>("", { nonNullable: true }),
      password: new FormControl<string>("", { nonNullable: true })
    }
  );

  registerSeller(): void {
    this.sellerService.registerSeller(this.sellerRegistrationForm.value).subscribe(
      sellerRegistrationResponse => {
        if (sellerRegistrationResponse.sellerAlreadyExists) {
          alert("seller already exists with email : " + sellerRegistrationResponse.sellerDetails.email);
        } else {
          console.log(sellerRegistrationResponse.sellerDetails);
        }
      }
    );
  }

}

4.seller.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { SellerRegistrationRequest } from '../models/seller-registration-request';
import { SellerRegistrationResponse } from '../models/seller-registration-response';

@Injectable({
  providedIn: 'root'
})
export class SellerService {

  constructor(private httpClient: HttpClient) { }

  registerSeller(sellerRegistrationRequest : SellerRegistrationRequest) {
    return this.httpClient.post<SellerRegistrationResponse>("http://localhost:8080/food-kart/api/v1/seller/register", sellerRegistrationRequest);
  }
}

Line...this.sellerService.registerSeller(this.sellerRegistrationForm.value).subscribe( is giving me the below error.

Argument of type 'Partial<{ username: string; email: string; password: string; }>' is not assignable to parameter of type 'SellerRegistrationRequest'.
  Types of property 'username' are incompatible.
    Type 'string | undefined' is not assignable to type 'string'.
      Type 'undefined' is not assignable to type 'string'.ts(2345)

Please provide your input so that I can pass the SellerRegistrationRequest type to my registerSeller method.


Solution

  • You see this problem because form.value will return the form shape with all properties being optional:

    {
      username? : string;
      email?    : string;
      password? : string;
    }
    

    Of course, this does not satisfy the type expected by your SellerRegistrationRequest interface, which requires all of these fields.

    According to the Angular Docs, you can use form.getRawValue() instead of form.value:

    this.sellerService.registerSeller(this.sellerRegistrationForm.getRawValue()).subscribe(...);