0

I have a Spring entity, Agent, which contains an Agency. When adding a new agent I have to also pass the agency he works at, but as an object. The backend code works just fine, but when I try to add an agent using an Angular form, I haven't managed to find how to pass the agency object to it. I'm only saving the id of an agency in the database, but I have to specify the whole object when creating the entity. This is the agent entity:

@Entity
@Builder
@Getter
@Setter
public class Agent {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(nullable = false)
    private String firstName;

    @Column(nullable = false)
    private String lastName;

    private String phone;

    @Column(nullable = false)
    private String email;

    @ManyToOne
    @JoinColumn(name = "agency_id", referencedColumnName = "id", nullable = false)
    @JsonIgnore
    private Agency agency;
}

This is the DTO:


@Getter
@Setter
@Builder
public class AgentDto {
    private Integer id;

    @NotNull
    private String firstName;

    @NotNull  
    private String lastName;

    private String phone;

    @NotNull
    @Email
    private String email;

    private Agency agency;
}

This is the method to save an agent, located in the AgentService (repo is the injection of a JpaRepository, so I'm using the standard save method):

 public AgentDto save(AgentDto agentDto) {
        Agent agent = mapper.mapToEntity(agentDto);
        Agent agentSaved = repo.save(agent);

        return mapper.mapToDto(agentSaved);
    }

The controller only contains a method call:

@PostMapping
    public AgentDto save(@RequestBody AgentDto agentDto) {
        return service.save(agentDto);
    }

Now, getting to Angular, this is my agent model:

import { Agency } from "./agency";

export interface Agent {
    id: number;
    firstName: string;
    lastName: string;
    email: string;
    phone: string;
    agency: Agency;
}

This is the method used in the agent service:

addAgent(model: any) {
    return this.http.post(this.baseUrl + 'agents', model);
  }

This is the typescript in my "add-agent" component:

import { Location } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AgentService } from '../_services/agent.service';

@Component({
  selector: 'app-create-agent',
  templateUrl: './create-agent.component.html',
  styleUrls: ['./create-agent.component.css']
})
export class CreateAgentComponent implements OnInit {
  model: any = {};
  agencies: Agency[] = [];

  constructor(private agentService: AgentService, private router: Router, private _location: Location) { }

  ngOnInit(): void { this.loadAgencies(); }

loadAgencies() {
    this.agencyService.loadAgencies().subscribe(
      agencies => {
        this.agencies = agencies;
      }
    );
  }
  

  createAgent() {
    this.agentService.addAgent(this.model).subscribe(() => {
      this._location.back();
    })
  }

}

And lastly, this is the HTML:

<form (ngSubmit)="createAgent()" autocomplete="off">

    <input 
      name="firstName"
      [(ngModel)]="model.firstName"
      class="form-control mt-3" 
      type="text" 
      placeholder="First name"
    >

    <input 
      name="lastName"
      [(ngModel)]="model.lastName"
      class="form-control mt-3" 
      type="text" 
      placeholder="Last name"
    >

   <input 
      name="email"
      [(ngModel)]="model.email"
      class="form-control mt-3" 
      type="text" 
      placeholder="E-mail"
    >

    <input 
      name="phone"
      [(ngModel)]="model.phone"
      class="form-control mt-3" 
      type="text" 
      placeholder="Phone"
    >

    <select name="agency" [(ngModel)]="model.agency" >
     <option *ngFor="let agency of agencies" [ngValue]="agency">{{ agency.id }}</option>
    </select>

    <button class="btn btn-success btn-sm mt-3" style="margin-right: 2%; box-shadow: 1px 1px grey;" type="submit">Add</button>

  </form>

How can I pass the agency in a way that its id will be saved in the database? I've tried saving it as a text and making the given id match an existing agency. I've also tried retrieving the agencies from the db and passing them in a tag, but it still didn't work. What is the right way to do this?

Alessia
  • 35
  • 1
  • 8

1 Answers1

1

The first 2 possible options that come to mind:


  1. Serialize the Agency object

As you are already doing, you bring in all the 'agencies', and once you know which agency is correct for the selected item, you put it as the field value. But instead of putting the field 'agency' of the DTO as type "object" 'Agency', you put it as string, and before passing it you "stringify" it:

// Serialize (convert to string) appropriateAgencyForThatAgent:Agency

let agencyString:string = JSON.stringify(appropriateAgencyForThatAgent);

In the back controller that processes the request (AgentDto save), you process that field again, and you "parse" from string to 'Agency': Read more about to pase a json in Java


  1. Pass the id Agency and process it in the back

Instead of putting the field 'agency' of the DTO as type "object" 'Agency', you put it as number, and you pass to the back the id of the 'Agency'.

In the controller of the back that processes the request (AgentDto save), you process it, with the id of the agency you look for it, and you add it.

  • Thank you! I will try these methods out. Do you think it would be easier to select just an id (instead of a whole object) and then set that id as the id of the agency? – Alessia Dec 19 '21 at 20:30
  • Yes, I do. Try it and let us know if you finally got it right, good luck! – Juan Vicente Berzosa Tejero Dec 19 '21 at 22:59
  • Ok, so I found a property [ngValue] that I can set in the option tag and I'm only chosing the id of an agency, which is mapped to the whole entity, I have updated the code in my question, and now it works – Alessia Dec 20 '21 at 13:17