Why does store.createRecord create a record immediately?


#1

Let’s say I have a list of items displayed using store.all(‘product’)

When I click add product button in my method I use. store.createRecord(‘product’, {name: ‘Awesome Product’})

The new product appears in the list immediately, even though it is not stored on the server.

store.createRocord returns a newly created record which I have to call save() on to persist it on the server.

What is the right way to create new records so they only appear in the store after they are saved?

I would think that store.push method would be more appropriate for adding records to the store without storing it on the server.


#2

I believe, at a high level, the reason is to improve responsiveness but I think also to allow for client-side validation, if applicable in your model (I might be wrong with the second point - that’s my own understanding). You can indicate that a record is stored locally with a watched property that your update once your Adapter saves the record by hooking into the appropriate record lifecycle event handlers (didCreate or didUpdate) and update the property there or use product.get("stateManager.currentState.name") to find where your save process is. Use this property to add some CSS and watch it until it changes and change your CSS accordingly.

The following article gives a good introduction to records and ember data


#3

I think what you want is buildRecord instead of createRecord. The latter sets that as loaded, which means is on the store, so it will appear on lists that might be bound to that collection. Hope this helps.


#4

buildRecord is marked as private in the API docs, does that mean it’s not meant to be part of the public API?

It definitely sounds useful, are there any gotchas to look out for when using it?


#5

Also interested with @lookingsideways on if there are any unexpected consequences on using buildRecord.


#6

We also ran into this problem as well. Coming from Rails, it feels like buildRecord should be a public API method. It’s annoying to have to clean up invalid/partial records that get dumped into the store.


#7

It is my understanding that the this is all by design. Internally, there is a RecordArrayManager that watches and responds to record changes in the store (adding a record, deleting a record, changing a record), and updates all RecordArrays that care about the model that has changed.

There is a RecordArray and a FilteredRecordArray (and an AdapterPopulatedRecordArray, but that’s not important here), but it is important to note that the RecordArray returned by store.all, while it is not a FilteredRecordArray instance, it is technically filtered in that it is registered with the RecordArrayManager. Its filter is very simple: it shall contain every record in the store that is of this particular type, regardless of anything else about it–state, isDirty, isNew, isDeleted, etc. So when you display a list backed by the RecordArray returned by store.all, it makes perfect sense that it immediately shows records that were just created via createRecord.

What you actually want to do instead of store.all is store.filter and set your filter only returns records of that type that are not isNew.

buildRecord is private because the client should never be creating records without receiving them from the persistence layer. createRecord creates records marked as dirty and new so that there can be a future point of synchronization with your persistence layer. If you need a record that is not dirty, you must retrieve it from the persistence layer (or some other trusted single source of truth, such as a local cache). Similarly, you shouldn’t use store.push because that method expects the deserialized POJOs that are about to be pushed into the store to be of a very specific format (the responsibility of translating the format of the response from your persistence layer into the format that store.push wants is left to your Adapter and your Serializer)

So while there is nothing saying that you can’t use buildRecord, what Ember-Data wants you do to is use createRecord when you actually are creating a brand new record that does not exist in the persistence layer, and use store.filter to make your list only show what you want it to show.

EDIT: I was incorrect when I said “it shall contain every record in the store that is of this particular type, regardless of anything else about it”. The only thing it cares about is whether or not the record isDeleted or isEmpty. Anything else will appear in your all RecordArray:


#9

I like your idea but it doesn’t work for me:

App.IndexRoute = Ember.Route.extend({

  model: function() {
    return this.store.filter('color', function(color) {
      return color.get("isNew") === false;
    })
  }

});  

I need to resolve to a hack:

App.IndexRoute = Ember.Route.extend({

  setupController: function(controller) {
      var _this = this;
      return this.get('store').find('color').then(function() {
          controller.set('model', _this.get('store').filter('color', function(color){
              return color.get("isNew") === false;
          }));
      });
  }

});  

hack was found here - https://github.com/emberjs/data/issues/1872#issuecomment-63253265 - and I’ll post complete examples / test cases in that github issue…

(as it still seems unresolved / affecting other users)


#10

Hello, has anyone had a better answer to this question as I just ran into this issue now with latest ember/cli where I have a record that is briefly created and then removed if the backend save fails… wondering how to prevent this?!

EDIT:

In case anyone wondering, using store.filter resolved the issue…

@store.filter 'content', {}, (content)->
  content.get('isNew') is false

#11

One of various reasons I have decided to completely abandon ember-data. Server has my data and I want people to see what is on the server not what you have input instantly in a second area. Then having to work around ember’s link-to taking the model object so you don’t get updated data without stupid workarounds.

Websites rely on a database 99.99% of the time, just get it.


#12

Calling reload() on the model after the Promise returns is the standard way of doing this in Ember Data. Save to store, call reload, then transition to the newly saved record.


#14