如何在 ember-data 模型中表示数组?
Posted
技术标签:
【中文标题】如何在 ember-data 模型中表示数组?【英文标题】:How to represent arrays within ember-data models? 【发布时间】:2012-08-23 12:15:15 【问题描述】:当模型包含数组时,是否有必要使用DS.hasMany
指向DS.Model
?即使数组元素不是真正的模型(没有自己的 ID 或端点)?有没有更好的办法?
我正在使用DS.hasMany
,但我的扩展DS.RESTAdapter
向我抛出一个404 试图访问模型,即使我从未在它上面调用find
,并且hasMany
是用 embedded: true
调用的.我第一次看到这个错误(显然与这个模型有关,因为没有它它就会消失):
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
这是什么意思,可能是什么原因造成的?
这是堆栈跟踪:
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
感谢您的帮助。
更新:This fiddle 有效(以docs 为例),但如果标签不是真实模型(即don't have IDs),这些对象如何表示?
【问题讨论】:
仅仅定义一个普通的数组属性不起作用? 我用小提琴更新了。我该怎么做? 嗯,我想如果你想让你的标签被持久化,你必须将它们描述为一个 DS.Model。如果没有,那么只需在 Person 模型中声明一个常用数组。最后,也许我不明白你想要什么…… 我创建了一个将 Tag 声明为常规 ember 对象的小提琴:jsfiddle.net/Sly7/JBmuA/2 灯具是我从服务器收到的一个例子。所以,一个普通的 ember 对象就可以了,但我怎样才能将它附加到模型上呢?我确实需要从数据库中加载它,但我不关心操作它。这可能吗/这里有最佳实践吗? 【参考方案1】:有趣的是,这个问题的所有其他 4 个答案都具有几乎相同的反序列化和序列化功能,因此您可以将事情简化为:
import Em from 'ember'
import DS from 'ember-data'
# Presumably based on these answers: http://***.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
这是使用ember-cli-coffees6 支持import/export
的咖啡脚本
【讨论】:
【参考方案2】:嗯...这有点困难,但在这篇文章中混合了所有答案,我成功了。
首先,您应该为新类型“数组”创建一个转换:
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);
现在,在您的模型中,只需将其用作另一个属性:
App.myModel = Ember.Model.extend(
name : DS.attr('string'),
cont : DS.attr('array')
我们完成了。 请记住,在向数组添加元素时,要使用 pushObject。
在控制器中:
this.get('model.cont').pushObject('new Item');
我希望这对某人有所帮助。
【讨论】:
效果很好,感谢您的示例和解释。 为什么不将其设为答案? 嘿@BrandonJMcKay,如何编辑数组并将其保存回有效负载。编辑现有数组时,我没有看到任何绑定。 @iosdev,嗯...为什么不问@Bommox?我在这里没有提供任何信息,但我想您可能正在寻找serializer 或adapter。转换会修改传入/传出的数据,但它适用于特定的数据类型。而已。如果您使用的是有效负载一词,我认为您需要一个序列化程序或一个适配器。【参考方案3】:在 Ember Data 1.0.0 Beta 中,人们可以“注册”他或她的自定义转换“子类”。我更愿意将其称为扩展的DS.Transform
对象。
DS.ArrayTransform = DS.Transform.extend(
deserialize: function(deserialized)
// ...
return deserialized;
,
serialize: function(serialized)
// ...
return serialized;
);
App.register('transform:array', DS.ArrayTransform);
【讨论】:
您能请教一下吗?让我们知道此代码需要添加到哪个文件中。 @RajaNagendraKumar 将其添加到您可以访问DS
变量的文件中。您将其作为导入变量还是全局变量?【参考方案4】:
我使用raw
转换,在 ember-data 修订版 11 中如下所示:
DS.RESTAdapter.registerTransform('raw',
deserialize: function(serialized)
return serialized;
,
serialize: function(deserialized)
return deserialized;
);
然后,在一个模型中,我这样做:
App.MyModel = Ember.Model.extend(
anArray: DS.attr('raw')
);
并且可以像在任何地方使用常规数组一样使用anArray
。
【讨论】:
这非常适合我的需求(FIXTURE 中的简单数组),使用 #each 来显示数组值。谢谢! 这是文档中显示的方法,所以我称之为“Ember 方式”:emberjs.com/api/data/classes/DS.Transform.html 此语法似乎已被弃用,请参阅@PatrickFisher 的链接 一个优点是这可以让您设置 DS.attr defaultValue,因为 DS.attr(null, defalutValue: []) 不起作用。我没有看到弃用通知?哪个版本/页面上的哪个位置?【参考方案5】:以下是在 Ember-Data(版本 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 []
现在您可以在模型属性中使用自定义类型:
App.CalenderWeek = DS.Model.extend
selected_days = DS.attr('array')
现在,当您通过以下方式获取记录时:
App.CalendarWeek.find(1)
这两个传入的 json 记录都会正确反序列化为一个数组:
selected_days: ['Monday', 'Tuesday', 'Saturday']
或
selected_days: 'Monday, Tuesday, Saturday'
【讨论】:
当我更改 selected_days 时,这不会在模型上设置 isDirty 标志。这是一个已知问题吗? @J.Mitchell 你能解决 isDirty 问题吗?甚至,我看不到模型随着车把中更改的值而更新【参考方案6】:这里的游戏有点晚了,但这是我发现的一个 jFiddle,它是声明新数组编解码器的简单实现
http://jsfiddle.net/Nook/ab2Xf/
【讨论】:
这似乎不适用于 EM 11。但 Shreyans 提交可以。【参考方案7】:如果您绝对需要与您的服务器交换自定义数据结构,例如,您可以丰富DS.attr.transforms
并声明一个新的array
编解码器。
请参阅source code 了解现有的属性编解码器实现。这是开始添加自己的好地方。
【讨论】:
确实是一个很好的实现。我的应用程序中也有同样的功能... :-) 如果使用此解决方案部分呈现您的数据 - 请查看此处的解决方法 -> github.com/emberjs/ember.js/issues/1990#issuecomment-13855767以上是关于如何在 ember-data 模型中表示数组?的主要内容,如果未能解决你的问题,请参考以下文章