Search code examples
knockout.jscoffeescriptko.observablearray

beforeChange in manual subscribe in knockoutjs with strange behavior


In my viewmodel have a list of files, file types and unused files type:

viewModel =
  files: ko.observableArray([
        new File({name: "A1", id: 1})
        new File({name: "B1", id: 2})
        new File({name: "C1", id: 3})
        new File({name: "D1", id: 4})
    ])
  types:  ko.observableArray(dataTypes)
  typesUnused: ko.observableArray(dataTypes)

I created a File class, and in it a type property. To fill list not used file types , have a subscribe beforeChange and other afterChange for type property

class File
  constructor: (data) ->
    @name = data.name
    @id = data.id
    @type = ko.observable(null)

    # Updates array of types not used
    @type.subscribe ((oldValue) ->
      console.log "OLD: #{oldValue}"
      viewModel.typesUnused.remove (item) -> item is oldValue if oldValue
    ), @, "beforeChange"
    @type.subscribe (newValue) ->        
      console.log "NEW: #{newValue}"
      if newValue && viewModel.typesUnused.indexOf(newValue) < 0 
        viewModel.typesUnused.push newValue

Idea
The idea is whenever you change the file type, an event is fired before the change will occur and add the old type in the list of file types not used. And another event will occur after the change that will remove the new type from the list of types not used.

Problem

  1. All files comes with the first type in the array of file types defined. In this case the "jpeg"
  2. By changing the file type, all others suffer the change!
  3. When you remove a file type from the list typesUnused, the types list may also be changed!

See: http://jsfiddle.net/4nyVE/2/


Solution

  • Your issue is that your types and typesUnused are initialized with the same array dataTypes. When items are removed from typesUnused this affects types as well since they contain a reference to the same array.

    At that point, the currently selected option in the other selects is no longer valid (no longer in types), so the value is set to the first choice.

    Your best bet is probably to initialize typesUnused with a copy of the original array (myarray.slice(0) is an easy way to copy) like:

    typesUnused: ko.observableArray(dataTypes.slice(0))