Two data types - observable-map
and observable-vector
- will be provided that implement
an IObservableCollection protocol. observable-maps's and observable-vector's will basically
wrap an atom
(or atom
-like structure such as a cursor
) with operations that allow it to
be updated in an "observable" way.
The IObservableCollection
protocol will be the minimum necessary functionality required
to bind a collection to an items-view
. It will be completely orthogonal to the other
functionality provided by observable-map
's and observable-vector's so that database
collections, etc. can also implement IObservableCollection
and be bound to items-view's
without necessarily supporting operations of observable-map
's and observable-vector
's
such as assoc!
and conj!
.
;; observable-map's can be created with initial data
(observable-map {:a 1 :b 2})
;; or by passing in an atom (or an IAtom structure like a cursor)
(def my-data (atom {:some-map {:a 1 :b 2} :some-vector [1 2 3]}))
(def some-map (observable-map (cursor my-data :some-map)))
;; observable-map's would support the ITransientMap operations
(assoc! some-map :c 3)
(dissoc! some-map :a)
;; as well as the update! function which works like
;; (swap! my-atom update-in path f ...)
(update! some-map :c inc)
;; observable-vector's could be created similarly:
(observable-vector [1 2 3])
;; or
(def some-vector (observable-vector (cursor my-data :some-vector)))
;; and would have the same operations as ITransientVector
(conj! some-vector 4)
(assoc! some-vector 0 0)
(pop! some-vector)
;; as well as supporting update!
(update! some-vector 0 inc)
;; all assoc!, conj!, dissoc! and pop! operations will automatically
;; be contained within a transaction, but they can be batched using with-tx
(with-tx some-coll
(assoc! some-coll :d 4)
(assoc! some-coll :b -2))
Both observable-map
's and observable-vector
's will support the IObservableCollection
protocol:
(defprotocol IObservableCollection
"Defines the minimum protocol required for an observable collection
to be bound to an items-view"
(get-cursor [coll key-or-index]
"Gets a cursor for the element in the collection at the provided
key-or-index. A cursor to an item behaves like a reactive atom - it
will implement deref, swap!, add-watch, remove-watch.")
(get-keys [coll]
"Returns a sequence (possibly lazy) of keys curently in the collection
that can be passed as arguments to get-cursor to get a cursor to the
actual item.")
(add-collection-watch [coll key f]
"Adds a collection watch to the observable collection. f is a function
taking 4 arguments: the key, the collection, the sequence of keys or
indices that were removed, the sequence of keys or indices that were removed.")
(remove-collection-watch [coll key]))
Item cursors should also implement an IItemCursor protocol which will have a single function get-key that returns the key or index of the cursor in the collection (that could be used as an argument to get-cursor):
(defprotocol IItemCursor
(get-key [cursor}))
observable-map
's and observable-vector
's will also implement an additional protocol
ITransactableCollection
which may be useful for things like logging transactions but
not necessary for binding to an items-view
:
(defprotocol ITransactableCollection
(add-tx-watch [coll key f]
"Where f is a functional taking 3 arguments:
the key, the collection, a sequence of changes in the form of:
[[key1 new-value1]
[key2 new-value2]
[key3] ;; missing new value indicates the element was removed
]
Subject to change")
(remove-tx-watch [coll key))
PLEASE post comments here: aaronc/freactive#23
Gist does not support notifications on comments