Build a chat app using Framework7
You will need Node and npm or Yarn installed on your machine.
Introduction
Realtime chat functionality has revitalized the way we communicate over the years. In recent times, customers can speak directly with business owners from their mobile applications and websites. This is a huge plus in technology considering how difficult it was in the past.
Framework7 is a free and open source mobile HTML framework to develop hybrid mobile apps with iOS and Android native look and feel. In this tutorial, we’ll build a realtime chat application with Framework7 using Pusher channels. Here’s a preview of what we’ll be building.
Prerequisites
Before you begin, ensure that you have Node and npm or Yarn installed on your machine. Here is a run-down of the core technologies we will be using.
-
Framework7 ****- a framework for building native-like mobile applications with HTML
-
Pusher - Pusher is a technology for building apps with varying realtime needs like push notifications and pub/sub messaging. It is the engine behind the realtime ability of our chat app.
-
Vue.js - an open-source JavaScript framework for building user interfaces
-
Phonegap - an application framework that enables you to build natively installed applications using HTML and JavaScript.
Setting up the project
The simplest way to set up a Phonegap project is through the command line interface (CLI). Before we create a Phonegap project, ensure that you have Phonegap installed on your machine. To install Phonegap, run:
$ npm install -g phonegap
This will install phonegap on your machine and you can confirm the installation by running the command:
$ phonegap -v
This should print the version of the currently installed Phonegap. Now that we have that installed let’s create a Phonegap project. Run:
// create a phonegap project with the framework7 vue template
$ phonegap create pusher-chat --template https://github.com/hollyschinsky/phonegap-template-framework7-vue
$ cd pusher-chat
//start the server
$ phonegap serve
If you get any prompts in the process, it is always safe to say yes within the scope of this demo. The phonegap serve
command will start your project on localhost:3000
. If you navigate to it on your browser, you should see this.
Setting up Pusher
Head over to Pusher and sign up for a free account.
Create a new app by selecting Channels apps on the sidebar and clicking Create Channels app button on the bottom of the sidebar:
Configure an app by providing basic information requested in the form presented. You can also choose the environment you intend to integrate Pusher with to be provided with some boilerplate code:
You can retrieve your keys from the App Keys tab:
Save your Pusher credentials somewhere in your editor where you can quickly retrieve it as we’ll be needing it soon.
Server configurations
We’ll be using a local Node server to establish communications with the client so as to render messages in realtime. Before we create our server, let’s install the packages it’ll need to run effectively. Back in your project root, run:
$ npm install --save axios body-parser cors express pusher pusher-js
Then in your code editor, create a new file server.js
and set it up like so:
//server.js
const Pusher = require('pusher');
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
var pusher = new Pusher({
appId: 'Your-app-Id',
key: 'Your-app-Key',
secret: 'Your-app-secret',
cluster: 'Your-app-cluster',
encrypted: true
});
app.set('PORT', process.env.PORT || 6000);
app.post('/message', (req, res) => {
const message = req.body;
pusher.trigger('chat', 'message', message);
res.send(message)
});
app.listen(app.get('PORT'), () =>
console.log('Listening at ' + app.get('PORT')))
Do not forget to replace the placeholder values here with your unique keys from Pusher.
Here we loaded all the necessary middlewares for the Express server and then configured Pusher using the credentials we got from our Pusher dashboard.
We also created the /``message
endpoint that will receive and process messages sent in from our app. Finally, we trigger an event named message
to Pusher Channels on a channel called chat
. The chat
channel holds a message payload that contains the message
sent by the user.
To start the server, run:
$ node server
The above command should output this message in your terminal:
Listening on port 6000
Application layout
Now let’s open the index.html
file and set it up. This is the entrance to our app and we’ll place a form to collect the user’s name and a button to take them to the chat page. Open the index.html
file, delete all the code in it and update it with this:
// www/index.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>My App</title>
<link rel="stylesheet" href="css/framework7.ios.min.css">
<link rel="stylesheet" href="css/framework7.ios.colors.min.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div id="app">
<f7-statusbar></f7-statusbar>
<f7-views>
<f7-view id="main-view" navbar-through :dynamic-navbar="true" main>
<f7-navbar back-link="Back" sliding>
<f7-nav-center sliding>Pusher-Framework7 App </f7-nav-center>
</f7-navbar>
<f7-pages>
<f7-page>
<f7-list>
</f7-list-item>
<f7-list-item>
<f7-input type="text" v-model:value="name" placeholder="Your name" clear-button></f7-input>
</f7-list-item>
</f7-list-item>
<f7-list-button v-on:click="enterChat">Enter Chat</f7-list-button>
</f7-list>
</f7-page>
</f7-pages>
</f7-view>
</f7-views>
</div>
<template id="page-chat">
<f7-page>
<!-- chat page goes here ... -->
</f7-page>
</template>
<!-- Add CDN for Pusher and Axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
<script src="cordova.js"></script>
<script src="js/framework7.min.js"></script>
<script src="js/vue.min.js"></script>
<script src="js/framework7-vue.min.js"></script>
<script src="js/app.js"></script>
</body>
</html>
Notice we added the CDN for both Pusher and Axios in the scripts above. Next, open the app.js
file and replace the code there in it with this:
// www/js/app.js
(function(){
function init(){
Vue.use(Framework7Vue)
Vue.component('page-chat', {
template: '#page-chat',
});
// Init App
new Vue({
el: '#app',
methods: {
enterChat: function(){
if(this.name.trim().length === 0 ){
alert(" Enter your name ...")
return false;
}
}
},
framework7: {
root: '#app',
// material: true,
routes: [
{
path: '/chat/',
component: 'page-chat'
}
],
}
});
}
// Handle device ready event
document.addEventListener('deviceready', init, false)
})();
At this point, if you save both edited files and check back on your browser, you should now get this look:
Here we can supply a name and click the Enter Chat button to enter the chat page, but at this point it won’t work. That is expected, given that we’ve not defined a function to handle the button click event. To fix that, let’s go ahead and do some data binding between our component and our states. Update the app.js
file like so:
// www/js/app.js
// init states
var states = {
name: '',
msgs: []
};
(function(){
function init(){
Vue.use(Framework7Vue)
Vue.component('page-chat', {
template: '#page-chat',
data: function(){
return states;
},
// handle onSend
methods: {
onSend: function(text, clear){
console.log("clicked")
if( typeof clear == 'function' ) clear()
}
}
});
// Init App
new Vue({
el: '#app',
data: function(){
return states;
},
// handle enterChat button
methods: {
enterChat: function(){
if(this.name.trim().length === 0 ){
alert(" Enter your name ...")
return false;
}
this.msgs.length = 0;
this.$f7.mainView.router.load({url:'/chat/'});
}
},
framework7: {
root: '#app',
/* Uncomment to enable Material theme: */
// material: true,
routes: [
{
path: '/chat/',
component: 'page-chat'
}
],
}
});
}
// Handle device ready event
document.addEventListener('deviceready', init, false)
})();
Here we defined a states object to hold the name of the user and an array of messages. Then we added the missing callbacks that we defined in our HTML file to handle the click events on the buttons.
If the user supplies a name and clicks the Enter Chat button, it’ll open up the chat page, but if that is not the case, it’ll alert the user to a name. Update your app.js
file with the snippet above and save. Now go back to the browser, enter a name and click Enter Chat. This should now open up the empty chat page.
Next, we’ll set up the chat page to have a simple chat form with an input to collect the message and a button to send it. Open the index.html
file and update the chat page template section with this code:
// www/index.html
// ...
<f7-messages>
<f7-message v-for="msg in msgs" :name="msg.name" :text="msg.text" :type="msg.type">
</f7-message>
</f7-messages>
<f7-messagebar placeholder="type message" send-link="Send" v-on:submit="onSend" >
</f7-messagebar>
// ...
Add this code in the section where we left a comment to add chat page. Save the index.html
file and check back on your browser. The chat page should look like this:
Realtime updates
At this point, we can type messages, but nothing happens when you try to send. This is because we haven’t integrated Pusher. At the beginning we set up Pusher and retrieved our app keys, we’ll now use it to configure our project. Open the app.js
file and add this code:
// www/js/app.js
const pusher = new Pusher('YOUR_PUSHER_KEY', {
cluster: 'YOUR_CLUSTER',
encrypted: true,
});
const channel = pusher.subscribe('chat');
channel.bind('message', data => {
console.log(data)
var type = data.name == states.name ? 'sent':'received'
var name = type == 'sent'? states.name : data.name;
states.msgs.push({name:name, text:data.text, type:type});
});
// ...
This sets up Pusher in our client with the credentials from our dashboard. We subscribe to the chat
channel we defined on the server and bind our response data to the appropriate view objects to display them on screen.
Now to send the chat to the server when the user clicks the Send button, let’s update the onSend function in the app.js
file:
// www/js/app.js
...
// Init Page Components
Vue.component('page-chat', {
template: '#page-chat',
data: function(){
return states;
},
methods: {
onSend: function(text, clear){
console.log("clicked")
var message = {
name: this.name,
text: text
}
axios.post('http://localhost:6000/message', message);
if( typeof clear == 'function' ) clear()
}
}
});
// Init App
new Vue({
el: '#app',
data: function(){
return states;
},
methods: {
enterChat: function(){
if(this.name.trim().length === 0 ){
alert(" Enter your name ...")
return false;
}
this.msgs.length = 0;
this.$f7.mainView.router.load({url:'/chat/'});
}
},
...
Here we define a message object that takes the name of the user and text message to send. Then we use Axios to post the message object to the /messages
endpoint we defined on the server.
Testing
To test out the application, ensure that both Phonegap server and the Node server is running. If that’s not the case, you can start the Phonegap server in project root with phonegap serve
and the Node server with node server
.
Before testing your application, I recommend you compare your codes with mine to ensure that you did everything right so as not to encounter any blockers. Compare your index.html
, server.js
and app.js
. If everything looks alright, now go ahead and test the application.
Open two browser windows to localhost:3000
and send messages:
Conclusion
The importance of realtime engagement of users on any platform cannot be overemphasized. As seen in this tutorial, we were able to implement a realtime chat system with Framework7 and Pusher. I hope you found this tutorial helpful.
Feel free to explore the code for this tutorial on GitHub and add more features as you deem fit.
9 July 2018
by Christian Nwamba