Reputation: 1833
Is it necessary to use DS.hasMany
pointing to a DS.Model
when a model contains an array? Even if the array elements are not really models (no IDs or endpoints of their own)? Is there a better way?
I am using DS.hasMany
, but my extended DS.RESTAdapter
is throwing me a 404 trying to access the model, even though I'm never calling find
on it, and hasMany
is called with { embedded: true }
. I am seeing this error for the first time (apparently in connection with this model, since it goes away without it):
Uncaught Error: assertion failed: Emptying a view in the inBuffer state is not allowed and should not happen under normal circumstances. Most likely there is a bug in your application. This may be due to excessive property change notifications. ember-latest.js:43
What does this mean and what might be causing it?
Here's the stack trace:
Ember.assert ember-latest.js:43
Ember.View.states.inBuffer.empty ember-latest.js:13644
Ember.View.Ember.Object.extend.invokeForState ember-latest.js:12257
Ember.CollectionView.Ember.ContainerView.extend.arrayWillChange ember-latest.js:14477
invokeAction ember-latest.js:3193
iterateSet ember-latest.js:3175
sendEvent ember-latest.js:3323
Ember.Array.Ember.Mixin.create.arrayContentWillChange ember-latest.js:6963
Ember.ArrayProxy.Ember.Object.extend.arrangedContentArrayWillChange ember-latest.js:9281
Ember.ArrayProxy.Ember.Object.extend._arrangedContentWillChange ember-latest.js:9235
invokeAction ember-latest.js:3193
iterateSet ember-latest.js:3175
sendEvent ember-latest.js:3323
notifyObservers ember-latest.js:1872
Ember.notifyBeforeObservers ember-latest.js:2016
propertyWillChange ember-latest.js:2594
iterDeps ember-latest.js:2077
dependentKeysWillChange ember-latest.js:2092
propertyWillChange ember-latest.js:2592
set ember-latest.js:1416
DS.Model.Ember.Object.extend.dataDidChange ember-data-latest.js:3145
Map.forEach ember-latest.js:1273
OrderedSet.forEach ember-latest.js:1145
Map.forEach ember-latest.js:1271
DS.Model.Ember.Object.extend.dataDidChange ember-data-latest.js:3128
invokeAction ember-latest.js:3193
iterateSet ember-latest.js:3175
sendEvent ember-latest.js:3323
notifyObservers ember-latest.js:1872
Ember.notifyObservers ember-latest.js:1999
propertyDidChange ember-latest.js:2632
Ember.Observable.Ember.Mixin.create.propertyDidChange ember-latest.js:7917
Ember.Observable.Ember.Mixin.create.notifyPropertyChange ember-latest.js:7930
didChangeData ember-data-latest.js:2053
Ember.StateManager.Ember.State.extend.sendRecursively ember-latest.js:15446
Ember.StateManager.Ember.State.extend.send ember-latest.js:15431
DS.Model.Ember.Object.extend.send ember-data-latest.js:3058
DS.Store.Ember.Object.extend.load ember-data-latest.js:1737
DS.Store.Ember.Object.extend.loadMany ember-data-latest.js:1763
embeddedFindRecord ember-data-latest.js:3434
hasAssociation ember-data-latest.js:3459
ComputedPropertyPrototype.get ember-latest.js:2968
get ember-latest.js:1362
getPath ember-latest.js:1484
get ember-latest.js:1355
getWithGlobals ember-latest.js:4041
Binding.connect ember-latest.js:4140
connectBindings ember-latest.js:4600
finishPartial ember-latest.js:4610
Class ember-latest.js:8315
Ember.Mixin.create.create ember-latest.js:8457
Ember.View.Ember.Object.extend.createChildView ember-latest.js:13179
Ember.View.states.inBuffer.appendChild ember-latest.js:13622
Ember.View.Ember.Object.extend.invokeForState ember-latest.js:12239
Ember.View.Ember.Object.extend.appendChild ember-latest.js:13058
EmberHandlebars.ViewHelper.Ember.Object.create.helper ember-latest.js:18687
(anonymous function) ember-latest.js:18844
(anonymous function) ember-latest.js:19043
(anonymous function) ember-latest.js:19208
(anonymous function)
(anonymous function) handlebars-1.0.0.beta.6.js:1512
Ember.View.Ember.Object.extend.render ember-latest.js:12223
Ember.View.Ember.Object.extend.renderToBuffer ember-latest.js:12872
Ember.View.states.inBuffer.appendChild ember-latest.js:13625
Ember.View.Ember.Object.extend.invokeForState ember-latest.js:12239
Ember.View.Ember.Object.extend.appendChild ember-latest.js:13058
EmberHandlebars.ViewHelper.Ember.Object.create.helper ember-latest.js:18687
(anonymous function) ember-latest.js:18844
program2
(anonymous function) handlebars-1.0.0.beta.6.js:1529
Ember.View.Ember.Object.extend.render ember-latest.js:12223
Ember._HandlebarsBoundView.Ember._MetamorphView.extend.render ember-latest.js:18075
Ember.wrap.newFunc ember-latest.js:949
Ember.View.Ember.Object.extend.renderToBuffer ember-latest.js:12872
Ember.View.states.inBuffer.appendChild ember-latest.js:13625
Ember.View.Ember.Object.extend.invokeForState ember-latest.js:12239
Ember.View.Ember.Object.extend.appendChild ember-latest.js:13058
bind ember-latest.js:18129
(anonymous function) ember-latest.js:18199
(anonymous function) ember-latest.js:18271
program1
(anonymous function) handlebars-1.0.0.beta.6.js:1529
Ember.View.Ember.Object.extend.render ember-latest.js:12223
Ember.View.Ember.Object.extend.renderToBuffer ember-latest.js:12872
Ember.ContainerView.Ember.View.extend.render ember-latest.js:14078
Ember.View.Ember.Object.extend.forEachChildView ember-latest.js:12486
Ember.ContainerView.Ember.View.extend.render ember-latest.js:14077
Ember.wrap.newFunc ember-latest.js:949
Ember.View.Ember.Object.extend.renderToBuffer ember-latest.js:12872
Ember.View.states.inBuffer.appendChild ember-latest.js:13625
Ember.View.Ember.Object.extend.invokeForState ember-latest.js:12239
Ember.View.Ember.Object.extend.appendChild ember-latest.js:13058
EmberHandlebars.ViewHelper.Ember.Object.create.helper ember-latest.js:18687
(anonymous function) ember-latest.js:18844
(anonymous function) ember-latest.js:19043
(anonymous function) ember-latest.js:19208
(anonymous function)
(anonymous function) handlebars-1.0.0.beta.6.js:1512
Ember.View.Ember.Object.extend.render ember-latest.js:12223
Ember.View.Ember.Object.extend.renderToBuffer ember-latest.js:12872
Ember.ContainerView.Ember.View.extend.render ember-latest.js:14078
Ember.View.Ember.Object.extend.forEachChildView ember-latest.js:12486
Ember.ContainerView.Ember.View.extend.render ember-latest.js:14077
Ember.wrap.newFunc ember-latest.js:949
Ember.View.Ember.Object.extend.renderToBuffer ember-latest.js:12872
Ember.View.states.inBuffer.appendChild ember-latest.js:13625
Ember.View.Ember.Object.extend.invokeForState ember-latest.js:12257
Ember.View.Ember.Object.extend.appendChild ember-latest.js:13058
EmberHandlebars.ViewHelper.Ember.Object.create.helper ember-latest.js:18687
(anonymous function) ember-latest.js:18844
(anonymous function) ember-latest.js:19624
(anonymous function) ember-latest.js:18167
(anonymous function)
(anonymous function) handlebars-1.0.0.beta.6.js:1512
Ember.View.Ember.Object.extend.render ember-latest.js:12223
Ember.View.Ember.Object.extend.renderToBuffer ember-latest.js:12872
Ember.View.Ember.Object.extend.createElement ember-latest.js:12669
Ember.View.states.preRender.insertElement ember-latest.js:13558
Ember.View.Ember.Object.extend.invokeForState ember-latest.js:12257
invoke ember-latest.js:3428
iter ember-latest.js:3475
RunLoop.flush ember-latest.js:3531
RunLoop.end ember-latest.js:3447
Ember.run.end ember-latest.js:3639
autorun ember-latest.js:3705
Thanks for any help.
Update: This fiddle works (with example from docs), but how could those objects be represented if the tags aren't real models (i.e. don't have IDs)?
Upvotes: 30
Views: 26470
Reputation: 3578
Well... It was a little bit difficult but mixing all answers in this post I made it work.
Firstly, you should create a transform for the new type "array":
DS.ArrayTransform = DS.Transform.extend({
deserialize: function(serialized) {
return (Ember.typeOf(serialized) == "array")
? serialized
: [];
},
serialize: function(deserialized) {
var type = Ember.typeOf(deserialized);
if (type == 'array') {
return deserialized
} else if (type == 'string') {
return deserialized.split(',').map(function(item) {
return jQuery.trim(item);
});
} else {
return [];
}
}
});
App.register("transform:array", DS.ArrayTransform);
Now, in your model, just use it as another attr:
App.myModel = Ember.Model.extend({
name : DS.attr('string'),
cont : DS.attr('array')
}
And we are done. Remember, when adding elements to the array, to use pushObject.
In a controller:
this.get('model.cont').pushObject('new Item');
Upvotes: 52
Reputation: 25267
Interestingly all the other 4 answers to this question have almost identical deserialize and serialize functions, so you could simplify things down to:
import Em from 'ember'
import DS from 'ember-data'
# Presumably based on these answers: http://stackoverflow.com/questions/12168570/how-to-represent-arrays-within-ember-data-models
# All we need to do is always make sure an array is returned from serialize or deserialize
toArray = (data) ->
switch Em.typeOf(data)
when 'array' then data
when 'string' then JSON.parse(data)
else []
export default DS.Transform.extend
deserialize: toArray
serialize: toArray
This is using ember-cli-coffees6 for coffeescript with import/export
support
Upvotes: 1
Reputation: 5160
In Ember Data 1.0.0 Beta, one has been given the ability to "register" his or her custom transform "subclass". I'd prefer to refer to it as an extended DS.Transform
object.
DS.ArrayTransform = DS.Transform.extend({
deserialize: function(deserialized) {
// ...
return deserialized;
},
serialize: function(serialized) {
// ...
return serialized;
}
});
App.register('transform:array', DS.ArrayTransform);
Upvotes: 9
Reputation: 881
I use a raw
transform, which looks like this in ember-data revision 11:
DS.RESTAdapter.registerTransform('raw', {
deserialize: function(serialized) {
return serialized;
},
serialize: function(deserialized) {
return deserialized;
}
});
Then, within a model, I do this:
App.MyModel = Ember.Model.extend({
anArray: DS.attr('raw')
});
and can use anArray
like a regular array anywhere.
Upvotes: 29
Reputation: 341
Here is an example of creating a custom array type in Ember-Data (version 10):
DS.JSONTransforms.array =
# If the outgoing json is already a valid javascript array
# then pass it through untouched. In all other cases, replace it
# with an empty array. This means null or undefined values
# automatically become empty arrays when serializing this type.
serialize: (jsonData)->
if Em.typeOf(jsonData) is 'array' then jsonData else []
# If the incoming data is a javascript array, pass it through.
# If it is a string, then coerce it into an array by splitting
# it on commas and trimming whitespace on each element.
# Otherwise pass back an empty array. This has the effect of
# turning all other data types (including nulls and undefined
# values) into empty arrays.
deserialize: (externalData)->
switch Em.typeOf(externalData)
when 'array' then return externalData
when 'string' then return externalData.split(',').map((item)-> jQuery.trim(item))
else return []
Now you can use the custom type in a model attribute:
App.CalenderWeek = DS.Model.extend
selected_days = DS.attr('array')
And now when you fetch a record with:
App.CalendarWeek.find(1)
both of these incoming json records will deserialize correctly into an Array:
{ selected_days: ['Monday', 'Tuesday', 'Saturday'] }
or
{ selected_days: 'Monday, Tuesday, Saturday' }
Upvotes: 20
Reputation: 4885
A bit late to the game here but here's a jFiddle I found that is a simple implementation of declaring a new array codec
http://jsfiddle.net/Nook/ab2Xf/
Upvotes: 0
Reputation: 9236
If you absolutely need a custom data structure to be exchanged with your server, you can enrich DS.attr.transforms
and declare a new array
codec, for example.
See source code for existing attribute codecs implementation. It is a good place to start adding your own.
Upvotes: 4