为什么我的Ember MutableArray没有公布变化?

时间:2014-06-25 00:34:15

标签: arrays ember.js observers computed-field

我有一个在我的代码库中使用的对象,我想扩展它以便它可以像一个数组一样工作。我尝试过使用Ember的MutableArray mixin取得了一些成功,但我还是需要手动回复对象的变化(即调用foo.arrayContentWillChangefoo.arrayContentDidChange)。我已经解除了ArrayProxy的大部分代码,所以看起来应该很熟悉。

任何帮助将不胜感激。谢谢!

问题

我必须公布对数组的所有更改,如下所示:

index = model.indexOf(@get('element'))
model.arrayContentWillChange(index, 1, 0)
model.removeAt(index)
model.arrayContentDidChange(index, 1, 0)

我想像Ember数组一样使用我的对象:

model.removeObject(@get('element'))

如何使用对象:

module.exports = DraftCampaign = ArrayModel.extend({
  item : null
  page_options : null
  needs_loader : null

  embeds : {CampaignEntity : 'item'}       # The HAL API defines the name 'item'

  #
  # Alias the embeds array to content. This is used by ArrayModel to
  # expose a MutableArry interface.
  #
  content: Ember.computed.alias('item')

  init: ->
    @_super()
    @set('item', Ember.A([]))

  populate_fields: (data) ->                   # "item" is initialized here.
    @setAllProperties(@, data)
})

班级定义

module.exports = ArrayModel = MyModel.extend(Ember.MutableArray, {

  content : null
  arrangedContent : Ember.computed.alias('content')

  contentArrayWillChange : Ember.K
  contentArrayDidChange  : Ember.K

  arrangedContentWillChange : Ember.K
  arrangedContentDidChange  : Ember.K

  init: ->
    @_super()
    @_setupContent()
    @_setupArrangedContent()

  objectAt: (index) ->
    if Ember.get(@, 'content')
      @objectAtContent(index)
    else
      throw new Ember.Error 'content is undefined'

  length: ( ->
    arrangedContent = Ember.get(@, 'arrangedContent')
    if arrangedContent then Ember.get(arrangedContent, 'length') else 0
  ).property('arrangedContent.@each')

  replace: ->
    if (Ember.get(@, 'arrangedContent') is Ember.get(@, 'content'))
      @_replace.apply(@, arguments)
    else
      throw new Ember.Error 'Using replace on an arranged ArrayProxy is not allowed.'

  removeAt: (start, length = 1) ->
    if typeof start is 'number'
      content = Ember.get(@, 'content')
      arrangedContent = Ember.get(@, 'arrangedContent')
      indicies = []
    if ((start < 0) or (start >= Ember.get(@, 'length')))
      throw new Ember.Error('index out of range')
    [start...start + length].map (index) ->
      indicies.push(content.indexOf(arrangedContent.objectAt(index)))
    indicies.sort((a,b) -> b - a)
    Ember.beginPropertyChanges()
    [0...indicies.length].map (index) =>
      @_replace(indicies[index], 1, [])
    Ember.endPropertyChanges()

  pushObject: (obj) ->
    content = Ember.get(@, 'content')
    @_insertAt(Ember.get(content, 'length'), obj)
    return obj

  pushObjects: (objects) ->
    if (not(Ember.Enumerable.detect(objects) or Ember.isArray(objects)))
      throw new TypeError(
        'Must pass Ember.Enumerable to Ember.MutableArry#pushObjects'
      )
    @_replace(Ember.get(@, 'length'), 0, objects)
    return @

  setObjects: (objects) ->
    return @clear() unless objects.length
    length = Ember.get(@, 'length')
    @_replace(0, length, objects)
    return @

  unshiftObject: (obj) ->
    @_insertAt(0, obj)
    return obj

  slice: ->
    arr = @toArray()
    return arr.slice.apply(arr, arguments)

  arrangedContentArrayWillChange: (item, index, removedCnt, addedCnt) ->
    @arrayContentWillChange(index, removedCnt, addedCnt)

  arrangedContentArrayDidChange: (item, index, removedCnt, addedCnt) ->
    @arrayContentDidChange(index, removedCnt, addedCnt)

  arrayWillChange: ->
    @arrayContentWillChange(arguments[1], arguments[2], arguments[3])

  arrayDidChange: ->
    @arrayContentDidChange(arguments[1], arguments[2], arguments[3])

  willDestroy: ->
    @_teardownArrangedContent()
    @_teardownContent()

})

