Before reading this section, we suggest you reading Getting Started and Fundamentals to grasp the basics of Onsen UI. Don’t worry, it won’t take more than 5 minutes.
Vue bindings for Onsen UI (VueOnsen) provide Vue 2 components and directives that wrap the core Web Components and expose a Vue-like API to interact with them.
In this guide, we’ll walk you through how to set up Onsen UI + Vue and cover basics to get started.
If you install version 3 of the Vue CLI, you can take advantage of features that make setting up an Onsen UI + Vue project really simple.
First, make sure you have at least version 3 of the CLI (the @vue/cli
package, not vue-cli
).
npm install -g @vue/cli
Now you can set up your app by using the create
command. This will guide you through various options. Please note: For starting your Onsen UI phone app do not add the Progressive Web App (PWA) support option which is for deploying to a website and not to a device.
vue create my-onsen-app
Once you have a Vue project set up, you just need to install the core Onsen UI package (onsenui
), and the Vue bindings (vue-onsenui
). Inside the project root, run:
npm install onsenui vue-onsenui
Inside your project’s main file (usually main.js
), Onsen UI’s files need to be imported.
// Webpack CSS import
import 'onsenui/css/onsenui.css';
import 'onsenui/css/onsen-css-components.css';
// JS import
import Vue from 'vue';
import VueOnsen from 'vue-onsenui'; // This imports 'onsenui', so no need to import it separately
Vue.use(VueOnsen); // VueOnsen set here as plugin to VUE. Done automatically if a call to window.Vue exists in the startup code.
To get started, let’s create a simple Hello World application. Projects set up using the Vue CLI are single file components. This means the HTML, CSS and JS are all contained in one .vue
file. The default project created by the CLI will have at least an App.vue
file. Edit it to look like this.
<template id="main-page">
<v-ons-page>
<v-ons-toolbar>
<div class="center">Title</div>
</v-ons-toolbar>
<p style="text-align: center">
<v-ons-button @click="$ons.notification.alert('Hello World!')">
Click me!
</v-ons-button>
</p>
</v-ons-page>
</template>
<style>
/* CSS goes here */
</style>
<script>
// Javascript goes here
</script>
Projects created by the Vue CLI come with various scripts, including one to serve the project so you can preview it in the browser.
npm run serve
Open the given URL in your browser. If you click the button, you will see an alert dialog. That’s it!
To continue from here, take a look at the Onsen UI Vue Components list. You should also take a look at our Kitchensink Example which shows all the Onsen UI components being used with Vue. For more information about Vue itself, we recommend reading the official Vue docs. If you want to know more about how the bindings word, keep reading below.
Onsen UI’s Vue components are simple wrappers around inner Custom Elements in most of the cases. This means that a Vue Component takes some props and translates them into DOM properties, DOM attributes or method calls for the Onsen UI core. It also listens for native events and fires the corresponding Vue events. If you inspect the DOM you will likely see a bunch of ons-*
components without v-*
prefix (these are real HTML elements). You can have a look at the implementation here.
Since v-ons-*
components compile into ons-*
DOM elements, you can use this knowledge to style your component with tag names as well. For example, if you want to style a button, you should target ons-button
, not v-ons-button
.
v-ons-page
component compiles into ons-page
custom element. This element, at the same time, processes its content and filters scrollable and fixed elements. Scrollable content is moved into a special div.page__content
wrapper. This behavior might create issues under some specific situations, like using v-for
with asynchronous data. To avoid any possible issue, manually providing a div.content
element is recommended:
<v-ons-page>
<v-ons-toolbar></v-ons-toolbar>
<div class="content">
<!-- Scrollable content here -->
<v-ons-input></v-ons-input>
<div v-for="item in asyncAjaxItems"></div>
</div>
<!-- Fixed content here -->
<v-ons-fab></v-ons-fab>
</v-ons-page>
See also Components Compilation section for more details about this.
The original ons
object is not available in the global scope in Vue applications. Instead, it is provided in every Vue instance as this.$ons
through Vue’s protoytpe:
<v-ons-button @click="$ons.notification.alert('Hi there!')">
Click me
</v-ons-button>
DOM events fired by Onsen UI elements are translated into Vue events in the corresponding components. For example, v-ons-navigator
can handle a postpush
event with @postpush="..."
.
From `vue@2.4.0and
vue-onsenui@2.1.0onwards, native DOM events are passed down to the children. This means that
will work __without__
nativemodifier. Prior to these versions,
@change.native=”…”` syntax is required. Note that these events are still DOM events and, as such, Vue DevTools won’t show any Vue event for these.
The exception to this is click
event, which is turned into a Vue event in some components. The reason is that click
event is used to override default behaviors:
<v-ons-back-button @click.prevent="pageStack.splice(1)"></v-ons-back-button>
The previous example overrides the v-ons-back-button
default behavior, popping 1 single page, and resets the stack to the first page instead. The prevent
modifier is just calling $event.preventDefault()
.
Additionally, components that are capable of handling Cordova’s backbutton
event (Android’s back button), can listen for this event with @deviceBackButton
handler.
<v-ons-dialog @deviceBackButton="$event.callParentHandler()"></v-ons-dialog>
More information about this event in the original Cordova-specific Features section.
Input components in Onsen UI (such as v-ons-input
or v-ons-checkbox
) support Vue’s v-model
directive:
<v-ons-input v-model="something"></v-ons-input>
The only exceptions are v-model
modifiers, which are not supported by custom components as of `vue@2.3, unfortunately. However, there is a workaround for the most important one, the
lazy` modifier:
<v-ons-input v-model="something" model-event="change"></v-ons-input>
This will update the model on change
events instead of input
(default). The rest of the modifiers can be easily implemented in other ways.
It is also possible to bind arrays to v-ons-checkbox
or v-ons-switch
just like normal <input type="checkbox">
, and make radio groups with v-model
for v-ons-radio
.
Apart from the full vue-onsenui
bundle, an ESM version is provided under vue-onsenui/esm
since `vue-onsenui@2.4.0`. The main benefit of using this distribution is that components you don’t use will be left out of the bundle without relying on tree shaking algorithms. This helps reduce your app’s size. See our core guide to ES Modules for more information.
import Vue from 'vue';
import VueOnsen from 'vue-onsenui/esm';
import VOnsPage from 'vue-onsenui/esm/components/VOnsPage';
import VOnsToolbar from 'vue-onsenui/esm/components/VOnsToolbar';
Vue.use(VueOnsen);
Vue.component(VOnsPage.name, VOnsPage);
Vue.component(VOnsToolbar.name, VOnsToolbar);
The previous code imported VueOnsen
(which is just an alias for $ons
object) and registers it in Vue. This step does not include any components at all, unlike import VueOnsen from 'vue-onsenui'
would do. Therefore, we can import the necessary components manually and register them as global Vue components (optional). Components have a name
property that can be used for registering them in Vue (v-ons-page
, v-ons-button
, etc).
Since importing and registering many components can be very verbose, we recommend creating an extra file with the desired list of components using the following export
syntax:
// onsenui-components.js
export { default } from 'vue-onsenui/esm/components/VOnsToolbar';
export { default } from 'vue-onsenui/esm/components/VOnsNavigator';
export { default } from 'vue-onsenui/esm/components/VOnsPage';
export { default } from 'vue-onsenui/esm/components/VOnsCarousel';
Then, simply import and register everything in your main file:
// main.js
import Vue from 'vue';
import VueOnsen from 'vue-onsenui/esm';
import * as OnsenComponents from './onsenui-components.js';
Vue.use(VueOnsen);
Object.values(OnsenComponents).forEach(c => Vue.component(c.name, c));
In case you don’t want to use the default c.name
provided, you can assign a new one with export { default as CustomName } from 'vue-onsenui/esm/component/...;'
and then using the object key instead of c.name
.
Turning your Onsen UI + Vue project into a Cordova app is simple using the vue-cli-plugin-cordova plugin. Simply follow the instructions there to get going.
The main guide describes how to disable or set some global features “right after including onsenui.js
in the app”. This means that it must take effect before any component is rendered. Since the ons
object is not provided globally, we need to use the $ons
object at the very first possible location:
import Vue from 'vue';
import VueOnsen from 'vue-onsenui';
Vue.use(VueOnsen);
new Vue({
el: '#app',
render: h => h(...),
beforeCreate() {
this.$ons.disableAutoStyling(); // Or any other method
}
})
This way, changes take effect before any component is rendered.
A Navigator’s pages are sibling elements, which means that communication among them in Vue is fairly challenging. Vuex is a good solution for this, but not the only one. When you push a new page component and want to add some initial data, you can simply extend it:
import nextPage from 'nextPage.vue';
// ...
pageStack.push({
extends: nextPage,
data() {
return {
myCustomDataHere: 42
};
}
})
In order to send data back to the previous page before popping, take a look at Vue state management.
Absolutely. Vuex is a good solution for component communication. If you feel you have too many props and events, Vuex may be a good fit. For an example of Onsen UI + Vue + Vuex, see the kitchensink app.
Onsen UI provides its own routing components: v-ons-navigator
, v-ons-tabbar
and v-ons-splitter
. These components can be mixed in many ways to achieve the desired route pattern. This is generally enough for mobile single-page application, so there is no need to plug an external router.
However, for those who really want to integrate vue-router, it is fully compatible with Onsen UI. It simply has to be integrated with the three routing components mentioned above.
Splitter:
For v-ons-splitter
, simply put your router-view
in the place where v-ons-page
components are normally located. In other words, inside v-ons-splitter-content
or v-ons-splitter-side
.
<v-ons-splitter>
<v-ons-splitter-side>...</v-ons-splitter-side>
<v-ons-splitter-content>
<router-view></router-view>
</v-ons-splitter-content>
</v-ons-splitter>
Tabbar:
v-ons-tabbar
accepts a slots syntax that allows us to include router-view
in the pages slot. Also, v-ons-tab
default behavior must be overriden in the click
handler in order to let the router do the job.
<v-ons-tabbar>
<router-view slot="pages"></router-view>
<v-ons-tab v-for="name in ['Home', 'Settings']" :key="name"
:label="name"
:active="$route.name === name"
@click.prevent="$router.push({name})"
></v-ons-tab>
</v-ons-tabbar>
Navigator: In this case we don’t need <router-view>
at all. The router simply has to decide what’s going to be the next page stack for the navigator, and the latter will do all the job (transitions, events, etc.):
template: `
<div id="app">
<v-ons-navigator swipeable
:page-stack="pageStack"
:pop-page="goBack"
></v-ons-navigator>
</div>
`,
methods: {
goBack() {
this.$router.push({ name: this.$route.matched[this.$route.matched.length - 2].name });
}
},
created() {
const mapRouteStack = route => this.pageStack = route.matched.map(m => m.components.default);
mapRouteStack(this.$route);
this.$router.beforeEach((to, from, next) => mapRouteStack(to) && next());
}
Notice that the previous example is adding every route to the Navigator’s page stack. It assumes all the routes have a valid page (v-ons-page
) as their default component. If this is not your case, simply filter out the routes you don’t need.
Once the router gives the new page stack to the Navigator, this one compares the current stack with the previous one. If the number of pages/routes has decreased and the top page has changed, it will perform a ‘popPage’ transition. Otherwise it will be ‘pushPage’.
Due to this, going back in the browser history ($router.go(-1)
) to a deeper route will push a page rather than popping. Therefore, going back in the history with the v-ons-back-button
is not recommended unless you have extra logic in your router to avoid unwanted behaviors.
In this example, the v-ons-back-button
components runs goBack
method, which is simply pushing the parent route (asumming it is the previous v-ons-page
) instead of going back in the history. This behavior will likely fit most mobile apps.
For a more complete example of v-ons-navigator
with vue-router
, have a look at this repository.