5
votes

I'm not sure if it's the correct way to express my requirement. But the word "fork" appears in the roadmap of Ember Data github page. And it's a killer feature in EPF. I'm wondering if I can do it in Ember Data.

The fork feature is useful when we have an edit page and bind a model. When we edit the information, I don't want the model properties to be changed because if the model properties are also displayed in other place they will be changed automatically. That's not what I want.

An example is a list on the left side of the page and a edit form for a specific model on the right side of the page. When I modify role name in the text field, the role name on the left side is changed because of data binding.

enter image description here

EPF solves this problem by "fork" the existing model and set it in a child session. The session in EPF is similar with store in Ember Data. When you modify the forked model it does not effect the model in the main session. After the forked model is updated it can be merged back to main session, and the corresponding model in main session are updated.

What I can think a solution in Ember Data is to create a different store and copy the model to that store. But it is a bit complicated. Does anyone have a better solution? I searched on stackoverflow and ember discuss forum and didn't find an answer.

2
I think I see what you're saying. I've done something similar in the past, but my solution isn't all that great. Basically, I was calling Model.toJSON into a new Em.Object instance, and binding the view to this new instance, then when I'm done editing (or whatever action), I'd serialize back to the actual model in store (only updating the properties in the model that were different than the new object). Have you tried something similar to this?MilkyWayJoe
Actually, currently this is similar with what I did in my site. But I choose to encapsulate Em.Object as model. And I copy the model to bind to the editing form. But this solution is not good for Ember Data, because when you want to update a model, you should set properties to this model and call save, in this case it'll also modify all other UI that bind the same model, so I can't just use a copied Em.Object to do that.darkbaby123
it was just a thought... When I did it, I added an extra step before calling the store save, so I had an action in the controller to extract the data from the model and/or serialize back every time I'd need to do read or write in object that's bound to the view. I've moved over to transactions after that so they wouldn't propagate the changes until I saved. I haven't had the time to touch this, or any Ember related suff in a while tho, like i said it was just a thoughtMilkyWayJoe
Ah you want clone for editing? Yeah I use a similar thing and have a function that goes through and replaces the original's data. It's clunky as all get up. Perhaps you should feature request it in the ember.js repo? I'd love to see that feature in ember!Julian Leviston
@Slevin that's not what he (@MilkyWayJoe) was saying. He was saying he used a simple Em.Object that he then used to update the model object after the user had finished their edits. It's a way of "hacking" around the fact that editing model objects are updated immediately rather than "forked" as darkbaby123 was wanting.Julian Leviston

2 Answers

1
votes

I'm not sure if there is a standard or common way to do this in Ember, but I have written a Mixin that I can put on my routes to give some basic 'buffering' of the model:

App.BufferedRouteMixin = Ember.Mixin.create({
    setupController: function(controller, model) {
        this.setBufferFromModel(controller, model);
        this._super(controller, model);
    },
    setBufferFromModel: function(controller, model) {
        var buffer = {};
        controller.set('model', model);
        model.eachAttribute(function(name, meta) {
            buffer[name] = model.get(name);
        });

        controller.set('buffer', buffer);
    },
    setModelFromBuffer: function() {
        var model = this.get('model'),
            buffer = this.get('buffer');

        model.eachAttribute(function(name, meta) {
            if (buffer[name]) {
                model.set(name, buffer[name]);
            }
        });
    }
});

Once this is added to my Edit Route, I can call setModelFromBuffer in my save action. In my templates, I can use the {{#with buffer}} helper.

-1
votes

What I believe to be the simplest solution, is to have an Ember.Object that mimics the structure of your model. When entering an edit mode, copy the properties from the model to the Ember.Object and then have them update there until the user clicks 'Save' or whichever action you wish to merge the changes back in. One thing I did that was important was to add the mixin Ember.Copyable to my object. Below is some code I used to solve this issue for myself.

NOTE:: This code was to prevent a model from being created before it was submitted, so instead of edit, mine is create new.

App.SomeModel = DS.Model.extend({
  user: DS.belongsTo('user'),
  home: DS.belongsTo('home'),

  cost: DS.attr('number'),
  title: DS.attr('string'),
  description: DS.attr('string'),
  category: DS.attr('number'),
  categoryName: function () {
    return Roomy.enums.TransactionCategories[this.get('category')]
  }.property('category'),
  date: DS.attr('date', {
       defaultValue: function() { return new Date(); }
  }),
  fuzzyDate: function () {
    return moment(this.get('date')).fromNow();
  }.property('date'),
  split: DS.attr('boolean'),
  contributors: DS.attr('array'),
  points: DS.attr('number')
});

App.SomeModelNew = Ember.Object.extend(Ember.Copyable, {
  user: null,
  home: null,
  cost: null,
  title: null,
  description: null,
  category: null,
  date: new Date(),
  split: false,
  contributors: [],
  points: null,
  copy: function () {
    return this.getProperties('cost', 'title', 'description', 'category', 'date', 'split', 'contributors', 'user', 'home');
}
});

Then to save this model I did something like this.
NOTE:: The code with User and Home I had to use because of the relationships, simply copying the json form of the User and Home would not persist the relationship and give the model the ID's it needed in the database.

Contoller code below:

//Before this function is called, all the inputs in the form have been filled in and the instance now has values for all the fields that were defined for it
saveTxn: function (txn) {
    // txn is the App.SomeModelNew instance
    copy = this.store.createRecord('transaction', txn); // this returns the App.SomeModelNew.copy() object
    copy.set('user', txn.get('user')); // OVerwrite user for relationship
    copy.set('home', txn.get('home')); // Overwrite home for relationship
    return copy.save();
}

I hope this helps.