#
# Private Methods
#
AdStageArrayModel.reopen({
  objectAtContent: (index) ->
    Ember.get(@, 'arrangedContent').objectAt(index)

  replaceContent: (index, amt, objects) ->
    content = Ember.get(@, 'content')
    @_contentWillChagne()
    @arrayContentWillChange(index, amt, objects.length)
    content.replace(index, amt, objects)
    @arrayContentDidChange(index, amt, objects.length)
    @_contentDidChange()

  _contentWillChange: Ember.beforeObserver('content', ->
    @_teardownContent()
  )

  _teardownContent: ->
    content = Ember.get(@, 'content')
    if content
      content.removeArrayObserver(@, {
        willChange : 'contentArrayWillChange'
        didChange  : 'contentArrayDidChange'
      })

  _insertAt: (index, object) ->
    throw new Ember.Error('out of range') if (index > Ember.get(@, 'content.length'))
    @_replace(index, 0, [object])
    return @

  _replace: (index, amt, objects) ->
    content = Ember.get(@, 'content')
    Ember.assert('The content property of ' + @.constructor + ' should be set' +
      ' before modifying if', content)
    @replaceContent(index, amt, objects) if content
    return @

  _contentDidChange: Ember.observer('content', ->
    content = Ember.get(@, 'content')
    Ember.assert('Can\'t set ArrayProxy\'s content to itself', content is not @)
    @_setupContent()
  )

  _setupContent: ->
    content = Ember.get(@, 'content')
    if content
      Ember.assert(
        Ember.String.fmt('ArrayProxy expects an Array or ' +
        'Ember.ArrayProxy, but you passed %@', [typeof content]),
        Ember.isArray(content) || content.isDestroyed
      )
      content.addArrayObserver(@, ->
        willChange : 'contentArrayWillChange'
        didChange  : 'contentArrayDidChange'
      )

  _arrangedContentWillChange: Ember.beforeObserver('arrangedContent', ->
    arrangedContent = Ember.get(@, 'arrangedContent')
    len = if arrangedContent then Ember.get(arrangedContent, 'length') else 0
    @arrangedContentArrayWillChange(@, 0, len, undefined)
    @arrangedContentWillChange(@)
    @_teardownArrangedContent(arrangedContent)
  )

  _arrangedContentDidChange: Ember.observer('arrangedContent', ->
    arrangedContent = Ember.get(@, 'arrangedContent')
    len = if arrangedContent then Ember.get(arrangedContent, 'length') else 0
    Ember.assert(
      'Can\'t set ArrayProxy\'s content to itself',
      arrangedContent is not @
    )
    @_setupArrangedContent()
    @arrangedContentDidChange(@)
    @arrangedContentArrayDidChange(@, 0, undefined, len)
  )

  _setupArrangedContent: ->
    arrangedContent = Ember.get(@, 'arrangedContent')
    if arrangedContent
      Ember.assert(Ember.String.fmt('ArrayProxy expects an Array or ' +
        'Ember.ArrayProxy, but you passed %@', [typeof arrangedContent]),
        Ember.isArray(arrangedContent) || arrangedContent.isDestroyed)
      arrangedContent.addArrayObserver(@, {
        willChange : 'arrangedContentArrayWillChange'
        didChange  : 'arrangedContentArrayDidChange'
      })

  _teardownArrangedContent: ->
    arrangedContent = Ember.get(@, 'arrangedContent')
    if arrangedContent
      arrangedContent.removeArrayObserver(@, {
        willChange : 'arrangedContentArrayWillChange'
        didChange  : 'arrangedContentArrayDidChange'
      })
})

1 个答案:

答案 0 :(得分:1)

我认为这可能有助于简化一下。请参阅TestMutableArray了解最简单的实现(尽管我认为slice是严格要求的,除了性能之外 - 如果objectAt不是slice,它将使用replace可用)。 MutableArray将通过arrayContentWillChange管理所有更改,因此您只需为所有方法在该位置调用arrayContentDidChangelength

作为参考,这里是我发现实现MutableArray的所有类。其中许多实现了额外的方法,但我认为它们主要是为那些特定的实现需要进行额外的簿记:

注意:Ember文档未按要求提及replace,但没有它{{1}}会传递一些奇怪的值,而某些方法似乎不起作用。