This is a basic Bootstrap menu that works for browsers and automatically collapses for mobile devices.
index.htm
<!DOCTYPE html>
<style>
</style>
<html>
<head>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</head>
<body>
<div id="app">
<div class="container">
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="#">Page 1</a></li>
<li class="active"><a href="#">Page 2</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Settings</a></li>
<li><a href="#">Language</a></li>
<li><a href="#">Login</a></li>
</ul>
</div><!--/.nav-collapse -->
</div><!--/.container-fluid -->
</nav>
</div>
</div>
</body>
</html>
Allows user to click on a button, which calls a function that builds a CSV file and opens it up in Microsoft Excel, for instance, if they have it installed.
index.htm
<script>
function download() {
const items = [
{name: 'Jim', score: 34},
{name: 'Jane', score: 22},
{name: 'Robert', score: 31}
];
const filename = 'scores.csv';
let text = '';
text += 'name;score\n';
for (item of items) {
text += item.name + ';' + item.score + '\n';
}
let element = document.createElement('a');
element.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
</script>
This code example shows how you can call one vue.js method from both outside the vue.js object on pageload, as well as via a button click. This would be helpful if you want to e.g. load initial values via an ajax call, for instance.
HTML
<!DOCTYPE html>
<html>
<head>
<title>vue.js onload test</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/pure_1.0.0/pure.css">
<script type="text/javascript" src="js/vue.min.js"></script>
<script type="text/javascript" src="js/axios.min.js"></script>
<style type="text/css">
body {
padding: 10px;
}
.sideBySide {
float: left;
}
.waitIcon {
width: 22px;
margin: 0 0 0 5px;
}
.clear {
clear: both;
}
.message {
margin: 5px 0 0 0;
}
</style>
</head>
<body>
<div id="app">
<div>
<button class="sideBySide" v-on:click="getMessage('clicked')" :disabled="status=='fetching'">Get Message</button>
<img class="waitIcon sideBySide" src="images/ajax_loading.gif" v-if="status=='fetching'"/>
<div class="clear"></div>
<div>
<div class="message">{{message}}</div>
</div>
<script>
var vue = new Vue({
el: '#app',
data: {
message: '',
numberOfTimes: 0,
status: 'ready'
},
methods: {
getMessage: function(option) {
this.numberOfTimes++;
this.status = 'fetching';
var that = this;
axios({
method: 'post',
url: 'data.php',
data: {
option: option
}
}).then(function (response) {
that.message = response.data['message'] + ' (option=' + option + '), executed: ' + that.numberOfTimes;
that.status = 'ready';
})
.catch(function (error) {
console.log(error);
});
}
}
});
vue.getMessage('loaded');
</script>
</body>
</html>
PHP
$_POST = json_decode(file_get_contents('php://input'), true);
//get user data
$option = $_POST['option'];
//sanitize
$option = filter_var ($option, FILTER_SANITIZE_STRING);
$data = [];
$data['message'] = 'from server';
//simulate network delay
sleep(1);
echo json_encode($data);
This example uses vue.js and axios to enable the user to find files by typing text into a textbox, each keystroke searching files in a directory and displaying those that match the typed keyword. Newly added to this example are two PHP classes which are loaded with include_once(). The classes contain helper functions such as enabling the text in the found files to be highlighed via regex. Autoload is not yet implemented, but in this way you can add any classes you need, which makes this example the beginning of an interactive web app.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/pure_1.0.0/pure.css">
<link rel="stylesheet" href="css/main.css">
<script type="text/javascript" src="js/vue.min.js"></script>
<script type="text/javascript" src="js/axios.min.js"></script>
<script type="text/javascript" src="js/jquery-2.2.4.min.js"></script>
</head>
<body>
<div id="app">
<form class="pure-form">
<fieldset>
<legend>Search Files</legend>
<input id="searchText" v-on:keyup="getNewText()" v-model="searchText" type="text" placeholder="search text" :disabled="timesLoaded == 0"/>
<img v-show="status=='loading'" class="loadingGraphic" style="display:none" src="images/ajax_loading.gif"/>
</fieldset>
<div class="resultArea">
<table v-if="timesLoaded > 0" class="pure-table pure-table-striped">
<thead>
<th><span v-html="howManyMessage"></span></th>
</thead>
<tbody>
<tr v-for="pathAndFileName in pathAndFileNames">
<td v-html="pathAndFileName"></td>
</tr>
</tbody>
</table>
</div>
</form>
</div>
<script>
var vue = new Vue({
el: '#app',
data: {
status: 'waiting',
searchText: '',
pathAndFileNames: [],
howManyMessage: '',
timesLoaded: 0
},
methods: {
getNewText: function() {
this.status = 'loading';
var that = this;
axios({
method: 'post',
url: 'data.php',
data: {
searchText: that.searchText
}
}).then(function (response) {
var pathAndFileNames = response.data['pathAndFileNames'];
var howManyMessage = response.data['howManyMessage'];
that.pathAndFileNames = pathAndFileNames;
that.howManyMessage = howManyMessage;
that.status = 'waiting';
that.timesLoaded++;
if(that.timesLoaded == 1) {
setTimeout(function() { $('#searchText').focus() }, 100);
}
})
.catch(function (error) {
console.log(error);
});
}
}
});
vue.getNewText();
</script>
</body>
</html>
data.php
include_once('classes/qstr.php');
include_once('classes/qfil.php');
$_POST = json_decode(file_get_contents('php://input'), true);
//config
$dir = 'C:\test';
//get user data
$searchText = $_POST['searchText'];
//sanitize
$searchText = filter_var ($searchText, FILTER_SANITIZE_STRING);
//get files
$rawPathAndFileNames = qfil::getRecursiveFiles($dir);
$pathAndFileNames = [];
if(count($rawPathAndFileNames) > 0) {
foreach($rawPathAndFileNames as $rawPathAndFileName){
if( $searchText == '' || strpos(strtolower($rawPathAndFileName), strtolower($searchText)) !== false) {
$rawPathAndFileName = qstr::addClassToUpperLowerFindText($rawPathAndFileName, $searchText, 'highlight');
$pathAndFileNames[] = $rawPathAndFileName;
}
}
}
$data = [];
$data['pathAndFileNames'] = $pathAndFileNames;
$data['howManyMessage'] = qstr::smartPlural(count($pathAndFileNames), 'files');
echo json_encode($data);
This code combines Vue.js, Axios, and PHP with Pure CSS to create a simple HTML interface which sends and receives data via AJAX calls. Note the that = this
trick to get Axios to recognize the vue.js internal variables inside its then
scope. Pure CSS offers a neutral design and has a much smaller footprint than Bootstrap, and the forms are responsive enough to work fine on mobile displays. The string sanitizing is taken care of by PHP's filter_var
, very simple but allows you to confidently send untrusted-user data to the frontend along with your own HTML, which can be easily displayed with the Vue.js v-html
attribute. All together, it's a nice, little combination of technologies to create a quick, online tool to process user data.
HTML / Vue.js / Pure CSS
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="css/pure_1.0.0/pure.css">
<link rel="stylesheet" href="css/main.css">
<script type="text/javascript" src="js/vue.min.js"></script>
<script type="text/javascript" src="js/axios.min.js"></script>
</head>
<style type="text/css">
code {
background-color:#eee;
}
.resultArea {
border-top: 1px solid #eee;
padding-top: 10px;
}
</style>
<body>
<div id="app">
<form class="pure-form">
<fieldset>
<legend>Create Full Name</legend>
<input v-model="firstName" type="text" placeholder="First Name">
<input v-model="lastName" type="text" placeholder="Last Name">
<button type="button" v-on:click="getNewText()" class="pure-button" :disabled="status=='loading'">Create</button>
<img v-show="status=='loading'" class="loadingGraphic" style="display:none" src="images/ajax_loading.gif"/>
</fieldset>
<div class="resultArea">
<div>Protected Text: <code>{{message}}</code></div>
<div>Protected HTML: <span v-html="message"></span></div>
</div>
</form>
</div>
<script>
new Vue({
el: '#app',
data: {
status: 'waiting',
firstName: '',
lastName: '',
message: ''
},
methods: {
getNewText: function() {
this.status = 'loading';
var that = this;
axios({
method: 'post',
url: 'data.php',
data: {
firstName: this.firstName,
lastName: this.lastName
}
}).then(function (response) {
var message = response.data['message'];
that.message = message;
that.status = 'waiting';
})
.catch(function (error) {
console.log(error);
});
}
}
});
</script>
</body>
</html>
PHP
$_POST = json_decode(file_get_contents('php://input'), true);
$firstName = $_POST['firstName'];
$lastName = $_POST['lastName'];
//sanitize
$firstName = filter_var ( $firstName, FILTER_SANITIZE_STRING);
$lastName = filter_var ( $lastName, FILTER_SANITIZE_STRING);
//$numberOfTimes = filter_var ( $numberOfTimes, FILTER_SANITIZE_NUMBER_INT);
$data = [];
$data['message'] = 'The full name is <b>' . trim($firstName . ' ' . $lastName) . '</b>.';
//simulate network connection lag time to show animated graphic
sleep(1);
echo json_encode($data)
This shows the basic functionality of vue.js, e.g. loops, models, if statements, inputs and checkbox interactivity. Vue.js has a much smaller footprint than Angular.js but provides the same 2-way binding which makes interactive pages considerably easier to make than with jQuery.
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript" src="js/vue.min.js"></script>
</head>
<body>
<div id="vuejs_app_test">
<p>{{message}}</p>
<p v-html="htmlMessage"></p>
<p>{{ status=='on' ? 'The status is on.' : 'The status is off.'}}</p>
<p v-if="finished">It is finished.</p>
<p>You can go to <a v-bind:href="infoUrl">the info URL</a>.</p>
<p>This is the {{message | addBrackets}} and here is {{status | addBrackets}}.</p>
<p>{{getStatusMessage}}</p>
<p>First Name: <input v-model="firstName"></p>
<p>Last Name: <input v-model="lastName"></p>
<p>Full name: {{getFullName}}</p>
<p>{{todos.length}} Todos:</p>
<ul>
<li v-for="todo in todos">
{{todo.text}}: {{todo.body}}
</li>
</ul>
<p><input type="checkbox" v-model="wantInfo"> I want info</p>
<p v-if="wantInfo">What kind of info do you want? <input type="text" v-model="kindOfInfo"/></p>
<p v-if="kindOfInfo.length > 0">We will send you info on: {{kindOfInfo}}</p>
</div>
<script>
new Vue({
el: '#vuejs_app_test',
data: {
message: 'this is a test2',
htmlMessage: 'this is <b>some html</b>',
status: 'on',
finished: true,
infoUrl: 'http://google.com',
firstName: 'Jim',
lastName: 'Thompson',
todos: [
{text:"The First Todo", body: "This is the first todo."},
{text:"The Second Todo", body: "This is the second todo."},
{text:"The Third Todo", body: "This is the third todo."}
],
wantInfo: false,
kindOfInfo: ''
},
created: function() {
console.log('creating');
},
filters: {
addBrackets: function (text) {
return '[' + text + ']';
}
},
computed: {
getStatusMessage: function() {
return this.status == 'on' ? 'status is on' : 'status is off';
},
getFullName: function() {
return this.firstName + ' ' + this.lastName;
}
}
});
</script>
</body>
</html>
This example shows how to have an ActionManager class which handles all the actions on a page, some of the functions being exposed globally and others (which begin with an underscore) not.
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<script src="js/jquery-2.2.4.min.js"></script>
<script>
var ActionManager = (function() {
'use strict';
function checkAllOptions(elemButton) {
var $button = $(elemButton);
var $manageArea = $button.closest('.manageArea');
var $checkboxes = $manageArea.find(':checkbox');
_setCheckBoxes($checkboxes, true);
};
function uncheckAllOptions(elemButton) {
var $button = $(elemButton);
var $manageArea = $button.closest('.manageArea');
var $checkboxes = $manageArea.find(':checkbox');
_setCheckBoxes($checkboxes, false);
};
function _setCheckBoxes($checkboxes, value) {
$checkboxes.prop('checked',value);
}
return {
checkAllOptions: checkAllOptions,
uncheckAllOptions: uncheckAllOptions
};
})();
</script>
<style type="text/css">
.manageArea {
border: 1px solid #ccc;
padding: 10px;
width: 300px;
border-radius: 5px;
margin: 0 0 10px 0;
}
</style>
</head>
<body>
<div class="manageArea">
<div><input id="area1_option_1" type="checkbox"/><label for="area1_option_1">Choice 1</label></div>
<div><input id="area1_option_2" type="checkbox"/><label for="area1_option_2">Choice 2</label></div>
<div><input id="area1_option_3" type="checkbox"/><label for="area1_option_3">Choice 3</label></div>
<button onclick="ActionManager.checkAllOptions(this)">Check All Options</button>
<button onclick="ActionManager.uncheckAllOptions(this)">Uncheck All Options</button>
</div>
<div class="manageArea">
<div><input id="area2_option_1" type="checkbox"/><label for="area2_option_1">Choice 1</label></div>
<div><input id="area2_option_2" type="checkbox"/><label for="area2_option_2">Choice 2</label></div>
<div><input id="area2_option_3" type="checkbox"/><label for="area2_option_3">Choice 3</label></div>
<button onclick="ActionManager.checkAllOptions(this)">Check All Options</button>
<button onclick="ActionManager.uncheckAllOptions(this)">Uncheck All Options</button>
</div>
</body>
</html>