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(window.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

Resources

Building Mobile Apps with Vue.js

By Simon MacDonald

Building Mobile Apps with Vue.js

DevTeach 2017

  • 3,203