/
compose.js
181 lines (164 loc) · 4.9 KB
/
compose.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import {Container, inject} from 'aurelia-dependency-injection';
import {TaskQueue} from 'aurelia-task-queue';
import {
CompositionEngine, ViewSlot, ViewResources,
customElement, bindable, noView, View
} from 'aurelia-templating';
import {DOM} from 'aurelia-pal';
/**
* Used to compose a new view / view-model template or bind to an existing instance.
*/
@customElement('compose')
@noView
@inject(DOM.Element, Container, CompositionEngine, ViewSlot, ViewResources, TaskQueue)
export class Compose {
/**
* Model to bind the custom element to.
*
* @property model
* @type {CustomElement}
*/
@bindable model
/**
* View to bind the custom element to.
*
* @property view
* @type {HtmlElement}
*/
@bindable view
/**
* View-model to bind the custom element's template to.
*
* @property viewModel
* @type {Class}
*/
@bindable viewModel
/**
* Creates an instance of Compose.
* @param element The Compose element.
* @param container The dependency injection container instance.
* @param compositionEngine CompositionEngine instance to compose the element.
* @param viewSlot The slot the view is injected in to.
* @param viewResources Collection of resources used to compile the the view.
* @param taskQueue The TaskQueue instance.
*/
constructor(element, container, compositionEngine, viewSlot, viewResources, taskQueue) {
this.element = element;
this.container = container;
this.compositionEngine = compositionEngine;
this.viewSlot = viewSlot;
this.viewResources = viewResources;
this.taskQueue = taskQueue;
this.currentController = null;
this.currentViewModel = null;
}
/**
* Invoked when the component has been created.
*
* @param owningView The view that this component was created inside of.
*/
created(owningView: View) {
this.owningView = owningView;
}
/**
* Used to set the bindingContext.
*
* @param bindingContext The context in which the view model is executed in.
* @param overrideContext The context in which the view model is executed in.
*/
bind(bindingContext, overrideContext) {
this.bindingContext = bindingContext;
this.overrideContext = overrideContext;
processInstruction(this, createInstruction(this, {
view: this.view,
viewModel: this.viewModel,
model: this.model
}));
}
/**
* Unbinds the Compose.
*/
unbind(bindingContext, overrideContext) {
this.bindingContext = null;
this.overrideContext = null;
let returnToCache = true;
let skipAnimation = true;
this.viewSlot.removeAll(returnToCache, skipAnimation);
}
/**
* Invoked everytime the bound model changes.
* @param newValue The new value.
* @param oldValue The old value.
*/
modelChanged(newValue, oldValue) {
if (this.currentInstruction) {
this.currentInstruction.model = newValue;
return;
}
this.taskQueue.queueMicroTask(() => {
if (this.currentInstruction) {
this.currentInstruction.model = newValue;
return;
}
let vm = this.currentViewModel;
if (vm && typeof vm.activate === 'function') {
vm.activate(newValue);
}
});
}
/**
* Invoked everytime the bound view changes.
* @param newValue The new value.
* @param oldValue The old value.
*/
viewChanged(newValue, oldValue) {
let instruction = createInstruction(this, {
view: newValue,
viewModel: this.currentViewModel || this.viewModel,
model: this.model
});
if (this.currentInstruction) {
this.currentInstruction = instruction;
return;
}
this.currentInstruction = instruction;
this.taskQueue.queueMicroTask(() => processInstruction(this, this.currentInstruction));
}
/**
* Invoked everytime the bound view model changes.
* @param newValue The new value.
* @param oldValue The old value.
*/
viewModelChanged(newValue, oldValue) {
let instruction = createInstruction(this, {
viewModel: newValue,
view: this.view,
model: this.model
});
if (this.currentInstruction) {
this.currentInstruction = instruction;
return;
}
this.currentInstruction = instruction;
this.taskQueue.queueMicroTask(() => processInstruction(this, this.currentInstruction));
}
}
function createInstruction(composer, instruction) {
return Object.assign(instruction, {
bindingContext: composer.bindingContext,
overrideContext: composer.overrideContext,
owningView: composer.owningView,
container: composer.container,
viewSlot: composer.viewSlot,
viewResources: composer.viewResources,
currentController: composer.currentController,
host: composer.element
});
}
function processInstruction(composer, instruction) {
composer.currentInstruction = null;
composer.compositionEngine.compose(instruction).then(controller => {
composer.currentController = controller;
composer.currentViewModel = controller ? controller.viewModel : null;
});
}