Skip to content

Commit

Permalink
feat(context): emit bind/unbind events on ContextView
Browse files Browse the repository at this point in the history
This allows applications to listen on an injected context view to react
on bind/unbind events in addition to refresh for more fine-grained update.
  • Loading branch information
raymondfeng committed Mar 18, 2020
1 parent 18c229c commit 65e3d38
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 3 deletions.
2 changes: 2 additions & 0 deletions docs/site/Context.md
Expand Up @@ -554,6 +554,8 @@ injection.

A `ContextView` object can emit one of the following events:

- 'bind': when a binding is added to the view
- 'unbind': when a binding is removed from the view
- 'refresh': when the view is refreshed as bindings are added/removed
- 'resolve': when the cached values are resolved and updated
- 'close': when the view is closed (stopped observing context events)
Expand Down
28 changes: 26 additions & 2 deletions packages/context/src/__tests__/unit/context-view.unit.ts
Expand Up @@ -10,6 +10,7 @@ import {
BindingTag,
compareBindingsByTag,
Context,
ContextEvent,
ContextView,
createViewGetter,
filterByTag,
Expand Down Expand Up @@ -156,12 +157,35 @@ describe('ContextView', () => {
.to('XYZ')
.tag('foo');
await taggedAsFoo.values();
expect(events).to.eql(['refresh', 'resolve']);
expect(events).to.eql(['bind', 'refresh', 'resolve']);
});

it('emits bind/unbind when bindings are changed', async () => {
const bindingEvents: ContextEvent[] = [];
taggedAsFoo.on('bind', evt => {
bindingEvents.push(evt);
});
taggedAsFoo.on('unbind', evt => {
bindingEvents.push(evt);
});
const binding = server
.bind('xyz')
.to('XYZ')
.tag('foo');
await taggedAsFoo.values();
const context = server;
expect(bindingEvents).to.eql([{type: 'bind', binding, context}]);
server.unbind('xyz');
await taggedAsFoo.values();
expect(bindingEvents).to.eql([
{type: 'bind', binding, context},
{type: 'unbind', binding, context},
]);
});

function setupListeners() {
events = [];
['open', 'close', 'refresh', 'resolve'].forEach(t =>
['open', 'close', 'refresh', 'resolve', 'bind', 'unbind'].forEach(t =>
taggedAsFoo.on(t, () => events.push(t)),
);
}
Expand Down
13 changes: 12 additions & 1 deletion packages/context/src/context-view.ts
Expand Up @@ -10,6 +10,7 @@ import {Binding} from './binding';
import {BindingFilter} from './binding-filter';
import {BindingComparator} from './binding-sorter';
import {Context} from './context';
import {ContextEvent} from './context-event';
import {ContextEventType, ContextObserver} from './context-observer';
import {Subscription} from './context-subscription';
import {Getter} from './inject';
Expand Down Expand Up @@ -97,7 +98,17 @@ export class ContextView<T = unknown> extends EventEmitter
/**
* Listen on `bind` or `unbind` and invalidate the cache
*/
observe(event: ContextEventType, binding: Readonly<Binding<unknown>>) {
observe(
event: ContextEventType,
binding: Readonly<Binding<unknown>>,
context: Context,
) {
const ctxEvent: ContextEvent = {
context,
binding,
type: event,
};
this.emit(event, ctxEvent);
this.refresh();
}

Expand Down

0 comments on commit 65e3d38

Please sign in to comment.