Skip to content

Commit 4fdbb70

Browse files
committed
support returning object with promises in data hook (close #92)
1 parent e03c866 commit 4fdbb70

File tree

3 files changed

+110
-25
lines changed

3 files changed

+110
-25
lines changed

src/pipeline.js

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getRouteConfig, resolveAsyncComponent } from './util'
1+
import { getRouteConfig, resolveAsyncComponent, isPromise } from './util'
22

33
/**
44
* Determine the reusability of an existing router view.
@@ -42,7 +42,9 @@ export function canDeactivate (view, transition, next) {
4242
if (!hook) {
4343
next()
4444
} else {
45-
transition.callHook(hook, fromComponent, next, true)
45+
transition.callHook(hook, fromComponent, next, {
46+
expectBoolean: true
47+
})
4648
}
4749
}
4850

@@ -65,7 +67,9 @@ export function canActivate (handler, transition, next) {
6567
if (!hook) {
6668
next()
6769
} else {
68-
transition.callHook(hook, null, next, true)
70+
transition.callHook(hook, null, next, {
71+
expectBoolean: true
72+
})
6973
}
7074
})
7175
}
@@ -173,7 +177,9 @@ export function activate (view, transition, depth, cb) {
173177
}
174178

175179
if (activateHook) {
176-
transition.callHook(activateHook, component, afterActivate, false, cleanup)
180+
transition.callHook(activateHook, component, afterActivate, {
181+
cleanup: cleanup
182+
})
177183
} else {
178184
afterActivate()
179185
}
@@ -206,11 +212,28 @@ export function reuse (view, transition) {
206212

207213
function loadData (component, transition, hook, cb, cleanup) {
208214
component.$loadingRouteData = true
209-
transition.callHook(hook, component, (data) => {
210-
for (let key in data) {
211-
component.$set(key, data[key])
215+
transition.callHook(hook, component, (data, onError) => {
216+
let promises = []
217+
Object.keys(data).forEach(key => {
218+
let val = data[key]
219+
if (isPromise(val)) {
220+
promises.push(val.then(resolvedVal => {
221+
component.$set(key, resolvedVal)
222+
}))
223+
} else {
224+
component.$set(key, val)
225+
}
226+
})
227+
if (!promises.length) {
228+
component.$loadingRouteData = false
229+
} else {
230+
promises[0].constructor.all(promises).then(_ => {
231+
component.$loadingRouteData = false
232+
}, onError)
212233
}
213-
component.$loadingRouteData = false
214234
cb && cb(data)
215-
}, false, cleanup)
235+
}, {
236+
cleanup: cleanup,
237+
expectData: true
238+
})
216239
}

src/transition.js

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -187,27 +187,21 @@ export default class RouteTransition {
187187
* @param {Function} hook
188188
* @param {*} [context]
189189
* @param {Function} [cb]
190-
* @param {Boolean} [expectBoolean]
191-
* @param {Function} [cleanup]
190+
* @param {Object} [options]
191+
* - {Boolean} expectBoolean
192+
* - {Boolean} expectData
193+
* - {Function} cleanup
192194
*/
193195

194-
callHook (hook, context, cb, expectBoolean, cleanup) {
196+
callHook (hook, context, cb, {
197+
expectBoolean = false,
198+
expectData = false,
199+
cleanup
200+
} = {}) {
201+
195202
let transition = this
196203
let nextCalled = false
197204

198-
// advance the transition to the next step
199-
let next = (data) => {
200-
if (nextCalled) {
201-
warn('transition.next() should be called only once.')
202-
return
203-
}
204-
nextCalled = true
205-
if (!cb || transition.aborted) {
206-
return
207-
}
208-
cb(data)
209-
}
210-
211205
// abort the transition
212206
let abort = (back) => {
213207
cleanup && cleanup()
@@ -226,6 +220,19 @@ export default class RouteTransition {
226220
}
227221
}
228222

223+
// advance the transition to the next step
224+
let next = (data) => {
225+
if (nextCalled) {
226+
warn('transition.next() should be called only once.')
227+
return
228+
}
229+
nextCalled = true
230+
if (!cb || transition.aborted) {
231+
return
232+
}
233+
cb(data, onError)
234+
}
235+
229236
// expose a clone of the transition object, so that each
230237
// hook gets a clean copy and prevent the user from
231238
// messing with the internals.
@@ -259,6 +266,12 @@ export default class RouteTransition {
259266
}
260267
} else if (resIsPromise) {
261268
res.then(next, onError)
269+
} else if (expectData && isPlainOjbect(res)) {
270+
next(res)
262271
}
263272
}
264273
}
274+
275+
function isPlainOjbect (val) {
276+
return Object.prototype.toString.call(val) === '[object Object]'
277+
}

test/unit/specs/pipeline/data.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,53 @@ describe('data', function () {
114114
}, wait * 2)
115115
})
116116
})
117+
118+
it('return object containing promises', function (done) {
119+
test({
120+
data: {
121+
data: function (transition) {
122+
return {
123+
msg: new Promise(function (resolve) {
124+
setTimeout(function () {
125+
resolve(transition.to.params.msg)
126+
}, wait)
127+
})
128+
}
129+
}
130+
}
131+
}, function (router, calls, emitter) {
132+
router.go('/data/hello')
133+
assertCalls(calls, ['data.data'])
134+
expect(router.app.$el.textContent).toBe('loading...')
135+
setTimeout(function () {
136+
expect(router.app.$el.textContent).toBe('hello')
137+
done()
138+
}, wait * 2)
139+
})
140+
})
141+
142+
it('return object containing promises reject', function (done) {
143+
test({
144+
data: {
145+
data: function (transition) {
146+
return {
147+
msg: new Promise(function (resolve, reject) {
148+
setTimeout(function () {
149+
reject()
150+
}, wait)
151+
})
152+
}
153+
}
154+
}
155+
}, function (router, calls, emitter) {
156+
router.go('/data/hello')
157+
assertCalls(calls, ['data.data'])
158+
expect(router.app.$el.textContent).toBe('loading...')
159+
setTimeout(function () {
160+
// should complete the transition despite error
161+
expect(router.app.$el.textContent).toBe('')
162+
done()
163+
}, wait * 2)
164+
})
165+
})
117166
})

0 commit comments

Comments
 (0)