11
votes

I'm using ember(+data) and have built a simple signup form, but I'm having trouble getting the form (and ember-data transaction) to work properly after a server-side validation error.

I'm following https://github.com/dgeb/ember_data_example as a guide for how to use transactions, and it mostly works for the Happy Path, but after a server-side validation error, I can't get ember-data to resubmit the API request when I click Submit.

I did some digging around, and I think I'm missing some step related to resetting the transaction after the model becomes invalid or something like that...

Github and App

You can try the app and reproduce the problem at http://emb.herokuapp.com/

You can also examine the full source code at https://github.com/justinfaulkner/ember-data-resubmit

Repro

You can trigger a fake server-side error by completing the form in my app using an e-mail address at @example.com. The Rails API will respond 422 and an errors object keyed to the username/email field.

The field should be highlighted in red with an error -- edit the email address to something that should be valid, and click Submit again. With developer tools open in chrome, I don't see ember-data sending an http request with the click (but a console.log in the controller indicates that the click is indeed being received).

What should happen

After modifying the field with the error, ember-data (I think) changes the model's from invalid to uncommitted so that it gets submitted the next time commit is called. When I click Submit, ember-data should send the HTTP request to my api, and ember should transition to the "Congrats!" page.

Instead, however, the form just sits there. No http request. No transition to "Congrats!".

Here's a screenshot after I clicked Submit a few (18) times after having updated the inputs:

Submit button click received, but no http request

Snippets

Here are some snippets of my ember app:

IndexRoute

My route, which uses startEditing like ember_data_example does:

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return null;
  },
  setupController: function(controller) {
    controller.startEditing();
  },
  deactivate: function() {
    this.controllerFor('index').stopEditing();
  }
});

IndexController

My IndexController is modeled after ember_data_example's ContactsNewController

App.IndexController = Ember.ObjectController.extend({
  startEditing: function() {
    this.transaction = this.get('store').transaction();
    this.set('content', this.transaction.createRecord(App.User));
  },
  submit: function(user){
    console.log("submitting!");
    this.transaction.commit();
    this.transaction = null;
  },
  _transitionOnSuccess: function(stuff) {
    if (this.get('content.id') && this.get('content.id').length > 0) {
      console.log("_transitionOnSuccess");
      this.transitionToRoute('success');
    }
  }.observes('content.id'),
  stopEditing: function() {
    if (this.transaction) {
      this.transaction.rollback();
      this.transaction = null;
    }
  }
});

User

Here's my model. I'm using ember-validations.

App.User = DS.Model.extend(Ember.Validations.Mixin);
App.User.reopen({
  username: DS.attr('string'),
  password: DS.attr('string'),
  profile: DS.belongsTo('App.Profile'),

  validations: {
      username: {
          presence: true
      },
      password: {
          presence: true,
          length: { minimum: 6 }
      }
  }
});

index.hbs

And here's the form from my handlebars template. I'm using ember-easyForm.

{{#formFor controller}}
    <div class="row">
        <div class="large-12 columns">
            {{input username placeholder="Email address"}}
        </div>

    </div>

    <div class="row">
        <div class="large-12 columns">
            {{input password placeholder="Password"}}
        </div>
    </div>

    {{submit "Sign Up" class="button"}}
{{/formFor}}

In case the library authors see this post, I have made a couple local modifications to the app's copy of ember-easyForm and ember-validations in this commit: https://github.com/justinfaulkner/dockyard-example/commit/f618b0e4fb72314d56bb3a9d95e1325925ba6ad0 . I don't think my changes are causing my problem, though.

becameInvalid and rollback?

I did run across a similar-looking question here: Ember Data and dirty records but when I added a User.becameInvalid to rollback the transaction, this caused the form to empty when trying to re-submit (and still no success having ember-data resubmit the http request).

Thanks!

I'm sure I'm following ember_data_example poorly (or failing to extend it to my use-case) or am making some simple mistake somewhere...

Thanks in advance.

Edit Apr 5

So far, I can find at least two main problems:

  1. this.transaction = null; Do I need this? What should I do instead?
  2. I tried removing this.transaction = null; and ember-data tries to actually commit now (but still won't submit the ajax request). Now, when I type in the invalid field, I can see ember-data try to update the record back to uncommitted/created... but it does it in a different transaction.

I pushed up a no-null branch to the repo that has some console.logs in ember-data that prints out what the transaction ID is... here's a snapshot of my console:

different transaction

recordBecameDirty is called when I type in the field. Ember-data updates the record to be ready to be committable again. But, it's doing it in some other transaction (458)

But my submit button is tied to the original transaction (316), and there's no records ready to commit in that transaction.... hmm....

1
Is the transaction being recreated after the server returns with an error? I noticed on the initial submission that you're doing: this.transaction = null; I would think this would prevent any subsequent requests from going through (though, I would also expect the console to show an error about calling commit on null).Adam
Aside, I noticed that when the server returns an error the model is moved into the error state and the model state machine doesn't seem to provide a transition out of the error state (which is necessary in order to try to save the model again). This stackoverflow post proved to be very helpful (though I think the fix seems more like a workaround to an ember-data bug).Adam
I think the model gets moved to a different transaction at some point. Also, when I type in the error field, I think ember-data notices the field changes and automatically transitions it out of the error state. I'll check both of these claims to make sure I'm not getting mixed up.Justin

1 Answers

2
votes

This is a known issue with ember data. See: https://github.com/emberjs/data/pull/539

The simple change in Ember Data in that commit https://github.com/Cyril-sf/data/commit/fe9c63beb02e9f16051e59a9f7c0a918152a0231 should solve your problem.