Почему мой Ember MutableArray не объявляет об изменениях?

У меня есть объект, который используется во всей нашей кодовой базе, и я хочу расширить его, чтобы он мог действовать как массив. Я попытался использовать мибин MutableArray от Ember с некоторым успехом, но мне все еще нужно объявить изменения объекта вручную (т.е. вызвать foo.arrayContentWillChange а также foo.arrayContentDidChange). Я снял большую часть кода с ArrayProxy так что это должно выглядеть довольно знакомо.

Любая помощь будет оценена. Спасибо!

проблема

Я должен объявить все свои изменения в массиве так:

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

Я хотел бы использовать мой объект как Ember Array:

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 ответ

Я думаю, что это может помочь немного упростить. См. TestMutableArray для того, что, вероятно, является самой простой реализацией (хотя я не думаю, slice строго требуется, кроме того, для выполнения - он будет использовать objectAt если slice не доступен). MutableArray направит все изменения через replace так что вам нужно только позвонить arrayContentWillChange а также arrayContentDidChange в этом одном месте для всех методов.

Для справки, вот все классы, которые я обнаружил, которые реализуют MutableArray. Многие из них реализуют дополнительные методы, но я думаю, что в основном они предназначены для дополнительной бухгалтерии, которую необходимо было сделать для этих конкретных реализаций:

Примечание: в документации Ember не упоминается length по мере необходимости, но без него replace получает некоторые странные значения, а некоторые методы не работают.

Другие вопросы по тегам