spullen
spullen

Reputation: 3317

How to handle cancel/rollback when transitioning between views in emberjs

So I asked a similar question awhile back. However, something about that solution left a bad smell.

Here's the scenario, when I want to create a record I have a new route. So for example PostsNewRoute that looks something like:

App.PostsNewRoute = Ember.Route.extend({
  model: function() {
    return App.Post.createRecord({title: '', content: ''});
  },
  deactivate: function() {
    var controller = this.controllerFor('posts.new');
    var content = controller.get('content');
    if (content && content.get('isNew') && !content.get('isSaving')) {
      content.deleteRecord();
    }
  }
});

and similarly for PostsEditRoute:

App.PostsEditRoute = Ember.Route.extend({
  model: function(params) {
    return App.Post.find(params.post_id);
  },
  deactivate: function() {
    var controller = this.controllerFor('posts.edit');
    var content = controller.get('content');
    if (content && content.get('isDirty') && !content.get('isSaving')) {
      content.rollback();
    }
  }
});

This didn't seem right to me. I feel that there is too much code to handle this, and in a bigger application where I'm doing this for many objects...

So here is a different approach I tried, which seemed a little cleaner, but had it's own issues (see after the jump):

App.PostsNewRoute = Ember.Route.extend({
  model: function() {
    return App.Post.createRecord({title: '', content: ''});
  }
});

App.PostsEditRoute = Ember.Route.extend({
  model: function(params) {
    return App.Post.find(params.post_id);
  }
});

App.PostsFormView = Ember.View.extend({
  templateName: 'posts/form',
  tagName: 'form',
  classNames: ['form-horizontal'],

  buttonText: '',

  submit: function() {
    // TODO: validation
    this.get('controller').get('store').commit();
    this.get('controller').transitionToRoute('posts.index');
  },

  cancel: function() {
    var content = this.get('controller').get('content');
    if(content && !content.get('isSaving')) {
      if(content.get('isNew')) {
        content.destroyRecord();
      } else if(content.get('isDirty')) {
        content.rollback();
      }
    }
    this.get('controller').transitionToRoute('posts.index');
  }
});

Template:

<div class="control-group">
  <label class="control-label">Title:</label>
  <div class="controls">
    {{view Ember.TextField valueBinding='title'}}
  </div>
</div>
<div class="control-group">
  <label class="control-label">Content:</label>
  <div class="controls">
    {{view Ember.TextArea valueBinding='content'}}
  </div>
</div>
<div class="form-actions">
  <button class="btn btn-primary">{{view.buttonText}}</button>
  <a href="#" class="btn btn-danger" {{action cancel target='view'}}>Cancel</a>
</div>

This works almost the same as what I tried before, however, now if a user clicks on a nav link or clicks the back button stuff doesn't get cleaned up.

Both of these ways don't seem quite right, what would be the right or better way to handle this?

Upvotes: 2

Views: 905

Answers (1)

Finn MacCool
Finn MacCool

Reputation: 808

well, i liked what i found in this nice little gist (the interesting part is close to the bottom).

following that example, (part of) my PostsNewController looks like this:

startEditing: function() {
    this.transaction = this.get('store').transaction();
    this.set('content', this.transaction.createRecord(EmberBlog.Post, {}));
},

stopEditing: function() {
    if (this.transaction) {
        this.transaction.rollback();
        this.transaction = null;
    }
}

and my PostsNewRoute like this:

EmberBlog.PostsNewRoute = Ember.Route.extend({
  model: function() {
    return null;
  },

  setupController: function(controller) {
    controller.startEditing();
  },

  deactivate: function() {
    this.controllerFor('posts.new').stopEditing();
  }
});

i think it should be easy enough to adapt to your specific needs.

Upvotes: 2

Related Questions