Leonardo Moreira
Leonardo Moreira

Reputation: 39

HTTP PUT is creating instead of updating in Angular app

I'm working in a simple CRUD app with Angular, and when I submit my form updating an element of the table, it creates another element instead of update the same. What is wrong?

Service Class:

export class EquipmentsService {
  url = "https://localhost:5001/api/Equipment";
  constructor(private http: HttpClient) { }

   Getall(): Observable<Equipments[]>{
    return this.http.get<Equipments[]>(this.url);
  }


  GetWithId(EquipmentsID: number): Observable<Equipments>
  {
    const Url = `${this.url}/${EquipmentsID}`;
    return this.http.get<Equipments>(Url);
  }
  PostEquipment(equipment: Equipments ): Observable<any>{
    return this.http.post<Equipments>(this.url, equipment, httpOptions);
  }
  PutEquipment(equipment: Equipments): Observable<any>{
    return this.http.put<Equipments>(this.url, equipment,httpOptions);
  }

  DeleteEquipment(equipmentId: number): Observable<any>
  {
    const Url = `${this.url}/${equipmentId}`;
    return this.http.delete<Number>(Url, httpOptions);
  }
}

Dialog Component:

export class DialogFormUpdateComponent implements OnInit {

  public titleForm!: string;
  formG!: FormGroup;
  equip!: Equipments;
  id!: number;


  constructor(public dialogRef: MatDialogRef<DialogFormUpdateComponent>, private fb: FormBuilder,
    private EquipmentService: EquipmentsService) {

  }

  ngOnInit(): void {

      console.log(this.id); // It's receiving Id value

      this.EquipmentService.GetWithId(this.id).subscribe(result => {
        this.titleForm = "Update Equipment";
        this.formG = this.fb.group({
          name: [result.name, [Validators.required]],
          serialNumber: [result.serialNumber, [Validators.required]],
          voltage: [result.voltage, [Validators.required]],
          electricCurrent: [result.electricCurrent, [Validators.required]],
          oil: [result.oil, [Validators.required]],
          date: [result.date, [Validators.required]],
        });
      });

  }

  public SendFormUpdate(): void {

    let newDate: moment.Moment = moment.utc(this.formG.value.date).local();
    this.formG.value.date = newDate.format("YYYY-MM-DD");
    const equipment: Equipments = this.formG.value;

    this.EquipmentService.PutEquipment(equipment).subscribe(result => {

      alert("Equipment was updated with success");
      this.formG.reset();
      this.dialogRef.close();
    })
  }

  Cancel() {

    this.dialogRef.close();
    this.formG.reset();

  }
}

Equipment Component:

export class EquipmentsComponent implements OnInit {
  ELEMENT_DATA!: Equipments[];
  form: any;
  titleForm!: string;
  displayedColumns: string[] = ['name', 'serialNumber', 'voltage', 'electricCurrent', 'oil', 'date', 'actions'];
  @Output() equipID: EventEmitter<number>= new EventEmitter<number>();


  public dataSource = new MatTableDataSource<Equipments>(this.ELEMENT_DATA);

  constructor(private EquipmentService: EquipmentsService, public dialog: MatDialog,
    public DialogUpate: MatDialog, public DialogComponentUpdate: DialogFormUpdateComponent) { }

  ngOnInit(): void {

    //getall on start
      this.EquipmentService.Getall().subscribe(result => {
      this.dataSource.data = result as Equipments[];
    });
  }

  //Open Create Form
  NewEquipemnt(): void {
    const dialogRef = this.dialog.open(DialogFormComponent,{
      minWidth: '300px', disableClose: true

    });

    dialogRef.afterClosed().subscribe(result => {
      console.log('The dialog was closed');
      //get all
      this.EquipmentService.Getall().subscribe(result => {
        this.dataSource.data = result as Equipments[];
        });
    });
  }

