emberjs / data

WarpDrive is a lightweight data library for web apps — universal, typed, reactive, and ready to scale.
https://api.emberjs.com/ember-data/release
MIT License
3.04k stars 1.33k forks source link

[Feature] "Fork" model instances à la EPF #1901

Closed GetContented closed 9 years ago

GetContented commented 10 years ago

As described on this stack overflow: http://stackoverflow.com/questions/19816871/how-to-fork-a-model-in-ember-data

There's a use case which keeps popping up in various forms that doesn't seem to be addressed in Ember and Ember Data yet... which is that programmers would like to have a new or existing object that isn't in the store, that is able to then update another model instance (in the case of editing) or be created later in the store...

So, if I have a model "Animal", then I'd like to be able to say "hey, give me a fake animal that I can later turn into a created animal in the store", or "hey, give me a fake clone of this particular animal that can update the original from later"

ie... The use case is that either

  1. The programmer doesn't want the "forked" object differences to be visible or used in the bound attributes of the application until a point is reached when the changes are persisted as one set (for validation, or atomicity reasons)... or;
  2. The programmer doesn't want the "new" object to appear in the store.

Presently solutions seem to be "just use a filter on all your lists" for the new case, but for the forked case, there doesn't appear to be a solution other than to use a whole other set of attributes which are bound to a controller and then reified into a "real" object later.

gopeter commented 10 years ago

:+1:

recipher commented 10 years ago

+2

On 29 April 2014 20:01, Peter Göbel notifications@github.com wrote:

+1

— Reply to this email directly or view it on GitHubhttps://github.com/emberjs/data/issues/1901#issuecomment-41718215 .

mellatone commented 10 years ago

+1! How does this example: http://embersherpa.com/articles/crud-example-app-without-ember-data/ pull off using the Copyable mixin? Can something similar be implemented with ember-data?

GetContented commented 10 years ago

@hazeltree note this issue isn't necessarily about deep cloning objects, which I would love if there was an implementation in straight Ember Data, too (JS copy by default only does shallow copying), but your question can be answered by the fact that Ember.Copyable provides an interface that you should create your own copy method for, which he then goes on to do like this:

copy: function() {
    // copy method is used by the PhotoEditRoute to create a clone of the model
    // we create a clone to preserve the original incase Cancel button is clicked
    return Em.run( this.constructor, 'create', this.serialize() );
  },
  serialize: function() {
    // Our presistance layer doesn't know about the fields that custom models need to preserve
    // for this reason, we need a serialize function that will return a version of this model
    // that can be saved to localStorage
    throw new Error(Ember.String.fmt("%@ has to implement serialize() method which is required to convert it to JSON.", [this]));
  }

and then in each of his models, he has a serializer/ (with getProperties) method (here's the one for Photo):

  // Tells the resistance layer what properties to save to localStorage
  // Ember Data does this for you.
  serialize: function() {
    return this.getProperties([ "guid", "image", "title", "description" ]);
  }
neverfox commented 10 years ago

Fireplace does this by allowing you to fork the whole store. When you're done, you destroy the fork. As far as I can tell, it's as simple as:

// store.js
  // just returns a new instance of the same store with the same container
  // means the cache is isolated & any finds etc are operating
  // independently of the forked store.
  // no need to re-join, just save or discard your changes & firebase
  // takes care of keeping the other models in sync
  fork: function() {
    return this.constructor.create({
      container: get(this, "container")
    });
  }
GetContented commented 10 years ago

@igorT Do you think this is a good idea? :) It seems there's some interest here.

bakura10 commented 10 years ago

Still no news about this? This seems like an essential feature. I can't find a simple way to circumvent this, and it leads really to strange UI because of data bindings. In some places, it is very confusing for users as they think the model is loaded as you type. Does the new SSOT would simplify such a feature?

stefanpenner commented 10 years ago

@bakura10 it is an incredibly non-trivial feature. Both from API and implementation. It is not that hard to implement it in very specific scenarios, but hard to scale that to a general case the consumers of ember-data. This gets alot more fun when you realize the potential for very hard to debug performance and memory leak related issues

This particular problem or pain point is something we are well aware of and hope to have a good solution for eventually. First and fore-most is a stable 1.0 after which this may become a focus area.

bakura10 commented 10 years ago

Nice to hear this is something you want to solve :).

stefanpenner commented 10 years ago

@bakura10 yup. Until then i typically buffer this state on the controllers and then apply the state to ember-data models after the fact. This enables the developer to finely tune the "forking" for the specific scenario.

bakura10 commented 10 years ago

Yes, this is the kind of things I usually do but this is very manual and require a lot of manual typings and duplication code. Anyway, I'll wait for that. I'm very happy that Ember-Data starts to be actually really usable (once you'll have nested URLs and forking, Ember-Data will definitely be an awesome solution ;))

mixonic commented 10 years ago

Also eager to work on this. Orbit has some good ideas for internals, EPF some good ideas for an API. We may need to improve the ability to pass actions with a store or scope attached before we can really pull off something great.

bakura10 commented 10 years ago

Hi,

I've found another use case where forking could be useful: I have a use case with exisitng models, and when I select one in a list, I want to duplicate it so that I can potnetially save a new copy of the exact same model. Currently, I duplicate all the fields but, as a consequence, the new model is considered as "isNew", and its isDirty flag is set to "true".

This means I cannot use the "isDirty" property to detect changes on the duplicated model, and I need to add an observer an all fields to simulate the "isDirty" behaviour.

I think that for the forking feature, a forking feature must keep its original data flags (so if "isDirty" is false on the original model, the forked model must keep "isDirty" as false).

GetContented commented 9 years ago

Doesn't seem to be enough interest, and I don't so much have this problem anymoe. Interested parties should open new issues if they still have them.