Skip to content

Commit e82d067

Browse files
committed
feat(devtools): show navigation entries under Frame
Instead of showing a NavigationEntry component under the Root element, we now properly display the pages that have been navigated to in a Frame under the Frame itself. Additionally we now use the `key` to show additional metadata about a specific navigation in development mode. BREAKING CHANGES: A Frame now only accepts a single child element, that is the defaultPage for the Frame. If you have multiple pages nested under the Frame element, you will need to refactor to use `$navigateTo` instead. Additionally the default slot is only rendered once, meaning that it will not be reactive after the initial render. This is due to how the Frame element works, it renders the defaultPage, and then "navigates" to it, and it is no longer a direct child of the Frame and cannot be reactive. If this affects you, plese open an issue and describe your use case, so we can discuss possible solutions. For most users this will not requre any additional refactoring.
1 parent 4d28b34 commit e82d067

File tree

5 files changed

+110
-24
lines changed

5 files changed

+110
-24
lines changed

platform/nativescript/plugins/navigator-plugin.js

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,24 @@
11
import { isObject, isDef, isPrimitive } from 'shared/util'
22
import { getFrame } from '../util/frame'
3+
import { updateDevtools } from '../util'
4+
5+
let sequentialCounter = 0
6+
7+
function serializeNavigationOptions(options) {
8+
if (process.env.NODE_ENV === 'production') {
9+
return null
10+
}
11+
12+
const allowed = ['backstackVisible', 'clearHistory']
13+
14+
return Object.keys(options)
15+
.filter(key => allowed.includes(key))
16+
.map(key => {
17+
return `${key}: ${options[key]}`
18+
})
19+
.concat(`uid: ${++sequentialCounter}`)
20+
.join(', ')
21+
}
322

