Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relationships and Changesets #551

Open
sandstrom opened this issue Oct 7, 2020 · 9 comments
Open

Relationships and Changesets #551

sandstrom opened this issue Oct 7, 2020 · 9 comments

Comments

@sandstrom
Copy link

When working with changesets, this pattern is quite common:

let home = new Address();
let work = new Address();

let user = new User();
user.addresses = [home, work];

let validator = function() {};
let model = Changeset(user, validator);
<form>
  <h3>Personal details</h3>
  {{input type='text' value=model.firstName}}
  <!-- etc… -->
  
  <h3>Addresses</h3>
  {{#each model.addresses as |address|}}
    {{input type='text' value=address.street}}
    {{input type='text' value=address.city}}
    <!-- etc… -->
  {{/each}}

</form>

What I'd like to do here is to make changes to the address instances and buffer them inside a changeset. So that I can do rollback on the model and automatically have rollbacks applied to all child changesets too.

Maybe by specifying which relationships should create proxy changesets and buffer, e.g. let model = Changeset(user, validator, { nested: ['addresses'] }); or similar (opt-in).

Right now changesets seems to support access via keys, e.g. changeset.set('profile.url', '…') but not changeset.get('profile').set('url', '…')

  1. Has there been any discussion on supporting relationships, such that the handlebars example above would work with rollback/save on the user, with changes applied to nested addresses?

  2. Would also be good to support rollback/apply for changes to the collection itself, i.e. adding/removing addresses. Maybe via an API such as model.get('addresses').add(vacationHome); where model.get('addresses') would return an instance of e.g. ChangeSetList.

Related issues:

@sandstrom
Copy link
Author

pinging @snewcomer @jaredgalanis @rwwagner90 since you've talked about these issues recently, happy to hear your thoughts on this!

snewcomer added a commit that referenced this issue Oct 10, 2020
snewcomer added a commit that referenced this issue Oct 10, 2020
)

* [Test]: mutating underlying complex objects should not be possible

close #539

* Add form addresses test

ref #551
@snewcomer
Copy link
Collaborator

snewcomer commented Oct 11, 2020

@sandstrom I think we would need to see the operations (in a code example) you are looking to take on the changeset. I think we can help you on that.

but not changeset.get('profile').set('url', '…')

This is the beauty of the Proxy. We can trap at any level. In this case, every level now has a .get/.set method with this PR 👇

validated-changeset/validated-changeset#85

@sandstrom
Copy link
Author

@snewcomer Sounds great! 😄 🎉

I've looked at the diff and I understand some of it, but far from everything.

Since there are no documentation, a few quick questions:

  • Will rollback/save etc. on the parent propagate down to any children?
  • I assume this still require the changeset-get and changeset-set helpers?

Also, what are your thoughts about adding/removing relationships to/from a model (or adding/removing from an array)?

@snewcomer
Copy link
Collaborator

  1. nope. I don't think that has ever worked but is an interesting idea. I'll think about how we can handle that. I don't think ember-data does that does it?

  2. with the recent changes, I don't think these are strictly necessary. They used to be bc handling nested set while not messing up non dirty child attributes was very difficult.

Adding and removing from an array will also be quite difficult. Atomically replacing the array is really the only sane way to do it, especially when dealing with ember-data array like objects. We trap the setter but don't trap array methods like push/concat.

@sandstrom
Copy link
Author

sandstrom commented Oct 14, 2020

@snewcomer Thanks for taking time answering!

One idea could be to extend the basic principle of the changeset to relationships.

It's already a proxy at the key/value surface of an object, basically get/set is proxied and buffered. But this "leaks" is if myChangeset.children return an array (or an array-like relationship object from Ember Data).

One approach would be to "tell" the changeset, when initiated, about the keys that return relationships, for example new Changeset(object, { listKeys: ['children'] }). For these keys, instead of proxying down to the underlying model the changeset could create a ChangeSetList (on-the-fly), associate this with the changeset and wrap it around the relationship [array or similar] and return it as the response.

myChangeset.children would then return a ChangeSetList with methods such as add and remove, plus support iteration, e.g. via toArray or Symbol.iterator. Adding something to the changeset-list, via add, would then buffer/proxy just like get and set already does on a changeset. This would allow rollback/save of added/removed relationship items.

Happy to clarify my thinking about this and explain in more detail!

@snewcomer
Copy link
Collaborator

@sandstrom That is an interesting idea. I like it. I'll see if I can put up a psuedo RFC at some point in the next few weeks.

@sandstrom
Copy link
Author

@snewcomer Sounds great!

Happy to help flesh out the idea, if you want? Just wanted to hear your overall thoughts on the idea first (to avoid spending time on something that is unlikely to bear fruit), but it sounds likely you are at least cautiously positive at this point.

@richbrown
Copy link

@sandstrom and @snewcomer, just to confirm your instincts on this; I would also find this feature very useful. I work around it by creating my own Changeset and define a property on that that overrides the original array and returns an array of changesets for each item in the original array. Equally, I override the save(), rollback(), isPristine and isDirty on the parent changeset to take into account the new array of child changesets. Having the feature @sandstrom described would save me quite a lot of boilerplate code.

@oliverlangan
Copy link

Plus one on this. I don't find the way ember-changeset works with relationships to be at all clear. I am trying to work around it, and @richbrown would definitely be interested in your approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants