Building Mobile Apps with
and
Simon MacDonald
simonmacdonald.com
@macdonst
Building Nice Looking Mobile Apps is Hard!
In my mind, I envision something like this:
Sadly in reality it ends up looking like this:
Why is design so hard?
Because…
…and I'm pretty lazy
So, that's why I use
to save myself a ton of work
Framework7 is an open source HTML framework to develop apps with iOS & Android native look and feel
iOS
Material
<div id="app">
<f7-statusbar />
<f7-views>
<f7-view id="main-view" navbar-through :dynamic-navbar="true" main>
<f7-navbar v-if="isiOS" title="Home"></f7-navbar>
<f7-pages>
<f7-page>
<f7-navbar sliding title="Home">
<f7-block>
<f7-block-title>
{{ title }}
</f7-block-title>
<f7-block>
Lorem ipsum dolor sit amet,
consectetur adipisicing elit.
Distinctio est aliquam officiis
quaerat placeat, cum explicabo
magni soluta totam maxime autem
minima accusamus eos suscipit
dignissimos corporis modi
voluptatum fugiat!
</f7-block>
</f7-block>
</f7-page>
</f7-pages>
</f7-view>
</f7-views>
</div>
Framework7 Example
That's pretty neat, but the only tag I recognize is <div/>
I guess I put the cart before the horse and I should talk about
Vue is a progressive framework for building user interfaces. Unlike other monolithic frameworks, Vue is designed from the ground up to be incrementally adoptable. The core library is focused on the view layer only, and is very easy to pick up and integrate with other libraries or existing projects. On the other hand, Vue is also perfectly capable of powering sophisticated Single-Page Applications when used in combination with modern tooling and supporting libraries.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<title>My App</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
JavaScript
import Vue from 'vue';
import Framework7 from 'framework7';
import Framework7Vue from 'framework7-vue';
import Routes from './routes';
import App from './App';
Vue.use(Framework7Vue);
new Vue({
el: '#app',
template: '<app />',
// Register App Component
components: {
app: App,
},
});
App.vue
<template>
<div id="app">
<f7-statusbar />
<f7-views>
<f7-view id="main-view" navbar-through :dynamic-navbar="true" main>
<f7-navbar v-if="isiOS" title="Home"></f7-navbar>
<f7-pages>
<f7-page>
<f7-navbar sliding title="Home">
<f7-block>
<f7-block-title>{{ title }}</f7-block-title>
<f7-block>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio est aliquam officiis quaerat placeat, cum explicabo magni soluta totam maxime autem minima accusamus eos suscipit dignissimos corporis modi voluptatum fugiat!
</f7-block>
</f7-block>
</f7-page>
</f7-pages>
</f7-view>
</f7-views>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
title: 'Hello World',
};
}
};
</script>
Wow! So you are saying I can compose my UI of reusable components?
That's exactly what I'm saying!
Let's create new component in our app to show some art
Data Store
Vue.js provides two way data binding so we'll setup a store in our main.js to store the art.
import Vue from 'vue';
import Framework7 from 'framework7';
import Framework7Vue from 'framework7-vue';
import Routes from './routes';
import App from './App';
// Store
window.pictureStore = JSON.parse(localStorage.getItem('pictures')) || [];
Vue.use(Framework7Vue);
new Vue({
el: '#app',
template: '<app />',
// Register App Component
components: {
app: App,
},
});
Data Refresh
- Add pull to refresh to page
- Add pictureStore to globals
- Create onRefresh method
<template>
<div id="app">
<f7-statusbar />
<f7-views>
<f7-view id="main-view" navbar-through :dynamic-navbar="true" main>
<f7-navbar v-if="isiOS" title="Home"></f7-navbar>
<f7-pages>
<f7-page navbar-fixed pull-to-refresh @ptr:refresh="onRefresh">
<f7-navbar sliding title="Home">
<f7-block>
<f7-block-title>{{ title }}</f7-block-title>
<f7-block>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Distinctio est aliquam officiis quaerat placeat, cum explicabo magni soluta totam maxime autem minima accusamus eos suscipit dignissimos corporis modi voluptatum fugiat!
</f7-block>
</f7-block>
</f7-page>
</f7-pages>
</f7-view>
</f7-views>
</div>
</template>
<script>
/* globals pictureStore */
export default {
name: 'App',
data() {
return {
title: 'Hello World',
};
}
};
</script>
<script>
/* globals pictureStore */
export default {
name: 'App',
data() {
return {
title: 'Hello World',
};
},
methods: {
onRefresh(event, done) {
fetch('http://localhost:8974')
.then(response => response.json())
.then((data) => {
pictureStore.unshift(data);
localStorage.setItem('pictures', JSON.stringify(pictureStore));
done();
});
},
}
};
</script>
Picture List
- Create a PictureList.vue file
- Populate the template
- Add the script tag
<template>
<div>
<f7-card v-for="pics in pictures">
<f7-card-header>
<div class="facebook-name">{{ pics.artist }}</div>
</f7-card-header>
<f7-card-content>
<img :src="pics.image" width="100%"/>
</f7-card-content>
</f7-card>
</div>
</template>
<template>
<div>
<f7-card v-for="pics in pictures">
<f7-card-header>
<div class="facebook-name">{{ pics.artist }}</div>
</f7-card-header>
<f7-card-content><img :src="pics.image" width="100%"/></f7-card-content>
</f7-card>
</div>
</template>
<script>
/* global pictureStore */
export default {
name: 'PictureList',
data() {
return { pictures: window.pictureStore, };
},
};
</script>
Use PictureList
- Add <picture-list/> tag
- Import the PictureList
- Include picture-list component
<template>
<div id="app">
<f7-statusbar />
<f7-views>
<f7-view id="main-view" navbar-through :dynamic-navbar="true" main>
<f7-navbar v-if="isiOS" title="Home"></f7-navbar>
<f7-pages>
<f7-page navbar-fixed pull-to-refresh @ptr:refresh="onRefresh">
<f7-navbar sliding title="Home">
<picture-list/>
</f7-page>
</f7-pages>
</f7-view>
</f7-views>
</div>
</template>
<script>
/* globals pictureStore */
import PictureList from '../PictureList';
export default {
name: 'App',
data() {
return {
title: 'Hello World',
};
},
methods: {
onRefresh(event, done) {
fetch('http://localhost:8974')
.then(response => response.json())
.then((data) => {
pictureStore.unshift(data);
localStorage.setItem('pictures', JSON.stringify(pictureStore));
done();
});
},
}
};
</script>
<script>
/* globals pictureStore */
import PictureList from '../PictureList';
export default {
name: 'App',
data() {
return {
title: 'Hello World',
};
},
components: {
'picture-list': PictureList,
},
methods: {
onRefresh(event, done) {
fetch('http://localhost:8974')
.then(response => response.json())
.then((data) => {
pictureStore.unshift(data);
localStorage.setItem('pictures', JSON.stringify(pictureStore));
done();
});
},
}
};
</script>
That's awesome! How do the single file components get included in the app?
Oh, that's easy. Just use Webpack
Nope!
Wait, don't run away now! We've created some templates to get you started!
In each of the following templates we've done the heavy lifting setting up
Plus, we've added Mocha for unit testing and Nightwatch for end to end testing.
As well as setting up default layouts using
Honestly, we don't care if you use all this stuff in the template or import your own.
The issue we are trying to combat here is JavaScript Paralysis where you don't know how to get started.
Blank
Tabs
Split Pane iOS
Split Pane Android
Getting Started
npm install cordova -g
cordova create MyAppName --template=phonegap-template-vue-f7-split-panel
cd MyAppName
npm install
npm run dev
You can then open the app in your browser by visiting localhost:8080
Deploy your app
// Develop in the iOS simulator
cordova platform add ios
npm run cordova-run-ios
// Develop in the Android emulator
cordova platform add android
npm run cordova-run-android
// Build for production, removes hot module reloading server
npm run build
Then you are ready to deploy your app to the web, iOS App Store or Google Play Store.
The Future
- We're adding Progressive Web App support
- Decrease boilerplate code using sw-toolbox
- Templates that score high on Lighthouse
- New templates using Preact
The End
Building Mobile Apps with Vue.js
By Simon MacDonald
Building Mobile Apps with Vue.js
Ottawa JS
- 2,641