ember-source 4.4.0-beta.1 → 4.5.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-metadata.json +3 -3
- package/dist/ember-template-compiler.js +4 -4
- package/dist/ember-template-compiler.map +1 -1
- package/dist/ember-testing.js +1 -1
- package/dist/ember-testing.map +1 -1
- package/dist/ember.debug.js +739 -869
- package/dist/ember.debug.map +1 -1
- package/dist/header/license.js +1 -1
- package/dist/packages/@ember/-internals/container/index.js +3 -10
- package/dist/packages/@ember/-internals/glimmer/index.js +19 -7
- package/dist/packages/@ember/-internals/owner/index.js +3 -0
- package/dist/packages/@ember/-internals/routing/lib/system/router.js +3 -0
- package/dist/packages/@ember/-internals/runtime/lib/system/namespace.js +15 -9
- package/dist/packages/@ember/-internals/views/lib/views/core_view.js +4 -6
- package/dist/packages/@ember/application/instance.js +93 -171
- package/dist/packages/@ember/application/lib/application.js +201 -304
- package/dist/packages/@ember/application/lib/lazy_load.js +8 -7
- package/dist/packages/@ember/canary-features/index.js +2 -2
- package/dist/packages/@ember/engine/index.js +313 -275
- package/dist/packages/@ember/engine/instance.js +74 -72
- package/dist/packages/ember/version.js +1 -1
- package/docs/data.json +350 -406
- package/package.json +3 -3
|
@@ -167,156 +167,63 @@ import { RouterService } from '@ember/-internals/routing';
|
|
|
167
167
|
|
|
168
168
|
@class Application
|
|
169
169
|
@extends Engine
|
|
170
|
-
@uses RegistryProxyMixin
|
|
171
170
|
@public
|
|
172
171
|
*/
|
|
173
172
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
view in your application should be a child of the element you specify here.
|
|
181
|
-
@property rootElement
|
|
182
|
-
@type DOMElement
|
|
183
|
-
@default 'body'
|
|
184
|
-
@public
|
|
185
|
-
*/
|
|
186
|
-
rootElement: 'body',
|
|
187
|
-
|
|
173
|
+
class Application extends Engine {
|
|
174
|
+
constructor() {
|
|
175
|
+
super(...arguments);
|
|
176
|
+
this._bootPromise = null;
|
|
177
|
+
this._bootResolver = null;
|
|
178
|
+
}
|
|
188
179
|
/**
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
180
|
+
This creates a registry with the default Ember naming conventions.
|
|
181
|
+
It also configures the registry:
|
|
182
|
+
* registered views are created every time they are looked up (they are
|
|
183
|
+
not singletons)
|
|
184
|
+
* registered templates are not factories; the registered value is
|
|
185
|
+
returned directly.
|
|
186
|
+
* the router receives the application as its `namespace` property
|
|
187
|
+
* all controllers receive the router as their `target` and `controllers`
|
|
188
|
+
properties
|
|
189
|
+
* all controllers receive the application as their `namespace` property
|
|
190
|
+
* the application view receives the application controller as its
|
|
191
|
+
`controller` property
|
|
192
|
+
* the application view receives the application template as its
|
|
193
|
+
`defaultTemplate` property
|
|
194
|
+
@method buildRegistry
|
|
195
|
+
@static
|
|
196
|
+
@param {Application} namespace the application for which to
|
|
197
|
+
build the registry
|
|
198
|
+
@return {Ember.Registry} the built registry
|
|
192
199
|
@private
|
|
193
200
|
*/
|
|
194
|
-
_document: hasDOM ? window.document : null,
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
The `Ember.EventDispatcher` responsible for delegating events to this
|
|
198
|
-
application's views.
|
|
199
|
-
The event dispatcher is created by the application at initialization time
|
|
200
|
-
and sets up event listeners on the DOM element described by the
|
|
201
|
-
application's `rootElement` property.
|
|
202
|
-
See the documentation for `Ember.EventDispatcher` for more information.
|
|
203
|
-
@property eventDispatcher
|
|
204
|
-
@type Ember.EventDispatcher
|
|
205
|
-
@default null
|
|
206
|
-
@public
|
|
207
|
-
*/
|
|
208
|
-
eventDispatcher: null,
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
The DOM events for which the event dispatcher should listen.
|
|
212
|
-
By default, the application's `Ember.EventDispatcher` listens
|
|
213
|
-
for a set of standard DOM events, such as `mousedown` and
|
|
214
|
-
`keyup`, and delegates them to your application's `Ember.View`
|
|
215
|
-
instances.
|
|
216
|
-
If you would like additional bubbling events to be delegated to your
|
|
217
|
-
views, set your `Application`'s `customEvents` property
|
|
218
|
-
to a hash containing the DOM event name as the key and the
|
|
219
|
-
corresponding view method name as the value. Setting an event to
|
|
220
|
-
a value of `null` will prevent a default event listener from being
|
|
221
|
-
added for that event.
|
|
222
|
-
To add new events to be listened to:
|
|
223
|
-
```app/app.js
|
|
224
|
-
import Application from '@ember/application';
|
|
225
|
-
let App = Application.extend({
|
|
226
|
-
customEvents: {
|
|
227
|
-
// add support for the paste event
|
|
228
|
-
paste: 'paste'
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
```
|
|
232
|
-
To prevent default events from being listened to:
|
|
233
|
-
```app/app.js
|
|
234
|
-
import Application from '@ember/application';
|
|
235
|
-
let App = Application.extend({
|
|
236
|
-
customEvents: {
|
|
237
|
-
// remove support for mouseenter / mouseleave events
|
|
238
|
-
mouseenter: null,
|
|
239
|
-
mouseleave: null
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
```
|
|
243
|
-
@property customEvents
|
|
244
|
-
@type Object
|
|
245
|
-
@default null
|
|
246
|
-
@public
|
|
247
|
-
*/
|
|
248
|
-
customEvents: null,
|
|
249
201
|
|
|
250
|
-
/**
|
|
251
|
-
Whether the application should automatically start routing and render
|
|
252
|
-
templates to the `rootElement` on DOM ready. While default by true,
|
|
253
|
-
other environments such as FastBoot or a testing harness can set this
|
|
254
|
-
property to `false` and control the precise timing and behavior of the boot
|
|
255
|
-
process.
|
|
256
|
-
@property autoboot
|
|
257
|
-
@type Boolean
|
|
258
|
-
@default true
|
|
259
|
-
@private
|
|
260
|
-
*/
|
|
261
|
-
autoboot: true,
|
|
262
202
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
import Component from '@ember/component';
|
|
270
|
-
let App = Application.create({
|
|
271
|
-
...
|
|
272
|
-
});
|
|
273
|
-
App.Router.reopen({
|
|
274
|
-
location: 'none'
|
|
275
|
-
});
|
|
276
|
-
App.Router.map({
|
|
277
|
-
...
|
|
278
|
-
});
|
|
279
|
-
App.MyComponent = Component.extend({
|
|
280
|
-
...
|
|
281
|
-
});
|
|
282
|
-
```
|
|
283
|
-
This flag also exposes other internal APIs that assumes the existence of
|
|
284
|
-
a special "default instance", like `App.__container__.lookup(...)`.
|
|
285
|
-
This option is currently not configurable, its value is derived from
|
|
286
|
-
the `autoboot` flag – disabling `autoboot` also implies opting-out of
|
|
287
|
-
globals mode support, although they are ultimately orthogonal concerns.
|
|
288
|
-
Some of the global modes features are already deprecated in 1.x. The
|
|
289
|
-
existence of this flag is to untangle the globals mode code paths from
|
|
290
|
-
the autoboot code paths, so that these legacy features can be reviewed
|
|
291
|
-
for deprecation/removal separately.
|
|
292
|
-
Forcing the (autoboot=true, _globalsMode=false) here and running the tests
|
|
293
|
-
would reveal all the places where we are still relying on these legacy
|
|
294
|
-
behavior internally (mostly just tests).
|
|
295
|
-
@property _globalsMode
|
|
296
|
-
@type Boolean
|
|
297
|
-
@default true
|
|
298
|
-
@private
|
|
299
|
-
*/
|
|
300
|
-
_globalsMode: true,
|
|
203
|
+
static buildRegistry(namespace) {
|
|
204
|
+
let registry = super.buildRegistry(namespace);
|
|
205
|
+
commonSetupRegistry(registry);
|
|
206
|
+
setupApplicationRegistry(registry);
|
|
207
|
+
return registry;
|
|
208
|
+
}
|
|
301
209
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
internally to ensure that all instances get destroyed.
|
|
305
|
-
@property _applicationInstances
|
|
306
|
-
@type Array
|
|
307
|
-
@default null
|
|
308
|
-
@private
|
|
309
|
-
*/
|
|
310
|
-
_applicationInstances: null,
|
|
210
|
+
init(properties) {
|
|
211
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
311
212
|
|
|
312
|
-
|
|
313
|
-
this.
|
|
213
|
+
super.init(properties);
|
|
214
|
+
(_a = this.rootElement) !== null && _a !== void 0 ? _a : this.rootElement = 'body';
|
|
215
|
+
(_b = this._document) !== null && _b !== void 0 ? _b : this._document = null;
|
|
216
|
+
(_c = this.eventDispatcher) !== null && _c !== void 0 ? _c : this.eventDispatcher = null;
|
|
217
|
+
(_d = this.customEvents) !== null && _d !== void 0 ? _d : this.customEvents = null;
|
|
218
|
+
(_e = this.autoboot) !== null && _e !== void 0 ? _e : this.autoboot = true;
|
|
219
|
+
(_f = this._document) !== null && _f !== void 0 ? _f : this._document = hasDOM ? window.document : null;
|
|
220
|
+
(_g = this._globalsMode) !== null && _g !== void 0 ? _g : this._globalsMode = true;
|
|
314
221
|
|
|
315
222
|
if (DEBUG) {
|
|
316
223
|
if (ENV.LOG_VERSION) {
|
|
317
224
|
// we only need to see this once per Application#init
|
|
318
225
|
ENV.LOG_VERSION = false;
|
|
319
|
-
libraries.logVersions();
|
|
226
|
+
(_h = libraries.logVersions) === null || _h === void 0 ? void 0 : _h.call(libraries);
|
|
320
227
|
}
|
|
321
228
|
} // Start off the number of deferrals at 1. This will be decremented by
|
|
322
229
|
// the Application's own `boot` method.
|
|
@@ -334,51 +241,56 @@ const Application = Engine.extend({
|
|
|
334
241
|
if (this.autoboot) {
|
|
335
242
|
this.waitForDOMReady();
|
|
336
243
|
}
|
|
337
|
-
}
|
|
338
|
-
|
|
244
|
+
}
|
|
339
245
|
/**
|
|
340
246
|
Create an ApplicationInstance for this application.
|
|
341
|
-
|
|
247
|
+
@public
|
|
342
248
|
@method buildInstance
|
|
343
249
|
@return {ApplicationInstance} the application instance
|
|
344
250
|
*/
|
|
251
|
+
|
|
252
|
+
|
|
345
253
|
buildInstance(options = {}) {
|
|
346
254
|
assert('You cannot build new instances of this application since it has already been destroyed', !this.isDestroyed);
|
|
347
255
|
assert('You cannot build new instances of this application since it is being destroyed', !this.isDestroying);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
256
|
+
return ApplicationInstance.create(Object.assign(Object.assign({}, options), {
|
|
257
|
+
base: this,
|
|
258
|
+
application: this
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
353
261
|
/**
|
|
354
262
|
Start tracking an ApplicationInstance for this application.
|
|
355
263
|
Used when the ApplicationInstance is created.
|
|
356
|
-
|
|
264
|
+
@private
|
|
357
265
|
@method _watchInstance
|
|
358
266
|
*/
|
|
267
|
+
|
|
268
|
+
|
|
359
269
|
_watchInstance(instance) {
|
|
360
270
|
this._applicationInstances.add(instance);
|
|
361
|
-
}
|
|
362
|
-
|
|
271
|
+
}
|
|
363
272
|
/**
|
|
364
273
|
Stop tracking an ApplicationInstance for this application.
|
|
365
274
|
Used when the ApplicationInstance is about to be destroyed.
|
|
366
|
-
|
|
275
|
+
@private
|
|
367
276
|
@method _unwatchInstance
|
|
368
277
|
*/
|
|
278
|
+
|
|
279
|
+
|
|
369
280
|
_unwatchInstance(instance) {
|
|
370
281
|
return this._applicationInstances.delete(instance);
|
|
371
|
-
}
|
|
372
|
-
|
|
282
|
+
}
|
|
373
283
|
/**
|
|
374
284
|
Enable the legacy globals mode by allowing this application to act
|
|
375
285
|
as a global namespace. See the docs on the `_globalsMode` property
|
|
376
286
|
for details.
|
|
377
|
-
|
|
287
|
+
Most of these features are already deprecated in 1.x, so we can
|
|
378
288
|
stop using them internally and try to remove them.
|
|
379
|
-
|
|
289
|
+
@private
|
|
380
290
|
@method _prepareForGlobalsMode
|
|
381
291
|
*/
|
|
292
|
+
|
|
293
|
+
|
|
382
294
|
_prepareForGlobalsMode() {
|
|
383
295
|
// Create subclass of Router for this Application instance.
|
|
384
296
|
// This is to ensure that someone reopening `App.Router` does not
|
|
@@ -386,20 +298,21 @@ const Application = Engine.extend({
|
|
|
386
298
|
this.Router = (this.Router || Router).extend();
|
|
387
299
|
|
|
388
300
|
this._buildDeprecatedInstance();
|
|
389
|
-
}
|
|
390
|
-
|
|
301
|
+
}
|
|
391
302
|
/*
|
|
392
303
|
Build the deprecated instance for legacy globals mode support.
|
|
393
304
|
Called when creating and resetting the application.
|
|
394
|
-
|
|
305
|
+
This is orthogonal to autoboot: the deprecated instance needs to
|
|
395
306
|
be created at Application construction (not boot) time to expose
|
|
396
307
|
App.__container__. If autoboot sees that this instance exists,
|
|
397
308
|
it will continue booting it to avoid doing unncessary work (as
|
|
398
309
|
opposed to building a new instance at boot time), but they are
|
|
399
310
|
otherwise unrelated.
|
|
400
|
-
|
|
311
|
+
@private
|
|
401
312
|
@method _buildDeprecatedInstance
|
|
402
313
|
*/
|
|
314
|
+
|
|
315
|
+
|
|
403
316
|
_buildDeprecatedInstance() {
|
|
404
317
|
// Build a default instance
|
|
405
318
|
let instance = this.buildInstance(); // Legacy support for App.__container__ and other global methods
|
|
@@ -407,42 +320,43 @@ const Application = Engine.extend({
|
|
|
407
320
|
|
|
408
321
|
this.__deprecatedInstance__ = instance;
|
|
409
322
|
this.__container__ = instance.__container__;
|
|
410
|
-
}
|
|
411
|
-
|
|
323
|
+
}
|
|
412
324
|
/**
|
|
413
325
|
Automatically kick-off the boot process for the application once the
|
|
414
326
|
DOM has become ready.
|
|
415
|
-
|
|
327
|
+
The initialization itself is scheduled on the actions queue which
|
|
416
328
|
ensures that code-loading finishes before booting.
|
|
417
|
-
|
|
329
|
+
If you are asynchronously loading code, you should call `deferReadiness()`
|
|
418
330
|
to defer booting, and then call `advanceReadiness()` once all of your code
|
|
419
331
|
has finished loading.
|
|
420
|
-
|
|
332
|
+
@private
|
|
421
333
|
@method waitForDOMReady
|
|
422
334
|
*/
|
|
335
|
+
|
|
336
|
+
|
|
423
337
|
waitForDOMReady() {
|
|
424
|
-
|
|
425
|
-
|
|
338
|
+
const document = this._document;
|
|
339
|
+
|
|
340
|
+
if (document === null || document.readyState !== 'loading') {
|
|
341
|
+
schedule('actions', this, this.domReady);
|
|
426
342
|
} else {
|
|
427
343
|
let callback = () => {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
run(this, 'domReady');
|
|
344
|
+
document.removeEventListener('DOMContentLoaded', callback);
|
|
345
|
+
run(this, this.domReady);
|
|
431
346
|
};
|
|
432
347
|
|
|
433
|
-
|
|
348
|
+
document.addEventListener('DOMContentLoaded', callback);
|
|
434
349
|
}
|
|
435
|
-
}
|
|
436
|
-
|
|
350
|
+
}
|
|
437
351
|
/**
|
|
438
352
|
This is the autoboot flow:
|
|
439
|
-
|
|
353
|
+
1. Boot the app by calling `this.boot()`
|
|
440
354
|
2. Create an instance (or use the `__deprecatedInstance__` in globals mode)
|
|
441
355
|
3. Boot the instance by calling `instance.boot()`
|
|
442
356
|
4. Invoke the `App.ready()` callback
|
|
443
357
|
5. Kick-off routing on the instance
|
|
444
|
-
|
|
445
|
-
|
|
358
|
+
Ideally, this is all we would need to do:
|
|
359
|
+
```javascript
|
|
446
360
|
_autoBoot() {
|
|
447
361
|
this.boot().then(() => {
|
|
448
362
|
let instance = (this._globalsMode) ? this.__deprecatedInstance__ : this.buildInstance();
|
|
@@ -453,16 +367,18 @@ const Application = Engine.extend({
|
|
|
453
367
|
});
|
|
454
368
|
}
|
|
455
369
|
```
|
|
456
|
-
|
|
370
|
+
Unfortunately, we cannot actually write this because we need to participate
|
|
457
371
|
in the "synchronous" boot process. While the code above would work fine on
|
|
458
372
|
the initial boot (i.e. DOM ready), when `App.reset()` is called, we need to
|
|
459
373
|
boot a new instance synchronously (see the documentation on `_bootSync()`
|
|
460
374
|
for details).
|
|
461
|
-
|
|
375
|
+
Because of this restriction, the actual logic of this method is located
|
|
462
376
|
inside `didBecomeReady()`.
|
|
463
|
-
|
|
377
|
+
@private
|
|
464
378
|
@method domReady
|
|
465
379
|
*/
|
|
380
|
+
|
|
381
|
+
|
|
466
382
|
domReady() {
|
|
467
383
|
if (this.isDestroying || this.isDestroyed) {
|
|
468
384
|
return;
|
|
@@ -470,45 +386,47 @@ const Application = Engine.extend({
|
|
|
470
386
|
|
|
471
387
|
this._bootSync(); // Continues to `didBecomeReady`
|
|
472
388
|
|
|
473
|
-
}
|
|
474
|
-
|
|
389
|
+
}
|
|
475
390
|
/**
|
|
476
391
|
Use this to defer readiness until some condition is true.
|
|
477
|
-
|
|
478
|
-
|
|
392
|
+
Example:
|
|
393
|
+
```javascript
|
|
479
394
|
import Application from '@ember/application';
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
395
|
+
let App = Application.create();
|
|
396
|
+
App.deferReadiness();
|
|
397
|
+
fetch('/auth-token')
|
|
483
398
|
.then(response => response.json())
|
|
484
399
|
.then(data => {
|
|
485
400
|
App.token = data.token;
|
|
486
401
|
App.advanceReadiness();
|
|
487
402
|
});
|
|
488
403
|
```
|
|
489
|
-
|
|
404
|
+
This allows you to perform asynchronous setup logic and defer
|
|
490
405
|
booting your application until the setup has finished.
|
|
491
|
-
|
|
406
|
+
However, if the setup requires a loading UI, it might be better
|
|
492
407
|
to use the router for this purpose.
|
|
493
|
-
|
|
408
|
+
@method deferReadiness
|
|
494
409
|
@public
|
|
495
410
|
*/
|
|
411
|
+
|
|
412
|
+
|
|
496
413
|
deferReadiness() {
|
|
497
414
|
assert('You must call deferReadiness on an instance of Application', this instanceof Application);
|
|
498
415
|
assert('You cannot defer readiness since application has already destroyed', !this.isDestroyed);
|
|
499
416
|
assert('You cannot defer readiness since the application is being destroyed', !this.isDestroying);
|
|
500
417
|
assert('You cannot defer readiness since the `ready()` hook has already been called', this._readinessDeferrals > 0);
|
|
501
418
|
this._readinessDeferrals++;
|
|
502
|
-
}
|
|
503
|
-
|
|
419
|
+
}
|
|
504
420
|
/**
|
|
505
421
|
Call `advanceReadiness` after any asynchronous setup logic has completed.
|
|
506
422
|
Each call to `deferReadiness` must be matched by a call to `advanceReadiness`
|
|
507
423
|
or the application will never become ready and routing will not begin.
|
|
508
|
-
|
|
424
|
+
@method advanceReadiness
|
|
509
425
|
@see {Application#deferReadiness}
|
|
510
426
|
@public
|
|
511
427
|
*/
|
|
428
|
+
|
|
429
|
+
|
|
512
430
|
advanceReadiness() {
|
|
513
431
|
assert('You must call advanceReadiness on an instance of Application', this instanceof Application);
|
|
514
432
|
assert('You cannot advance readiness since application has already destroyed', !this.isDestroyed);
|
|
@@ -519,21 +437,22 @@ const Application = Engine.extend({
|
|
|
519
437
|
if (this._readinessDeferrals === 0) {
|
|
520
438
|
once(this, this.didBecomeReady);
|
|
521
439
|
}
|
|
522
|
-
}
|
|
523
|
-
|
|
440
|
+
}
|
|
524
441
|
/**
|
|
525
442
|
Initialize the application and return a promise that resolves with the `Application`
|
|
526
443
|
object when the boot process is complete.
|
|
527
|
-
|
|
444
|
+
Run any application initializers and run the application load hook. These hooks may
|
|
528
445
|
choose to defer readiness. For example, an authentication hook might want to defer
|
|
529
446
|
readiness until the auth token has been retrieved.
|
|
530
|
-
|
|
447
|
+
By default, this method is called automatically on "DOM ready"; however, if autoboot
|
|
531
448
|
is disabled, this is automatically called when the first application instance is
|
|
532
449
|
created via `visit`.
|
|
533
|
-
|
|
450
|
+
@public
|
|
534
451
|
@method boot
|
|
535
452
|
@return {Promise<Application,Error>}
|
|
536
453
|
*/
|
|
454
|
+
|
|
455
|
+
|
|
537
456
|
boot() {
|
|
538
457
|
assert('You cannot boot this application since it has already been destroyed', !this.isDestroyed);
|
|
539
458
|
assert('You cannot boot this application since it is being destroyed', !this.isDestroying);
|
|
@@ -548,20 +467,22 @@ const Application = Engine.extend({
|
|
|
548
467
|
// in the promise rejection
|
|
549
468
|
}
|
|
550
469
|
|
|
470
|
+
assert('has boot promise', this._bootPromise);
|
|
551
471
|
return this._bootPromise;
|
|
552
|
-
}
|
|
553
|
-
|
|
472
|
+
}
|
|
554
473
|
/**
|
|
555
474
|
Unfortunately, a lot of existing code assumes the booting process is
|
|
556
475
|
"synchronous". Specifically, a lot of tests assumes the last call to
|
|
557
476
|
`app.advanceReadiness()` or `app.reset()` will result in the app being
|
|
558
477
|
fully-booted when the current runloop completes.
|
|
559
|
-
|
|
478
|
+
We would like new code (like the `visit` API) to stop making this assumption,
|
|
560
479
|
so we created the asynchronous version above that returns a promise. But until
|
|
561
480
|
we have migrated all the code, we would have to expose this method for use
|
|
562
481
|
*internally* in places where we need to boot an app "synchronously".
|
|
563
|
-
|
|
482
|
+
@private
|
|
564
483
|
*/
|
|
484
|
+
|
|
485
|
+
|
|
565
486
|
_bootSync() {
|
|
566
487
|
if (this._booted || this.isDestroying || this.isDestroyed) {
|
|
567
488
|
return;
|
|
@@ -584,45 +505,44 @@ const Application = Engine.extend({
|
|
|
584
505
|
|
|
585
506
|
throw error;
|
|
586
507
|
}
|
|
587
|
-
}
|
|
588
|
-
|
|
508
|
+
}
|
|
589
509
|
/**
|
|
590
510
|
Reset the application. This is typically used only in tests. It cleans up
|
|
591
511
|
the application in the following order:
|
|
592
|
-
|
|
512
|
+
1. Deactivate existing routes
|
|
593
513
|
2. Destroy all objects in the container
|
|
594
514
|
3. Create a new application container
|
|
595
515
|
4. Re-route to the existing url
|
|
596
|
-
|
|
597
|
-
|
|
516
|
+
Typical Example:
|
|
517
|
+
```javascript
|
|
598
518
|
import Application from '@ember/application';
|
|
599
519
|
let App;
|
|
600
|
-
|
|
520
|
+
run(function() {
|
|
601
521
|
App = Application.create();
|
|
602
522
|
});
|
|
603
|
-
|
|
523
|
+
module('acceptance test', {
|
|
604
524
|
setup: function() {
|
|
605
525
|
App.reset();
|
|
606
526
|
}
|
|
607
527
|
});
|
|
608
|
-
|
|
528
|
+
test('first test', function() {
|
|
609
529
|
// App is freshly reset
|
|
610
530
|
});
|
|
611
|
-
|
|
531
|
+
test('second test', function() {
|
|
612
532
|
// App is again freshly reset
|
|
613
533
|
});
|
|
614
534
|
```
|
|
615
|
-
|
|
616
|
-
|
|
535
|
+
Advanced Example:
|
|
536
|
+
Occasionally you may want to prevent the app from initializing during
|
|
617
537
|
setup. This could enable extra configuration, or enable asserting prior
|
|
618
538
|
to the app becoming ready.
|
|
619
|
-
|
|
539
|
+
```javascript
|
|
620
540
|
import Application from '@ember/application';
|
|
621
541
|
let App;
|
|
622
|
-
|
|
542
|
+
run(function() {
|
|
623
543
|
App = Application.create();
|
|
624
544
|
});
|
|
625
|
-
|
|
545
|
+
module('acceptance test', {
|
|
626
546
|
setup: function() {
|
|
627
547
|
run(function() {
|
|
628
548
|
App.reset();
|
|
@@ -630,17 +550,19 @@ const Application = Engine.extend({
|
|
|
630
550
|
});
|
|
631
551
|
}
|
|
632
552
|
});
|
|
633
|
-
|
|
553
|
+
test('first test', function() {
|
|
634
554
|
ok(true, 'something before app is initialized');
|
|
635
|
-
|
|
555
|
+
run(function() {
|
|
636
556
|
App.advanceReadiness();
|
|
637
557
|
});
|
|
638
|
-
|
|
558
|
+
ok(true, 'something after app is initialized');
|
|
639
559
|
});
|
|
640
560
|
```
|
|
641
|
-
|
|
561
|
+
@method reset
|
|
642
562
|
@public
|
|
643
563
|
*/
|
|
564
|
+
|
|
565
|
+
|
|
644
566
|
reset() {
|
|
645
567
|
assert('You cannot reset this application since it has already been destroyed', !this.isDestroyed);
|
|
646
568
|
assert('You cannot reset this application since it is being destroyed', !this.isDestroying);
|
|
@@ -655,6 +577,7 @@ const Application = Engine.extend({
|
|
|
655
577
|
this._booted = false;
|
|
656
578
|
|
|
657
579
|
function handleReset() {
|
|
580
|
+
assert('expected instance', instance);
|
|
658
581
|
run(instance, 'destroy');
|
|
659
582
|
|
|
660
583
|
this._buildDeprecatedInstance();
|
|
@@ -663,17 +586,20 @@ const Application = Engine.extend({
|
|
|
663
586
|
}
|
|
664
587
|
|
|
665
588
|
join(this, handleReset);
|
|
666
|
-
}
|
|
667
|
-
|
|
589
|
+
}
|
|
668
590
|
/**
|
|
669
591
|
@private
|
|
670
592
|
@method didBecomeReady
|
|
671
593
|
*/
|
|
594
|
+
|
|
595
|
+
|
|
672
596
|
didBecomeReady() {
|
|
673
597
|
if (this.isDestroying || this.isDestroyed) {
|
|
674
598
|
return;
|
|
675
599
|
}
|
|
676
600
|
|
|
601
|
+
assert('expected _bootResolver', this._bootResolver);
|
|
602
|
+
|
|
677
603
|
try {
|
|
678
604
|
// TODO: Is this still needed for _globalsMode = false?
|
|
679
605
|
// See documentation on `_autoboot()` for details
|
|
@@ -684,6 +610,7 @@ const Application = Engine.extend({
|
|
|
684
610
|
// If we already have the __deprecatedInstance__ lying around, boot it to
|
|
685
611
|
// avoid unnecessary work
|
|
686
612
|
instance = this.__deprecatedInstance__;
|
|
613
|
+
assert('expected instance', instance);
|
|
687
614
|
} else {
|
|
688
615
|
// Otherwise, build an instance and boot it. This is currently unreachable,
|
|
689
616
|
// because we forced _globalsMode to === autoboot; but having this branch
|
|
@@ -711,24 +638,25 @@ const Application = Engine.extend({
|
|
|
711
638
|
|
|
712
639
|
throw error;
|
|
713
640
|
}
|
|
714
|
-
}
|
|
715
|
-
|
|
641
|
+
}
|
|
716
642
|
/**
|
|
717
643
|
Called when the Application has become ready, immediately before routing
|
|
718
644
|
begins. The call will be delayed until the DOM has become ready.
|
|
719
|
-
|
|
645
|
+
@event ready
|
|
720
646
|
@public
|
|
721
647
|
*/
|
|
648
|
+
|
|
649
|
+
|
|
722
650
|
ready() {
|
|
723
651
|
return this;
|
|
724
|
-
}
|
|
652
|
+
} // This method must be moved to the application instance object
|
|
653
|
+
|
|
725
654
|
|
|
726
|
-
// This method must be moved to the application instance object
|
|
727
655
|
willDestroy() {
|
|
728
|
-
|
|
656
|
+
super.willDestroy();
|
|
729
657
|
|
|
730
|
-
if (_loaded
|
|
731
|
-
_loaded
|
|
658
|
+
if (_loaded['application'] === this) {
|
|
659
|
+
_loaded['application'] = undefined;
|
|
732
660
|
}
|
|
733
661
|
|
|
734
662
|
if (this._applicationInstances.size) {
|
|
@@ -736,86 +664,85 @@ const Application = Engine.extend({
|
|
|
736
664
|
|
|
737
665
|
this._applicationInstances.clear();
|
|
738
666
|
}
|
|
739
|
-
}
|
|
740
|
-
|
|
667
|
+
}
|
|
741
668
|
/**
|
|
742
669
|
Boot a new instance of `ApplicationInstance` for the current
|
|
743
670
|
application and navigate it to the given `url`. Returns a `Promise` that
|
|
744
671
|
resolves with the instance when the initial routing and rendering is
|
|
745
672
|
complete, or rejects with any error that occurred during the boot process.
|
|
746
|
-
|
|
673
|
+
When `autoboot` is disabled, calling `visit` would first cause the
|
|
747
674
|
application to boot, which runs the application initializers.
|
|
748
|
-
|
|
675
|
+
This method also takes a hash of boot-time configuration options for
|
|
749
676
|
customizing the instance's behavior. See the documentation on
|
|
750
677
|
`ApplicationInstance.BootOptions` for details.
|
|
751
|
-
|
|
678
|
+
`ApplicationInstance.BootOptions` is an interface class that exists
|
|
752
679
|
purely to document the available options; you do not need to construct it
|
|
753
680
|
manually. Simply pass a regular JavaScript object containing of the
|
|
754
681
|
desired options:
|
|
755
|
-
|
|
682
|
+
```javascript
|
|
756
683
|
MyApp.visit("/", { location: "none", rootElement: "#container" });
|
|
757
684
|
```
|
|
758
|
-
|
|
759
|
-
|
|
685
|
+
### Supported Scenarios
|
|
686
|
+
While the `BootOptions` class exposes a large number of knobs, not all
|
|
760
687
|
combinations of them are valid; certain incompatible combinations might
|
|
761
688
|
result in unexpected behavior.
|
|
762
|
-
|
|
689
|
+
For example, booting the instance in the full browser environment
|
|
763
690
|
while specifying a foreign `document` object (e.g. `{ isBrowser: true,
|
|
764
691
|
document: iframe.contentDocument }`) does not work correctly today,
|
|
765
692
|
largely due to Ember's jQuery dependency.
|
|
766
|
-
|
|
693
|
+
Currently, there are three officially supported scenarios/configurations.
|
|
767
694
|
Usages outside of these scenarios are not guaranteed to work, but please
|
|
768
695
|
feel free to file bug reports documenting your experience and any issues
|
|
769
696
|
you encountered to help expand support.
|
|
770
|
-
|
|
771
|
-
|
|
697
|
+
#### Browser Applications (Manual Boot)
|
|
698
|
+
The setup is largely similar to how Ember works out-of-the-box. Normally,
|
|
772
699
|
Ember will boot a default instance for your Application on "DOM ready".
|
|
773
700
|
However, you can customize this behavior by disabling `autoboot`.
|
|
774
|
-
|
|
701
|
+
For example, this allows you to render a miniture demo of your application
|
|
775
702
|
into a specific area on your marketing website:
|
|
776
|
-
|
|
703
|
+
```javascript
|
|
777
704
|
import MyApp from 'my-app';
|
|
778
|
-
|
|
705
|
+
$(function() {
|
|
779
706
|
let App = MyApp.create({ autoboot: false });
|
|
780
|
-
|
|
707
|
+
let options = {
|
|
781
708
|
// Override the router's location adapter to prevent it from updating
|
|
782
709
|
// the URL in the address bar
|
|
783
710
|
location: 'none',
|
|
784
|
-
|
|
711
|
+
// Override the default `rootElement` on the app to render into a
|
|
785
712
|
// specific `div` on the page
|
|
786
713
|
rootElement: '#demo'
|
|
787
714
|
};
|
|
788
|
-
|
|
715
|
+
// Start the app at the special demo URL
|
|
789
716
|
App.visit('/demo', options);
|
|
790
717
|
});
|
|
791
718
|
```
|
|
792
|
-
|
|
719
|
+
Or perhaps you might want to boot two instances of your app on the same
|
|
793
720
|
page for a split-screen multiplayer experience:
|
|
794
|
-
|
|
721
|
+
```javascript
|
|
795
722
|
import MyApp from 'my-app';
|
|
796
|
-
|
|
723
|
+
$(function() {
|
|
797
724
|
let App = MyApp.create({ autoboot: false });
|
|
798
|
-
|
|
799
|
-
|
|
725
|
+
let sessionId = MyApp.generateSessionID();
|
|
726
|
+
let player1 = App.visit(`/matches/join?name=Player+1&session=${sessionId}`, { rootElement: '#left', location: 'none' });
|
|
800
727
|
let player2 = App.visit(`/matches/join?name=Player+2&session=${sessionId}`, { rootElement: '#right', location: 'none' });
|
|
801
|
-
|
|
728
|
+
Promise.all([player1, player2]).then(() => {
|
|
802
729
|
// Both apps have completed the initial render
|
|
803
730
|
$('#loading').fadeOut();
|
|
804
731
|
});
|
|
805
732
|
});
|
|
806
733
|
```
|
|
807
|
-
|
|
734
|
+
Do note that each app instance maintains their own registry/container, so
|
|
808
735
|
they will run in complete isolation by default.
|
|
809
|
-
|
|
810
|
-
|
|
736
|
+
#### Server-Side Rendering (also known as FastBoot)
|
|
737
|
+
This setup allows you to run your Ember app in a server environment using
|
|
811
738
|
Node.js and render its content into static HTML for SEO purposes.
|
|
812
|
-
|
|
739
|
+
```javascript
|
|
813
740
|
const HTMLSerializer = new SimpleDOM.HTMLSerializer(SimpleDOM.voidMap);
|
|
814
|
-
|
|
741
|
+
function renderURL(url) {
|
|
815
742
|
let dom = new SimpleDOM.Document();
|
|
816
743
|
let rootElement = dom.body;
|
|
817
744
|
let options = { isBrowser: false, document: dom, rootElement: rootElement };
|
|
818
|
-
|
|
745
|
+
return MyApp.visit(options).then(instance => {
|
|
819
746
|
try {
|
|
820
747
|
return HTMLSerializer.serialize(rootElement.firstChild);
|
|
821
748
|
} finally {
|
|
@@ -824,50 +751,50 @@ const Application = Engine.extend({
|
|
|
824
751
|
});
|
|
825
752
|
}
|
|
826
753
|
```
|
|
827
|
-
|
|
754
|
+
In this scenario, because Ember does not have access to a global `document`
|
|
828
755
|
object in the Node.js environment, you must provide one explicitly. In practice,
|
|
829
756
|
in the non-browser environment, the stand-in `document` object only needs to
|
|
830
757
|
implement a limited subset of the full DOM API. The `SimpleDOM` library is known
|
|
831
758
|
to work.
|
|
832
|
-
|
|
759
|
+
Since there is no DOM access in the non-browser environment, you must also
|
|
833
760
|
specify a DOM `Element` object in the same `document` for the `rootElement` option
|
|
834
761
|
(as opposed to a selector string like `"body"`).
|
|
835
|
-
|
|
762
|
+
See the documentation on the `isBrowser`, `document` and `rootElement` properties
|
|
836
763
|
on `ApplicationInstance.BootOptions` for details.
|
|
837
|
-
|
|
838
|
-
|
|
764
|
+
#### Server-Side Resource Discovery
|
|
765
|
+
This setup allows you to run the routing layer of your Ember app in a server
|
|
839
766
|
environment using Node.js and completely disable rendering. This allows you
|
|
840
767
|
to simulate and discover the resources (i.e. AJAX requests) needed to fulfill
|
|
841
768
|
a given request and eagerly "push" these resources to the client.
|
|
842
|
-
|
|
769
|
+
```app/initializers/network-service.js
|
|
843
770
|
import BrowserNetworkService from 'app/services/network/browser';
|
|
844
771
|
import NodeNetworkService from 'app/services/network/node';
|
|
845
|
-
|
|
772
|
+
// Inject a (hypothetical) service for abstracting all AJAX calls and use
|
|
846
773
|
// the appropriate implementation on the client/server. This also allows the
|
|
847
774
|
// server to log all the AJAX calls made during a particular request and use
|
|
848
775
|
// that for resource-discovery purpose.
|
|
849
|
-
|
|
776
|
+
export function initialize(application) {
|
|
850
777
|
if (window) { // browser
|
|
851
778
|
application.register('service:network', BrowserNetworkService);
|
|
852
779
|
} else { // node
|
|
853
780
|
application.register('service:network', NodeNetworkService);
|
|
854
781
|
}
|
|
855
782
|
};
|
|
856
|
-
|
|
783
|
+
export default {
|
|
857
784
|
name: 'network-service',
|
|
858
785
|
initialize: initialize
|
|
859
786
|
};
|
|
860
787
|
```
|
|
861
|
-
|
|
788
|
+
```app/routes/post.js
|
|
862
789
|
import Route from '@ember/routing/route';
|
|
863
790
|
import { service } from '@ember/service';
|
|
864
|
-
|
|
865
|
-
|
|
791
|
+
// An example of how the (hypothetical) service is used in routes.
|
|
792
|
+
export default class IndexRoute extends Route {
|
|
866
793
|
@service network;
|
|
867
|
-
|
|
794
|
+
model(params) {
|
|
868
795
|
return this.network.fetch(`/api/posts/${params.post_id}.json`);
|
|
869
796
|
}
|
|
870
|
-
|
|
797
|
+
afterModel(post) {
|
|
871
798
|
if (post.isExternalContent) {
|
|
872
799
|
return this.network.fetch(`/api/external/?url=${post.externalURL}`);
|
|
873
800
|
} else {
|
|
@@ -876,21 +803,23 @@ const Application = Engine.extend({
|
|
|
876
803
|
}
|
|
877
804
|
}
|
|
878
805
|
```
|
|
879
|
-
|
|
806
|
+
```javascript
|
|
880
807
|
// Finally, put all the pieces together
|
|
881
|
-
|
|
808
|
+
function discoverResourcesFor(url) {
|
|
882
809
|
return MyApp.visit(url, { isBrowser: false, shouldRender: false }).then(instance => {
|
|
883
810
|
let networkService = instance.lookup('service:network');
|
|
884
811
|
return networkService.requests; // => { "/api/posts/123.json": "..." }
|
|
885
812
|
});
|
|
886
813
|
}
|
|
887
814
|
```
|
|
888
|
-
|
|
815
|
+
@public
|
|
889
816
|
@method visit
|
|
890
817
|
@param url {String} The initial URL to navigate to
|
|
891
818
|
@param options {ApplicationInstance.BootOptions}
|
|
892
819
|
@return {Promise<ApplicationInstance, Error>}
|
|
893
820
|
*/
|
|
821
|
+
|
|
822
|
+
|
|
894
823
|
visit(url, options) {
|
|
895
824
|
assert('You cannot visit this application since it has already been destroyed', !this.isDestroyed);
|
|
896
825
|
assert('You cannot visit this application since it is being destroyed', !this.isDestroying);
|
|
@@ -903,39 +832,7 @@ const Application = Engine.extend({
|
|
|
903
832
|
});
|
|
904
833
|
}
|
|
905
834
|
|
|
906
|
-
}
|
|
907
|
-
Application.reopenClass({
|
|
908
|
-
/**
|
|
909
|
-
This creates a registry with the default Ember naming conventions.
|
|
910
|
-
It also configures the registry:
|
|
911
|
-
* registered views are created every time they are looked up (they are
|
|
912
|
-
not singletons)
|
|
913
|
-
* registered templates are not factories; the registered value is
|
|
914
|
-
returned directly.
|
|
915
|
-
* the router receives the application as its `namespace` property
|
|
916
|
-
* all controllers receive the router as their `target` and `controllers`
|
|
917
|
-
properties
|
|
918
|
-
* all controllers receive the application as their `namespace` property
|
|
919
|
-
* the application view receives the application controller as its
|
|
920
|
-
`controller` property
|
|
921
|
-
* the application view receives the application template as its
|
|
922
|
-
`defaultTemplate` property
|
|
923
|
-
@method buildRegistry
|
|
924
|
-
@static
|
|
925
|
-
@param {Application} namespace the application for which to
|
|
926
|
-
build the registry
|
|
927
|
-
@return {Ember.Registry} the built registry
|
|
928
|
-
@private
|
|
929
|
-
*/
|
|
930
|
-
buildRegistry() {
|
|
931
|
-
let registry = this._super(...arguments);
|
|
932
|
-
|
|
933
|
-
commonSetupRegistry(registry);
|
|
934
|
-
setupApplicationRegistry(registry);
|
|
935
|
-
return registry;
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
});
|
|
835
|
+
}
|
|
939
836
|
|
|
940
837
|
function commonSetupRegistry(registry) {
|
|
941
838
|
registry.register('router:main', Router);
|