1
votes

Ember duplicates models on save. I have two models

**bill**

export default class BillModel extends Model {
    @attr("string", { defaultValue: "", uppercase: true })
    vehicleNumber;

    @attr("string", { defaultValue: "" })
    customerName;

    @attr("number", { defaultValue: "" })
    customerMobileNumber;

    @attr("date")
    createdOn;

    @hasMany("bill-item")
    billItems;

    @attr("number", { defaultValue: 0 })
    total;

    @computed(
        "[email protected]"
    )
    get computedTotal() {
        let total = 0;
        this.billItems.forEach((item) => {
            if (item.cost)
                total += parseFloat(item.cost);
        });
        return parseFloat(total);
    }
}


**bill-item**

export default class BillItemModel extends Model {
    @attr("string", { defaultValue: "" })
    itemName;

    @attr("number", { defaultValue: null })
    cost;
}

Then I create a new record of the "bill" model and create a "bill-item" record and add that in the billItems property of the bill model.

let bill = this.store.createRecord("bill");
bill.billItems.addObject(this.store.createRecord("bill-item"));

then when I use the save() the "bill" model, I now have two records in the billItems property. One with id returned from the server response and one with But I should have only one. Below is the server response.

{
  "vehicleNumber": "1231",
  "customerName": "123",
  "customerMobileNumber": 1231232312,
  "createdOn": null,
  "total": 23123,
  "billItems": [
    {
      "itemName": "1231",
      "cost": 23123,
      "id": "9510"
    }
  ],
  "id": 467
}

Why does the ember store now have 2 records of "bill-item" model when I tried to save only one? The adapter used for the models are RESTAdapter and serializer is JSONSerializer.

Ember inspector attachment for reference

2
I think you have two records because you createRecord twice. I don’t think you need the second one at all. - Buck Doyle
No. I've created a bill-item record only once using createdRecord(). If I shouldn't be using the second createRecord, then how am I supposed to add bill-item records to the bill model? - r4M
@r4M you can still use my solution - Michał Habigier
@MichałHabigier Yes I can use yours but its more like a hack. I'm looking to find out the exact reason for this behaviour and to see if this a bug or if I'm doing anything that is not recommended. - r4M

2 Answers

3
votes

Save the billItem first.

After

let bill = this.store.createRecord("bill");
bill.billItems.addObject(this.store.createRecord("bill-item"));

bill contains a billItem with only default attributes, and thus undefined id. When the API response from bill.save() comes back, ember-data accepts the response as the new state of the record it just tried to save, updating everything that is different to the model in store (i.e. your back-end could modify other attributes and they would be updated).

Apparently, your back end created a billItem record, too, upon receiving a bill that includes an embedded billItem without an id. However, since ember-data never explicitly saved this billItem, the embedded billItem is treated as a different record apparently also associated with the bill and thus added to its relation. (I do not have a good intuitive explanation why the association with the old, id-less one is still there - I guess it's because ember-data still treats it as an unsaved billItem record (green in the inspector) and still remembers its (inverse) relation with the original bill.)

Another fix might be not to serialize embedded records but only ids and retrieve the records separately, but if each billItem belongs to exactly one bill this may not be what you want.

0
votes

I've run into the same problem. Since I didn't have enough time for having considerations about of it I decided to use simple workaround. Just in case of succesful save, you can invoke

foo(){
  this.store.peekAll('bill-item')
      .filterBy('isNew', true)
      .forEach(item => this.store.deleteRecord(item));
}

And then, all records without id should have been removed from the store. However, I'd gladly hear some explanations of that issue from more experienced ember developers.