hasDirtyAttributes do not work with nested attributes (JSON API)


#1

Hi

I have a model returning from the backend using JSON API.

// models/client.js
import DS from 'ember-data';
export default DS.Model.extend({
    name: DS.attr(),
    full_name: DS.attr(),
    contacts: DS.attr(),
});

One of the attributes is an array of properties, “contacts”, coming from a jsonb column in the clients table in PostgreSQL. Not from a related table.

{
  "data": {
    "id": "230bc966-b109-4dc8-b99d-7ac29bbbb883",
    "type": "parties",
    "attributes": {
      "name": "Gran tienda",
      "full-name": "Gran tienda del universo",
      "contacts": {
        "included": [{
          "id": 1,
          "type": "contacts",
          "attributes": {
            "name": "pepe",
            "phone": "91 000 00 00"
          }
        }, {
          "id": 2,
          "type": "contacts",
          "attributes": {
            "name": "juan",
            "phone": "91 000 00 11"
          }
        }, {
          "id": 3,
          "type": "contacts",
          "attributes": {
            "name": "fernando",
            "phone": "91 000 00 22"
          }
        }]
      }
    }
  }
}

I have no problem to show, edit and save the Contacts elements, but it seems that “hasDirtyAttributes” do not detect changes in the nested Contacts.

I’ve read some things about similar problems, but the problems and the solution tricks are related to the REST API, not the JSON API. It is supposed that JSON API “understand” structures like mine, so no tricks should be necessary, isn’t it?

My intuition tells me that I must create a contacts model, like in a relationship:

// models/contact
import DS from 'ember-data';
export default DS.Model.extend({
    name: DS.attr(),
    phone: DS.attr()
});

And find a way to relate it to the client model.

export default DS.Model.extend({
    name: DS.attr(),
    full_name: DS.attr(),
    contacts: DS.hasMany('contact')
});

But there is not a true relationship between tables or entities, so I am really lost at this point.

I have also inserted “included”: [{ … }] as the property of the array, and the “id” and “type” attributes to maintain the JSON API data structure.

Can someone point me n the right direction to find the missing pieces?

Than you in advance:

Nacho B.


#2

It is a real problem. I looked at ember-dirtier but it did’t work for my ember-data version. I also looked at ember-data-relationship-tracker but it seemed to much like a hack. Perhaps we can improve ember-dirtier to work with more ember-data versions.


#3

Assuming your app is associating different contacts to a party and not editing each contact, then relationships is the way to go. If you don’t have control over the API, what you can do is move the list of contacts from attributes to relationships and included per JSON:API before the data is loaded into the store.

If you were talking about editing each contact and expecting hasDirtyAttributes to be true on the party model, then that gets a little more complicated heh.


#4

Yes, it seems to be a real problem. :slight_smile:

Relationships can be very complex, so I can understand the limits of hasDirtyAttributes. But this case is not a true relationship, It’s just nested json, exactly as it comes out from the backend database. Somehow, I see it like another “text” field. Thats why I am surprised that changes are not detected.

David, I read a great post from you about nested data. It’s exactly the same situation. But you were using DS.EmbeddedRecordsMixin and REST API. (Ihttps://thejsguy.com/2016/01/29/working-with-nested-data-in-ember-data-models.html)

It seems to me that I must solve it in a “procedural” way:

  1. Extract the Contacts as an array.
  2. Show and edit the copy.
  3. If something changes I set a flag (my own “dirty flag”).
  4. If “flag”, copy back the edited data into the main record.
  5. If not “flag”, there is nothing to do.

In the end, the whole client model comes from a record in PostgreSQL. Contacts is just a column of the type jsonb. There are no relationships to care for.

Maybe my approach is too harsh for Ember :slight_smile: But I need this functionality.

Regards:

Nacho B.


#5

You could use ember-data-model-fragments ( it tracks dirtyness for you ) you would use the fragmentArray or fragments ( contact ) or you can use ember-data-change-tracker which can track arbitrary objects


#6

Thank you, Daniel. In the end that’s exactly what I need, but I feel somehow insecure because it’s not integrated in Ember’s core. By now I prefer to reduce the amount of extensions. If something interfere with something or gets deprecated… I still have not enough knowledge to make workarounds.

Regards: Nacho B.