  //Open Update Form
  public UpdateEquipment(EquipId:  number): void
  {

    const dialogRef = this.DialogUpate.open(DialogFormUpdateComponent,{
      minWidth: '300px', disableClose: true,
    });

    dialogRef.componentInstance.id = EquipId;
    dialogRef.afterClosed().subscribe(result => {
      console.log('The dialog was closed');
        //get all
        this.EquipmentService.Getall().subscribe(result => {
        this.dataSource.data = result as Equipments[];
        });
    });

  }


}

My update button get a data of element will be updated, but when I click in submit is created other element in my DB

Equipment Controller:

namespace TreeApi.Controllers
{
    [ApiController]
    [Route("Api/[Controller]")]
    public class EquipmentController : ControllerBase
    {
        private readonly ContextEquipment _ContextEquipment;

        public EquipmentController (ContextEquipment context)
        {
            _ContextEquipment = context;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<Equipments>>> GetAllAsync()
        {
         return await _ContextEquipment.Equipment.ToListAsync();
        }

        [HttpGet("{EquipmentsID}")]
        public async Task<ActionResult<Equipments>> GetEquipmentAsync(int EquipmentsID)
        {
            Equipments equipment = await _ContextEquipment.Equipment.FindAsync(EquipmentsID);

            if(equipment == null)
            {
                return NotFound();
            }

            return equipment;
        }

        [HttpPost]
        public async Task<ActionResult<Equipments>> PostEquipmentAsync(Equipments equipments)
        {   
            await _ContextEquipment.Equipment.AddAsync(equipments);
            await _ContextEquipment.SaveChangesAsync();

            return Ok();
        }
        [HttpPut]
        public async Task<ActionResult> PutEquipmentAsync(Equipments equipments)
        {
            _ContextEquipment.Equipment.Update(equipments);
            await _ContextEquipment.SaveChangesAsync();

            return Ok();

        }

        [HttpDelete("{EquipmentsID}")]
        public async Task<ActionResult> DeleteEquipmentAsync(int EquipmentsID)
        {
           Equipments equipment = await _ContextEquipment.Equipment.FindAsync(EquipmentsID);
            _ContextEquipment.Remove(equipment);
            await _ContextEquipment.SaveChangesAsync();

            return Ok();
        }
    }
}

Startup:

namespace TreeApi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    
        public IConfiguration Configuration { get; }
    
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddDbContext<ContextEquipment>(options => options.UseSqlServer
                (Configuration.GetConnectionString("ConnectionDB")));
            services.AddCors();
        }
    
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
    
            app.UseHttpsRedirection();
    
            app.UseRouting();
            app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
    
            app.UseAuthorization();
    
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

Upvotes: 1

Views: 77

Answers (1)

Yong Shun
Yong Shun

Reputation: 51285

Concern

In your SendFormUpdate method, you missed out to assign id which is your primary key for the equipment.

DialogFormUpdateComponent

public SendFormUpdate(): void {

    let newDate: moment.Moment = moment.utc(this.formG.value.date).local();
    this.formG.value.date = newDate.format("YYYY-MM-DD");
    const equipment: Equipments = this.formG.value;

    this.EquipmentService.PutEquipment(equipment).subscribe(result => {

      alert("Equipment was updated with success");
      this.formG.reset();
      this.dialogRef.close();
    })
}

According to DbContext.Update(Object) method,

For entity types with generated keys if an entity has its primary key value set then it will be tracked in the Modified state.

If the primary key value is not set then it will be tracked in the Added state. This helps ensure new entities will be inserted, while existing entities will be updated.

An entity is considered to have its primary key value set if the primary key property is set to anything other than the CLR default for the property type.

Thus, new Equipment record will be created instead of update the existing record.


Solution

Make sure that you need to assign id (primary key) with value to equipment to solve the concern above.

DialogFormUpdateComponent

public SendFormUpdate(): void {
    ...
    
    const equipment: Equipments = this.formG.value;
    equipment.id = this.id;

    ...
}

Upvotes: 2

Related Questions