The coach framework calls a coach view's handlers in these stages:
- load (once): the view's DOM exists but is not yet displayed; bindings and options are readable (this.context.binding.get("value")), this.context.element is there - initialise state, create widgets, subscribe to binding changes. For a composite view, the views placed in its content box are initialised first: the children's load handlers run before the parent's load handler, so the parent already sees fully built children. The reverse is not true - inside a child's load, this.context.parentView() is not yet initialised, so never call it there.
- view: the view is displayed - safe for measurements and third-party widgets that need a size (charts, maps), and for focus; it runs again when a hidden view becomes visible (tabs, sections toggled with setVisible).
- change: the bound data or a configuration option changed (from other views, services, setData). event.type is "binding" or "config", event.property names what changed, event.newVal / event.oldVal hold the values; for list bindings event.insertedInto or event.removedFrom give the index.
- validate (8.6+): runs when validation is requested (submit, or on demand) and can add validation errors on the binding.
- unload: the view is destroyed (coach ends, dynamic sections remove it, table rows leave) - detach widgets, timers and DOM listeners; leaks here add up in long portal sessions.
Order for Parent { Child A, Child B } (children in the parent's content box):
ChildA.load -> ChildB.load -> Parent.load // children are initialised before the parent's load handler
... view handlers when displayed (a parent's view handler can rely on its children being loaded)
change handlers whenever bound data / options change
unload handlers when the views are destroyed
// change handler pattern
change: function (event) {
if (event.type === "config") return; // option changed, not data
if (event.insertedInto !== undefined) this.renderRow(event.newVal, event.insertedInto);
else if (event.removedFrom !== undefined) this.removeRow(event.removedFrom);
else this.renderAll(this.context.binding.get("value"));
}Further rules: never return from load or unload handlers (a return statement there breaks the parent-view wiring and user-defined events - documented in an APAR); table and repeating-section rows instantiate their views on demand (load / view per row when rendered, unload when the row leaves), so keep per-row state in the bound data, not in the parent; modal sections load with the coach but run their view handler only when shown; and a service result arriving after unload must not touch the DOM - set a flag in unload and check it in the callback. To see the actual order in your coach, add console.log(this.context.viewid, "load") to each handler once.
References