Events
Model events are based on the standard Node.js EventEmitter methods, and they support the same methods: on, once, removeListener, emit, etc.
Mutation events
Racer emits events whenever it mutates data via model.set(), model.push(), etc. It also emits events when data is remotely updated via a subscription. These events provide an entry point for an app to react to a specific data mutation or pattern of data mutations. The events might not be exactly the same as the methods that created them, since they can be transformed via OT.
model.on() and model.once() accept a second argument for these mutation events. The second argument is a path pattern that will filter emitted events, calling the handler function only when a mutator matches the pattern. Path patterns support a single segment wildcard (*) anywhere in a path, and a multi-segment wildcard (**) at the end of the path. The multi-segment wildcard alone ('**') matches all paths.
listener = model.on(method, path, [options], eventCallback)
methodName of the event to listen to:'change','insert','remove','move','load','unload', or'all'pathPattern matching the path to listen to. For example:'_page.user','users.*.name','users.*','users.**'/'users**', or'**'.**is valid only by itself or at the end of the path.options(optional)
useEventObjects- If true, the callback is called with a structured event object instead of with a variable number of arguments. Introduced in racer@0.9.6.eventCallbackFunction to call when a matching method and path are mutated- Returns
listener- the listener function subscribed to the event emitter. This is the function that should be passed tomodel.removeListener
New code should use the {useEventObjects: true} option, since the structured event objects are easier to work with. The TypeScript definitions require the option, since the legacy callback’s dynamic arguments are impossible to type correctly.
Event callbacks with {useEventObjects: true}
Introduced in racer@0.9.6.
eventCallback(event, captures)
event- Object - An instance of an Event object (see below)captures- Array<string | string[]> - The captured path segments, one item per wildcard in the pattern. Each'*'results in a string, and a'**'results in a sub-array of strings.
Event objects:
ChangeEvent { value, previous, passed }
type: 'change'valueThe current value at the path that was changed. Will beundefinedfor a deletion.previousThe previous value at the path. Will beundefinedif the path was previously unset.
InsertEvent { index, values, passed }
type: 'insert'indexThe index at which items were insertedvaluesAn array of values that were inserted. Always an array, even if only one item was pushed, unshifted, or inserted.
RemoveEvent { index, removed, passed }
type: 'remove'indexThe index at which items were removedremovedAn array of values that were removed. Always an array, even if only one item was popped, shifted, or removed
MoveEvent { from, to, howMany, passed }
type: 'move'fromThe index from which items were movedtoThe index to which items were movedhowManyHow many items were moved
LoadEvent { document, passed }
type: 'load'documentThis event fires when a document is loaded via a subscription or fetch. This the value of the newly loaded document object.
UnloadEvent { previousDocument, passed }
type: 'unload'previousDocumentThis event fires when a document is removed from the model via unsubscribe or unfetch. This is the value of the document object that was unloaded.
The event.type is useful for distinguising the actual event type when listening to 'all'.
// Matches model.push('messages', message)
model.on('insert', 'messages', {useEventObjects: true}, function(insertEvent) {
console.log(insertEvent.values, 'inserted at index', insertEvent.index);
});
// Matches model.set('todos.4.completed', true), etc.
model.on('change', 'todos.*.completed', {useEventObjects: true}, function(changeEvent, captures) {
console.log('todos.' + captures[0] + ' set to ' + changeEvent.value);
});
// Matches all events
model.on('all', '**', {useEventObjects: true}, function(event, captures) {
var starStarSegments = captures[0];
console.log(event.type + ' at ' + starStarSegments.join('.') + ':', event);
});
Legacy event callbacks, when useEventObjects is false or undefined
Click here to show documentation for legacy event callbacks
The event callback receives a number of arguments based on the path pattern and method. The arguments are:
eventCallback([captures...], [eventType], args..., passed)
capturesThe path segment or segments that is passed in only when matching wildcards in the path patterneventTypeOnly the'all'event adds the emitted event name after the captures and before the argsargsEvent specific arguments. See belowpassedAn object with properties provided viamodel.pass(). See description below
Callbacks for each event type:
changeCallback([captures...], value, previous, passed)
valueThe current value at the path that was changed. Will beundefinedfor objects that were deletedpreviousThe previous value at the path. Will beundefinedfor paths set for the first time
insertCallback([captures...], index, values, passed)
indexThe index at which items were insertedvaluesAn array of values that were inserted. Always an array, even if only one item was pushed, unshifted, or inserted
removeCallback([captures...], index, removed, passed)
indexThe index at which items were removedremovedAn array of values that were removed. Always an array, even if only one item was popped, shifted, or removed
moveCallback([captures...], from, to, howMany, passed)
fromThe index from which items were movedtoThe index to which items were movedhowManyHow many items were moved
loadCallback([captures...], document, passed)
documentThis event fires when a document is loaded via a subscription or fetch. It emits the value of the newly loaded document object
unloadCallback([captures...], previousDocument, passed)
previousDocumentThis event fires when a document is removed from the model via unsubscribe or unfetch. It emits the value of the document object that was unloaded
// Matches model.push('messages', message)
model.on('insert', 'messages', function(index, [message]) {
...
});
// Matches model.set('todos.4.completed', true), etc.
model.on('change', 'todos.*.completed', function(todoId, isComplete) {
...
});
// Matches all events - `path` and `event` are passed in to the event callback
model.on('all', '**', function(path, event, args...) {
...
});
Passing data to event listeners
model.pass(object)
objectAn object whose properties will each be set on thepassedargument
model.pass() can be chained before calling a mutator method to pass an argument to model event listeners. You must pass it an object with a property that identifies the name of the parameter.
This value is only passed to local listeners, and it is not sent to the server or other clients. It is typically used to identify the originator of a particular mutation so that multiple responses to the same change and infinite loops may be avoided. Such loops could occur for listeners that respond to paths that they may modify.
On a string insert or string remove mutation, a 'change’ event is emitted, since strings are immutable values, and inserting or removing from a string requires changing its entire value. However, detail on what specifically was inserted or removed is neccessary to implement view bindings properly for realtime collaborative text editing. This additional information is added to the passed object. On a string insert, the passed object has an additional property of $stringInsert: {index: Number, text: String}. On a string remove, the passed object has an additional property of $stringRemove: {index: Number, howMany: Number}.
// Logs:
// 'red', {}
// 'green', {message: 'hi'}
model.on('change', 'color', function(value, previous, passed) {
console.log(value, passed);
});
model.set('color', 'red');
model.pass({message: 'hi'}).set('color', 'green');