423
export function getFrameInstance(frame) {
524
// get the frame that we need to navigate
@@ -54,17 +73,21 @@ export default {
5473
return new Promise(resolve => {
5574
const frame = getFrameInstance(options.frame)
5675
const navEntryInstance = new Vue({
76+
abstract: true,
77+
functional: true,
5778
name: 'NavigationEntry',
58-
parent: this.$root,
79+
parent: frame,
5980
frame,
60-
props: {
61-
frame: {
62-
default: frame.id
63-
}
64-
},
65-
render: h => h(component, { props: options.props })
81+
render: h =>
82+
h(component, {
83+
props: options.props,
84+
key: serializeNavigationOptions(options)
85+
})
6686
})
6787
const page = navEntryInstance.$mount().$el.nativeView
88+
page.__isNavigatedTo = true
89+
90+
updateDevtools()
6891

6992
const handler = args => {
7093
if (args.isBackNavigation) {

platform/nativescript/runtime/components/frame.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { setFrame, getFrame, deleteFrame } from '../../util/frame'
2+
import { warn } from 'core/util/debug'
23

34
let idCounter = 1
45

@@ -39,10 +40,12 @@ export default {
3940
},
4041
data() {
4142
return {
43+
// isFirstRender: true,
4244
properties: {}
4345
}
4446
},
4547
created() {
48+
this._isFirstRender = true
4649
let properties = {}
4750

4851
if (getFrame(this.$props.id)) {
@@ -57,13 +60,30 @@ export default {
5760
deleteFrame(this.properties.id)
5861
},
5962
render(h) {
63+
let vnode = null
64+
65+
// Render slot on first render to ensure default page is displayed
66+
if (this.$slots.default && this._isFirstRender) {
67+
if (
68+
process.env.NODE_ENV !== 'production' &&
69+
this.$slots.default.length > 1
70+
) {
71+
warn(
72+
`The <Frame> element can only have a single child element, that is the defaultPage.`
73+
)
74+
}
75+
this._isFirstRender = false
76+
vnode = this.$slots.default[0]
77+
vnode.key = 'default'
78+
}
79+
6080
return h(
6181
'NativeFrame',
6282
{
6383
attrs: this.properties,
6484
on: this.$listeners
6585
},
66-
this.$slots.default
86+
[vnode]
6787
)
6888
},
6989
methods: {
@@ -97,13 +117,19 @@ export default {
97117
},
98118

99119
notifyPageMounted(pageVm) {
120+
console.log('Page Mounted.')
100121
let options = {
101122
backstackVisible: this.backstackVisible,
102123
clearHistory: this.clearHistory,
103124
create: () => pageVm.$el.nativeView
104125
}
105126

106127
this.$nextTick(() => {
128+
if (pageVm.$el.nativeView.__isNavigatedTo) {
129+
// Ignore pages we've navigated to, since they are already on screen
130+
return
131+
}
132+
107133
this.navigate(options)
108134
})
109135
},

platform/nativescript/runtime/components/page.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { updateDevtools } from '../../util'
2+
13
export const PAGE_REF = '__vuePageRef__'
24

35
export default {
@@ -35,6 +37,7 @@ export default {
3537
this.$el.nativeView.disposeNativeView = (...args) => {
3638
this.$parent.$destroy()
3739
dispose.call(this.$el.nativeView, args)
40+
updateDevtools()
3841
}
3942
},
4043
methods: {

platform/nativescript/util/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,13 @@ export function deepProxy(object, depth = 0) {
111111
}
112112
})
113113
}
114+
115+
export function updateDevtools() {
116+
if (global.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
117+
try {
118+
global.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('flush')
119+
} catch (err) {
120+
//
121+
}
122+
}
123+
}

samples/app/app-with-pages.js

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
const Vue = require('nativescript-vue')
22

3-
Vue.config.debug = true
3+
const VueDevtools = require('nativescript-vue-devtools')
4+
Vue.use(VueDevtools)
5+
6+
Vue.config.devtools = true
7+
Vue.config.debug = false //true
48
Vue.config.silent = false
59

6-
const App = {
10+
const ToggledComp = {
11+
template: `<Label text="im toggled" />`
12+
}
13+
14+
const DefaultPage = {
15+
name: 'DefaultPage',
16+
17+
components: { ToggledComp },
718
template: `
8-
<Frame>
9-
<Page>
10-
<ActionBar class="action-bar" title="Home Page">
11-
<ActionItem text="Action"></ActionItem>
12-
</ActionBar>
13-
<StackLayout>
14-
<Button text="Open page" @tap="openPage" />
15-
</StackLayout>
16-
</Page>
17-
</Frame>
18-
`,
19+
<Page>
20+
<ActionBar class="action-bar" title="Home Page">
21+
<ActionItem text="Action"></ActionItem>
22+
</ActionBar>
23+
<StackLayout>
24+
<Button text="Open page" @tap="openPage" />
25+
</StackLayout>
26+
</Page>`,
1927

2028
methods: {
2129
openPage() {
@@ -25,29 +33,45 @@ const App = {
2533
}
2634

2735
const DetailsPage = {
36+
name: 'DetailsPage',
2837
template: `
2938
<Page>
3039
<ActionBar class="action-bar" title="Details Page">
3140
<ActionItem text="Action"></ActionItem>
3241
</ActionBar>
3342
<StackLayout>
3443
<Label :text="'Details ' + Math.random()" />
35-
<Button text="another" @tap="openDetails" />
44+
<Button text="another" @tap="openDetails({})" />
45+
<Button text="another backstackVisible=false" @tap="openDetails({backstackVisible: false})" />
46+
<Button text="another clearHistory=true" @tap="openDefault({clearHistory: true})" />
3647
<Button text="back" @tap="goBack" />
3748
</StackLayout>
3849
</Page>
3950
`,
4051

4152
methods: {
42-
openDetails() {
43-
this.$navigateTo(DetailsPage)
53+
openDetails(options = {}) {
54+
this.$navigateTo(DetailsPage, options)
55+
},
56+
openDefault(options) {
57+
this.$navigateTo(DefaultPage, options)
4458
},
4559
goBack() {
4660
this.$navigateBack()
4761
}
4862
}
4963
}
5064

65+
const App = {
66+
name: 'App',
67+
components: { DefaultPage },
68+
template: `
69+
<Frame>
70+
<DefaultPage/>
71+
</Frame>
72+
`
73+
}
74+
5175
new Vue({
5276
render: h => h(App)
5377
}).$start()

0 commit comments

Comments
 (0)