2

我有一个在整个代码库中使用的对象,我想扩展它,以便它可以像数组一样工作。我已经尝试使用 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'
      })
})
4

1 回答 1

1

我认为这可能有助于简化一点。请参阅TestMutableArray了解可能是最简单的实现(尽管我认为这不是严格要求的,除了性能 -如果不可用slice,它将使用)。MutableArray 将通过管道传输所有更改,因此您应该只需要在一个地方调用所有方法。objectAtslicereplacearrayContentWillChangearrayContentDidChange

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

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

于 2014-07-11T20:02:27.507 回答