diff --git a/packages/ember-htmlbars/tests/integration/component_lifecycle_test.js b/packages/ember-htmlbars/tests/integration/component_lifecycle_test.js
index 15b44bf7655..322a32e7064 100644
--- a/packages/ember-htmlbars/tests/integration/component_lifecycle_test.js
+++ b/packages/ember-htmlbars/tests/integration/component_lifecycle_test.js
@@ -25,7 +25,7 @@ if (isEnabled('ember-htmlbars-component-generation')) {
}
styles.forEach(style => {
- function invoke(name, hash) {
+ function invoke(name, hash = {}) {
if (style.name === 'curly') {
let attrs = Object.keys(hash).map(k => `${k}=${val(hash[k])}`).join(' ');
return `{{${name} ${attrs}}}`;
@@ -87,6 +87,7 @@ styles.forEach(style => {
this.label = label;
components[label] = this;
this._super.apply(this, arguments);
+ pushHook(label, 'init');
},
didInitAttrs(options) {
@@ -147,9 +148,9 @@ styles.forEach(style => {
let bottomAttrs = { website: 'tomdale.net' };
deepEqual(hooks, [
- hook('top', 'didInitAttrs', { attrs: topAttrs }), hook('top', 'didReceiveAttrs', { newAttrs: topAttrs }), hook('top', 'willRender'),
- hook('middle', 'didInitAttrs', { attrs: middleAttrs }), hook('middle', 'didReceiveAttrs', { newAttrs: middleAttrs }), hook('middle', 'willRender'),
- hook('bottom', 'didInitAttrs', { attrs: bottomAttrs }), hook('bottom', 'didReceiveAttrs', { newAttrs: bottomAttrs }), hook('bottom', 'willRender'),
+ hook('top', 'init'), hook('top', 'didInitAttrs', { attrs: topAttrs }), hook('top', 'didReceiveAttrs', { newAttrs: topAttrs }), hook('top', 'willRender'),
+ hook('middle', 'init'), hook('middle', 'didInitAttrs', { attrs: middleAttrs }), hook('middle', 'didReceiveAttrs', { newAttrs: middleAttrs }), hook('middle', 'willRender'),
+ hook('bottom', 'init'), hook('bottom', 'didInitAttrs', { attrs: bottomAttrs }), hook('bottom', 'didReceiveAttrs', { newAttrs: bottomAttrs }), hook('bottom', 'willRender'),
hook('bottom', 'didInsertElement'), hook('bottom', 'didRender'),
hook('middle', 'didInsertElement'), hook('middle', 'didRender'),
hook('top', 'didInsertElement'), hook('top', 'didRender')
@@ -238,6 +239,7 @@ styles.forEach(style => {
this.label = label;
components[label] = this;
this._super.apply(this, arguments);
+ pushHook(label, 'init');
},
didInitAttrs(options) {
@@ -298,9 +300,9 @@ styles.forEach(style => {
let bottomAttrs = { twitterMiddle: '@tomdale' };
deepEqual(hooks, [
- hook('top', 'didInitAttrs', { attrs: topAttrs }), hook('top', 'didReceiveAttrs', { newAttrs: topAttrs }), hook('top', 'willRender'),
- hook('middle', 'didInitAttrs', { attrs: middleAttrs }), hook('middle', 'didReceiveAttrs', { newAttrs: middleAttrs }), hook('middle', 'willRender'),
- hook('bottom', 'didInitAttrs', { attrs: bottomAttrs }), hook('bottom', 'didReceiveAttrs', { newAttrs: bottomAttrs }), hook('bottom', 'willRender'),
+ hook('top', 'init'), hook('top', 'didInitAttrs', { attrs: topAttrs }), hook('top', 'didReceiveAttrs', { newAttrs: topAttrs }), hook('top', 'willRender'),
+ hook('middle', 'init'), hook('middle', 'didInitAttrs', { attrs: middleAttrs }), hook('middle', 'didReceiveAttrs', { newAttrs: middleAttrs }), hook('middle', 'willRender'),
+ hook('bottom', 'init'), hook('bottom', 'didInitAttrs', { attrs: bottomAttrs }), hook('bottom', 'didReceiveAttrs', { newAttrs: bottomAttrs }), hook('bottom', 'willRender'),
hook('bottom', 'didInsertElement'), hook('bottom', 'didRender'),
hook('middle', 'didInsertElement'), hook('middle', 'didRender'),
hook('top', 'didInsertElement'), hook('top', 'didRender')
@@ -384,6 +386,30 @@ styles.forEach(style => {
component.destroy();
});
});
+
+ QUnit.test('properties set during `init` are availabe in `didReceiveAttrs`', function(assert) {
+ assert.expect(1);
+
+ registry.register('component:the-thing', style.class.extend({
+ init() {
+ this._super(...arguments);
+ this.propertySetInInit = 'init fired!';
+ },
+
+ didReceiveAttrs() {
+ this._super(...arguments);
+
+ assert.equal(this.propertySetInInit, 'init fired!', 'init has already finished before didReceiveAttrs');
+ }
+ }));
+
+ view = EmberView.extend({
+ template: compile(invoke('the-thing')),
+ container: container
+ }).create();
+
+ runAppend(view);
+ });
});
// TODO: Write a test that involves deep mutability: the component plucks something
diff --git a/packages/ember-runtime/lib/system/core_object.js b/packages/ember-runtime/lib/system/core_object.js
index 37ef0084272..e6016a4c809 100644
--- a/packages/ember-runtime/lib/system/core_object.js
+++ b/packages/ember-runtime/lib/system/core_object.js
@@ -46,7 +46,9 @@ import {
K
} from 'ember-metal/core';
import { validatePropertyInjections } from 'ember-runtime/inject';
+import { symbol } from 'ember-metal/utils';
+export let POST_INIT = symbol('POST_INIT');
var schedule = run.schedule;
var applyMixin = Mixin._apply;
var finishPartial = Mixin.finishPartial;
@@ -191,6 +193,8 @@ function makeCtor() {
this.init.apply(this, args);
}
+ this[POST_INIT]();
+
m.proto = proto;
finishChains(this);
sendEvent(this, 'init');
@@ -265,6 +269,9 @@ CoreObject.PrototypeMixin = Mixin.create({
@public
*/
init() {},
+
+ [POST_INIT]: function() { },
+
__defineNonEnumerable(property) {
Object.defineProperty(this, property.name, property.descriptor);
//this[property.name] = property.descriptor.value;
diff --git a/packages/ember-views/lib/mixins/view_support.js b/packages/ember-views/lib/mixins/view_support.js
index ae8f869bc2d..97cc171bf50 100644
--- a/packages/ember-views/lib/mixins/view_support.js
+++ b/packages/ember-views/lib/mixins/view_support.js
@@ -6,6 +6,7 @@ import { addObserver, removeObserver } from 'ember-metal/observer';
import { guidFor } from 'ember-metal/utils';
import { computed } from 'ember-metal/computed';
import { Mixin } from 'ember-metal/mixin';
+import { POST_INIT } from 'ember-runtime/system/core_object';
import jQuery from 'ember-views/system/jquery';
@@ -609,7 +610,6 @@ export default Mixin.create({
this.scheduledRevalidation = false;
this._super(...arguments);
- this.renderer.componentInitAttrs(this, this.attrs || {});
assert(
'Using a custom `.render` function is no longer supported.',
@@ -617,6 +617,20 @@ export default Mixin.create({
);
},
+ /*
+ This is a special hook implemented in CoreObject, that allows Views/Components
+ to have a way to ensure that `init` fires before `didInitAttrs` / `didReceiveAttrs`
+ (so that `this._super` in init does not trigger `didReceiveAttrs` before the classes
+ own `init` is finished).
+
+ @method __postInitInitialization
+ @private
+ */
+ [POST_INIT]: function() {
+ this._super(...arguments);
+ this.renderer.componentInitAttrs(this, this.attrs || {});
+ },
+
__defineNonEnumerable(property) {
this[property.name] = property.descriptor.value;
},