diff --git a/packages/ember-glimmer/lib/renderer.js b/packages/ember-glimmer/lib/renderer.js index 468733278f6..94266f16a40 100644 --- a/packages/ember-glimmer/lib/renderer.js +++ b/packages/ember-glimmer/lib/renderer.js @@ -192,7 +192,9 @@ class Renderer { _renderResult.destroy(); } - view.destroy(); + if (!view.isDestroying) { + view.destroy(); + } } componentInitAttrs() { diff --git a/packages/ember-htmlbars/lib/component.js b/packages/ember-htmlbars/lib/component.js index ddebdb46ec7..63dfb2284a0 100644 --- a/packages/ember-htmlbars/lib/component.js +++ b/packages/ember-htmlbars/lib/component.js @@ -193,7 +193,7 @@ const Component = View.extend(TargetActionSupport, ActionSupport, { let attr = this._renderNode.childNodes.filter(node => node.attrName === name)[0]; if (!attr) { return null; } return attr.getContent(); - } + }, /** Returns true when the component was invoked with a block template. @@ -359,6 +359,7 @@ const Component = View.extend(TargetActionSupport, ActionSupport, { @public @since 1.13.0 */ + didReceiveAttrs() {}, /** Called when the attributes passed into the component have been updated. @@ -379,6 +380,7 @@ const Component = View.extend(TargetActionSupport, ActionSupport, { @public @since 1.13.0 */ + didRender() {}, /** Called after a component has been rendered, both on initial render and @@ -397,6 +399,7 @@ const Component = View.extend(TargetActionSupport, ActionSupport, { @public @since 1.13.0 */ + willRender() {}, /** Called before a component has been rendered, both on initial render and @@ -415,6 +418,7 @@ const Component = View.extend(TargetActionSupport, ActionSupport, { @public @since 1.13.0 */ + didUpdateAttrs() {}, /** Called when the attributes passed into the component have been changed. @@ -433,6 +437,7 @@ const Component = View.extend(TargetActionSupport, ActionSupport, { @public @since 1.13.0 */ + willUpdate() {}, /** Called when the component is about to update and rerender itself. @@ -447,10 +452,11 @@ const Component = View.extend(TargetActionSupport, ActionSupport, { Called when the component has updated and rerendered itself. Called only during a rerender, not during an initial render. - @event didUpdate + @method didUpdate @public @since 1.13.0 */ + didUpdate() {} /** Called when the component has updated and rerendered itself. @@ -465,7 +471,8 @@ const Component = View.extend(TargetActionSupport, ActionSupport, { Component[NAME_KEY] = 'Ember.Component'; Component.reopenClass({ - isComponentFactory: true + isComponentFactory: true, + positionalParams: [] }); export default Component; diff --git a/packages/ember-htmlbars/lib/hooks/destroy-render-node.js b/packages/ember-htmlbars/lib/hooks/destroy-render-node.js index 4090de331c2..4be52cd4b06 100644 --- a/packages/ember-htmlbars/lib/hooks/destroy-render-node.js +++ b/packages/ember-htmlbars/lib/hooks/destroy-render-node.js @@ -2,10 +2,10 @@ @module ember @submodule ember-htmlbars */ - export default function destroyRenderNode(renderNode) { - if (renderNode.emberView) { - renderNode.emberView.destroy(); + let view = renderNode.emberView; + if (view) { + view.renderer.remove(view, true); } let streamUnsubscribers = renderNode.streamUnsubscribers; diff --git a/packages/ember-htmlbars/lib/morphs/morph.js b/packages/ember-htmlbars/lib/morphs/morph.js index 7ef666edb62..82c2c2b7792 100644 --- a/packages/ember-htmlbars/lib/morphs/morph.js +++ b/packages/ember-htmlbars/lib/morphs/morph.js @@ -29,6 +29,7 @@ proto.HTMLBarsMorph$constructor = HTMLBarsMorph; proto.HTMLBarsMorph$clear = HTMLBarsMorph.prototype.clear; proto.addDestruction = function(toDestroy) { + // called from Router.prototype._connectActiveComponentNode for {{render}} this.emberToDestroy = this.emberToDestroy || []; this.emberToDestroy.push(toDestroy); }; @@ -38,7 +39,6 @@ proto.cleanup = function() { if (view) { let parentView = view.parentView; - if (parentView && view.ownerView._destroyingSubtreeForView === parentView) { parentView.removeChild(view); } diff --git a/packages/ember-htmlbars/lib/renderer.js b/packages/ember-htmlbars/lib/renderer.js index 98647acba06..a4048151d55 100755 --- a/packages/ember-htmlbars/lib/renderer.js +++ b/packages/ember-htmlbars/lib/renderer.js @@ -166,10 +166,6 @@ Renderer.prototype.willInsertElement = function (view) { if (view.trigger) { view.trigger('willInsertElement'); } }; // Will place into DOM. -Renderer.prototype.setAttrs = function (view, attrs) { - set(view, 'attrs', attrs); -}; // Set attrs the first time. - Renderer.prototype.componentInitAttrs = function (component, attrs) { component.trigger('didInitAttrs', { attrs }); component.trigger('didReceiveAttrs', { newAttrs: attrs }); @@ -191,10 +187,6 @@ Renderer.prototype.didRender = function (view) { if (view.trigger) { view.trigger('didRender'); } }; -Renderer.prototype.updateAttrs = function (view, attrs) { - this.setAttrs(view, attrs); -}; // Setting new attrs. - Renderer.prototype.componentUpdateAttrs = function (component, newAttrs) { let oldAttrs = null; @@ -244,51 +236,46 @@ Renderer.prototype.rerender = function (view) { }; Renderer.prototype.remove = function (view, shouldDestroy) { - this.willDestroyElement(view); - - view._willRemoveElement = true; - run.schedule('render', this, this.renderElementRemoval, view); -}; - -Renderer.prototype.renderElementRemoval = - function Renderer_renderElementRemoval(view) { - // Use the _willRemoveElement flag to avoid mulitple removal attempts in - // case many have been scheduled. This should be more performant than using - // `scheduleOnce`. - if (view._willRemoveElement) { - view._willRemoveElement = false; + let renderNode = view._renderNode; + view._renderNode = null; + if (renderNode) { + renderNode.emberView = null; + this.willDestroyElement(view); + view._transitionTo('destroying'); - if (view._renderNode && view.element && view.element.parentNode) { - view._renderNode.clear(); - } - this.didDestroyElement(view); + view._renderNode = null; + let lastResult = renderNode.lastResult; + if (lastResult) { + internal.clearMorph(renderNode, lastResult.env, shouldDestroy !== false); } - }; + if (!shouldDestroy) { + view._transitionTo('preRender'); + } + this.didDestroyElement(view); + } + + // toplevel view removed, remove insertion point + let lastResult = view.lastResult; + if (lastResult) { + view.lastResult = null; + lastResult.destroy(); + } -Renderer.prototype.willRemoveElement = function (/*view*/) {}; + if (shouldDestroy && !view.isDestroying) { + view.destroy(); + } +}; Renderer.prototype.willDestroyElement = function (view) { if (view.trigger) { view.trigger('willDestroyElement'); view.trigger('willClearRender'); } - - if (view._transitionTo) { - view._transitionTo('destroying'); - } }; Renderer.prototype.didDestroyElement = function (view) { view.element = null; - // Views that are being destroyed should never go back to the preRender state. - // However if we're just destroying an element on a view (as is the case when - // using View#remove) then the view should go to a preRender state so that - // it can be rendered again later. - if (view._state !== 'destroying' && view._transitionTo) { - view._transitionTo('preRender'); - } - if (view.trigger) { view.trigger('didDestroyElement'); } diff --git a/packages/ember-views/lib/mixins/view_support.js b/packages/ember-views/lib/mixins/view_support.js index 75e55488742..38606543a2a 100644 --- a/packages/ember-views/lib/mixins/view_support.js +++ b/packages/ember-views/lib/mixins/view_support.js @@ -368,7 +368,22 @@ export default Mixin.create({ @private */ destroyElement() { - return this._currentState.destroyElement(this); + this._currentState.destroyElement(this); + return this; + }, + + /** + You must call `destroy` on a view to destroy the view (and all of its + child views). This will remove the view from any parent node, then make + sure that the DOM element managed by the view can be released by the + memory manager. + + @method destroy + @private + */ + destroy() { + this._super(...arguments); + this._currentState.destroy(this); }, /** @@ -524,26 +539,6 @@ export default Mixin.create({ } }, - /** - You must call `destroy` on a view to destroy the view (and all of its - child views). This will remove the view from any parent node, then make - sure that the DOM element managed by the view can be released by the - memory manager. - - @method destroy - @private - */ - destroy() { - if (!this._super(...arguments)) { return; } - - // Destroy HTMLbars template - if (this.lastResult) { - this.lastResult.destroy(); - } - - return this; - }, - // ....................................................... // EVENT HANDLING // diff --git a/packages/ember-views/lib/views/core_view.js b/packages/ember-views/lib/views/core_view.js index b19bc5df9fe..661c9bf1ccd 100644 --- a/packages/ember-views/lib/views/core_view.js +++ b/packages/ember-views/lib/views/core_view.js @@ -41,6 +41,15 @@ const CoreView = EmberObject.extend(Evented, ActionHandler, { this._super(...arguments); this._state = 'preRender'; this._currentState = this._states.preRender; + this._willInsert = false; + this._renderNode = null; + this.lastResult = null; + this._dispatching = null; + this._destroyingSubtreeForView = null; + this._isDispatchingAttrs = false; + this._isVisible = false; + this.element = null; + this.env = null; this._isVisible = get(this, 'isVisible'); // Fallback for legacy cases where the view was created directly @@ -50,9 +59,6 @@ const CoreView = EmberObject.extend(Evented, ActionHandler, { renderer = renderer || InteractiveRenderer.create({ dom: new DOMHelper() }); this.renderer = renderer; } - - this._destroyingSubtreeForView = null; - this._dispatching = null; }, /** @@ -97,14 +103,6 @@ const CoreView = EmberObject.extend(Evented, ActionHandler, { has(name) { return typeOf(this[name]) === 'function' || this._super(name); - }, - - destroy() { - if (!this._super(...arguments)) { return; } - - this._currentState.cleanup(this); - - return this; } }); diff --git a/packages/ember-views/lib/views/states/default.js b/packages/ember-views/lib/views/states/default.js index f1e66e734c8..066581d150f 100644 --- a/packages/ember-views/lib/views/states/default.js +++ b/packages/ember-views/lib/views/states/default.js @@ -38,9 +38,10 @@ export default { return true; // continue event propagation }, - cleanup() { } , destroyElement() { }, + destroy() { }, + rerender(view) { view.renderer.ensureViewNotRendering(view); } diff --git a/packages/ember-views/lib/views/states/destroying.js b/packages/ember-views/lib/views/states/destroying.js index 2ea1e59707f..25c6ea59c67 100644 --- a/packages/ember-views/lib/views/states/destroying.js +++ b/packages/ember-views/lib/views/states/destroying.js @@ -21,4 +21,3 @@ assign(destroying, { }); export default destroying; - diff --git a/packages/ember-views/lib/views/states/has_element.js b/packages/ember-views/lib/views/states/has_element.js index 53ac1fa2ffc..c84691b6b79 100644 --- a/packages/ember-views/lib/views/states/has_element.js +++ b/packages/ember-views/lib/views/states/has_element.js @@ -33,17 +33,12 @@ assign(hasElement, { view.renderer.rerender(view); }, - cleanup(view) { - view._currentState.destroyElement(view); - }, - - // once the view is already in the DOM, destroying it removes it - // from the DOM, nukes its element, and puts it back into the - // preRender state if inDOM. - destroyElement(view) { view.renderer.remove(view, false); - return view; + }, + + destroy(view) { + view.renderer.remove(view, true); }, // Handle events from `Ember.EventDispatcher` diff --git a/packages/ember-views/lib/views/states/in_dom.js b/packages/ember-views/lib/views/states/in_dom.js index b0edeec8334..5c82c42ca41 100644 --- a/packages/ember-views/lib/views/states/in_dom.js +++ b/packages/ember-views/lib/views/states/in_dom.js @@ -27,7 +27,9 @@ assign(inDOM, { }, exit(view) { - view._unregister(); + if (view.tagName !== '') { + view._unregister(); + } } }); diff --git a/packages/ember-views/tests/system/event_dispatcher_test.js b/packages/ember-views/tests/system/event_dispatcher_test.js index 05ebc73bf88..03449a7513e 100644 --- a/packages/ember-views/tests/system/event_dispatcher_test.js +++ b/packages/ember-views/tests/system/event_dispatcher_test.js @@ -108,9 +108,7 @@ QUnit.test('should not dispatch events to views not inDOM', function() { let $element = view.$(); run(() => { - // TODO change this test not to use private API - // Force into preRender - view.renderer.remove(view, false, true); + view.destroyElement(); }); $element.trigger('mousedown');