Skip to content

Commit 0fcfb8e

Browse files
committed
Merge pull request vuejs#1967 from tgeorgiev/dev
Fixing Vue.$destroy(remove) does not trigger "detached" hook on secon…
2 parents ad2ac50 + a4fcc83 commit 0fcfb8e

File tree

3 files changed

+108
-10
lines changed

3 files changed

+108
-10
lines changed

src/instance/internal/lifecycle.js

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,30 @@ export default function (Vue) {
150150
}
151151
return
152152
}
153+
154+
var destroyReady
155+
var pendingRemoval
156+
157+
var self = this
158+
// Cleanup should be called either synchronously or asynchronoysly as
159+
// callback of this.$remove(), or if remove and deferCleanup are false.
160+
// In any case it should be called after all other removing, unbinding and
161+
// turning of is done
162+
var cleanupIfPossible = function () {
163+
if (destroyReady && !pendingRemoval && !deferCleanup) {
164+
self._cleanup()
165+
}
166+
}
167+
168+
// remove DOM element
169+
if (remove && this.$el) {
170+
pendingRemoval = true
171+
this.$remove(function () {
172+
pendingRemoval = false
173+
cleanupIfPossible()
174+
})
175+
}
176+
153177
this._callHook('beforeDestroy')
154178
this._isBeingDestroyed = true
155179
var i
@@ -183,15 +207,9 @@ export default function (Vue) {
183207
if (this.$el) {
184208
this.$el.__vue__ = null
185209
}
186-
// remove DOM element
187-
var self = this
188-
if (remove && this.$el) {
189-
this.$remove(function () {
190-
self._cleanup()
191-
})
192-
} else if (!deferCleanup) {
193-
this._cleanup()
194-
}
210+
211+
destroyReady = true
212+
cleanupIfPossible()
195213
}
196214

197215
/**

test/unit/specs/api/lifecycle_spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,37 @@ describe('Lifecycle API', function () {
192192
expect(opts.detached).toHaveBeenCalled()
193193
})
194194

195+
// #1966
196+
it('grandchild hooks', function () {
197+
var grandChildBeforeDestroy = jasmine.createSpy()
198+
var grandChildDestroyed = jasmine.createSpy()
199+
var grandChildDetached = jasmine.createSpy()
200+
201+
var opts = {
202+
template: '<div><test></test></div>',
203+
components: {
204+
test: {
205+
template: '<div><test-inner></test-inner></div>',
206+
components: {
207+
'test-inner': {
208+
beforeDestroy: grandChildBeforeDestroy,
209+
destroyed: grandChildDestroyed,
210+
detached: grandChildDetached
211+
}
212+
}
213+
}
214+
}
215+
}
216+
var el = opts.el = document.createElement('div')
217+
document.body.appendChild(el)
218+
var vm = new Vue(opts)
219+
vm.$destroy(true)
220+
221+
expect(grandChildBeforeDestroy).toHaveBeenCalled()
222+
expect(grandChildDestroyed).toHaveBeenCalled()
223+
expect(grandChildDetached).toHaveBeenCalled()
224+
})
225+
195226
it('parent', function () {
196227
var parent = new Vue()
197228
var child = new Vue({ parent: parent })

test/unit/specs/misc_spec.js

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,56 @@ describe('Misc', function () {
119119
expect(logs.join()).toBe('0,5,6,5,6,1')
120120
logs = []
121121
vm.$destroy(true)
122-
expect(logs.join()).toBe('3,8,9,8,9,2,7,7,4')
122+
expect(logs.join()).toBe('2,7,7,3,8,9,8,9,4')
123+
Vue.options.replace = false
124+
})
125+
126+
// #1966
127+
it('call lifecycle hooks for child and grandchild components', function () {
128+
Vue.options.replace = true
129+
var el = document.createElement('div')
130+
var logs = []
131+
function log (n) {
132+
return function () {
133+
logs.push(n)
134+
}
135+
}
136+
document.body.appendChild(el)
137+
var vm = new Vue({
138+
el: el,
139+
attached: log(0),
140+
ready: log(1),
141+
detached: log(2),
142+
beforeDestroy: log(3),
143+
destroyed: log(4),
144+
template: '<div><test></test></div>',
145+
components: {
146+
test: {
147+
attached: log(5),
148+
ready: log(6),
149+
detached: log(7),
150+
beforeDestroy: log(8),
151+
destroyed: log(9),
152+
template: '<div><test-inner></test-inner></div>',
153+
components: {
154+
'test-inner': {
155+
attached: log(10),
156+
ready: log(11),
157+
detached: log(12),
158+
beforeDestroy: log(13),
159+
destroyed: log(14),
160+
template: '<span>hi</span>'
161+
}
162+
}
163+
164+
}
165+
}
166+
})
167+
expect(vm.$el.innerHTML).toBe('<div><span>hi</span></div>')
168+
expect(logs.join()).toBe('0,5,10,11,6,1')
169+
logs = []
170+
vm.$destroy(true)
171+
expect(logs.join()).toBe('2,7,12,3,8,13,14,9,4')
123172
Vue.options.replace = false
124173
})
125174

0 commit comments

Comments
 (0)