┌─────────────────────────────────────────────────────────────────────────────┐
│ DAO EVENT FLOW │
└─────────────────────────────────────────────────────────────────────────────┘
write (put / remove / removeAll)
│
▼
┌───────────────┐ pub('on', 'put' | 'remove' | 'reset')
│ leaf DAO │ ──────────────────────────┐
│ (MDAO, client)│ │ event bubbles UP
└───────────────┘ │ (each decorator forwards 'on')
▲ │
│ delegate ▼
┌───────────────┐ ┌──────────────────┐
│ CachingDAO │ ◄───────────────── │ FilteredDAO │
│ ProxyDAO ... │ │ OrderedDAO ... │
└───────────────┘ └────────┬─────────┘
│
▼
┌──────────────────┐
│ view (listen) │ any event →
│ DAOSlot / table │ re-select → re-render
└──────────────────┘
A DAO fires an event when its data changes. Decorators relay it up the chain. The view listening at the top re-reads and re-renders. You only step in when no event fires on its own (server changed, or you want an on-demand reload).
The on topic is declared once on the base class (dao/AbstractDAO.js:44). A DAO
publishes against it; subscribers register with on (JS) or a listener (Java):
topics: [ { name: 'on', topics: [ 'put', 'remove', 'reset' ] } ]
| Event | Fires when | Payload |
|---|---|---|
put |
one row added / updated | the object |
remove |
one row deleted | the removed object |
reset |
bulk / opaque change, re-read from scratch | none |
removeAllfires oneremoveper row (MDAO.removeAll_:205), not onereset. JS subscribes against theontopic; Java registers alisteners_list (AbstractDAO.js:691-728).
dao.listen(sink) all 3 events → sink ← views use this
dao.on.put.sub(fn) one event type only
this.onDetach(dao.listen(mySink)); // preferred, all events
this.onDetach(dao.on.put.sub((sub,on,evt,obj) => {...})); // one type; ALWAYS onDetach or it leaks
Views wire this for you: DAOSlot.js:59, GroupingDAOList.js:83 (re-selects on any event).
Need the view to update?
│
├─ changed it via dao.put()? ............ do nothing (event auto-fires)
│
├─ re-read, data already correct in RAM . dao.on.reset.pub()
│
├─ re-read everywhere, no cache ......... dao.cmd(DAO.RESET_CMD)
│
└─ server changed / cache is stale ...... dao.cmd(DAO.PURGE_CMD)
| Call | Travels | Re-reads from |
|---|---|---|
dao.on.reset.pub() |
this level only | current chain (cache) |
dao.cmd(DAO.RESET_CMD) |
down to client DAO, reset bubbles up to all listeners (RequestResponseClientDAO.js:123) |
client cache |
dao.cmd(DAO.PURGE_CMD) |
down to CachingDAO (CachingDAO.cmd_:168) |
server |
cmd, not cmd_ (it supplies context). Constants: dao/DAO.js:28/:39.where()/inX() wrapper relays from its base, so the base reaches it; a different instance does not.EasyDAO with cache:true reads from an in-memory cache, writes through to source.
So RESET re-reads the stale cache and shows nothing. PURGE is required.
dao.cmd(PURGE_CMD)
→ CachingDAO.onSrcReset() (CachingDAO.js:198)
├─ clearPrivate_('delegate') → next select re-fetches from SERVER → 'put' → view fills
└─ cache.removeAll() → per-row 'remove' → bubbles up → view shrinks
Nobody pubs a literal reset. PURGE empties the cache, the cache MDAO fires remove/put,
and those drive the view.
this.onDetach(dao.listen(sink)); // react to all changes (preferred)
this.onDetach(dao.on.put.sub(fn)); // react to one event type
dao.on.reset.pub(); // local re-select (memory data only)
dao.cmd(foam.dao.DAO.RESET_CMD); // reset whole stack + all listeners (re-reads cache)
dao.cmd(foam.dao.DAO.PURGE_CMD); // dump cache, re-pull from server
this.pub('on', 'put', obj); // publish (custom leaf DAO only, after the mutation)
onDetach a raw on.*.sub.