EWD 3 Training Course Part 30: Modularising QEWD Applications

R
Rob TweedIT Consultant, Developer & Director/Founder at M/Gateway Developments Ltd
Copyright © 2016 M/Gateway Developments Ltd
EWD 3 Training Course
Part 30
Modularising
QEWD Applications
Rob Tweed
Director, M/Gateway Developments Ltd
Twitter: @rtweed
Copyright © 2016 M/Gateway Developments Ltd
Back-end Modules
• So far we've just saved our module files
directly as .js files into the node_modules
folder
– C:qewdnode_modules
– ~/qewd/node_module
• We used the convention:
– Application name = module file name
• So the back-end module for our demo1 application
was named demo1.js
Copyright © 2016 M/Gateway Developments Ltd
Fully-fledged Node.js Modules
• However, back-end QEWD modules can
be fully-fledged Node.js modules
– ie a complete module package directory,
including:
• index.js
• package.json
• README.md
• etc
– Can be published on NPM
– Can be installed from NPM
Copyright © 2016 M/Gateway Developments Ltd
Example
• Our demo1 application back-end module
• Instead of it just being in
C:ewd3node_modulesdemo1.js
• Have a directory C:ewd3node_modulesdemo1
– package.json
– index.js
– /lib folder
– Optional READme.md documentation file
Copyright © 2016 M/Gateway Developments Ltd
package.json
• As a minimum, specify these 3 properties:
{
"name": "demo1",
"version": "1.0.0",
"main": "index.js",
}
Copyright © 2016 M/Gateway Developments Ltd
package.json
• More typically and usefully:
{
"name": "demo1",
"version": "1.0.0",
"description": "Back end message handlers for demo app",
"main": "index.js",
"author": "Rob Tweed",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/robtweed/demo1.git "
}
}
Copyright © 2016 M/Gateway Developments Ltd
package.json
{
"name": "demo1",
"version": "1.0.0",
"description": "Back end message handlers for demo app",
"main": "index.js",
"author": "Rob Tweed",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/robtweed/demo1.git "
}
}
Tells Node.js
where to find
the main code/
script file
Copyright © 2016 M/Gateway Developments Ltd
index.js
• The main script file
• Can contain the code
• Usually just a pointer to the code
– Which is usually in a separate folder
• eg /lib
Copyright © 2016 M/Gateway Developments Ltd
index.js
'use strict';
module.exports = require('./lib/demo1');
So the main code is actually
in /lib/demo1.js
Copyright © 2016 M/Gateway Developments Ltd
/lib/demo1.js
module.exports = {
init: function() {
servicesAllowed: {
testService: true
},
handlers: {
login: function(messageObj, session, send, finished) {
// etc
},
// etc
}
};
So this is a standard QEWD back-end module
Copyright © 2016 M/Gateway Developments Ltd
Now a full Node.js/NPM module
• This demo1 module could now be
published to NPM if appropriate
• Others could then use it by installing it
– npm install demo1
Copyright © 2016 M/Gateway Developments Ltd
Module Name Mapping
• QEWD default back-end module name
mapping:
– Registered application name (eg demo1)
must be used as the name of either:
• The module file, eg:
– ~/qewd/node_modules/demo1.js
• The module directory, eg:
– ~/qewd/node_modules/demo1
» Within which is the package.json, etc
Copyright © 2016 M/Gateway Developments Ltd
Module Name Mapping
• However, by using the moduleMap QEWD
startup configuration array property, you
can specify any Node.js module to be the
back-end for a particular QEWD
application
– They don't have to have the same name
Copyright © 2016 M/Gateway Developments Ltd
Module Name Mapping
• Remember: the QEWD application name
is defined in its front-end app.js file when
you invoke the ewd-client start() function,
eg:
– EWD.start('test-app', $, io);
– This application will be registered on QEWD
as test-app
Copyright © 2016 M/Gateway Developments Ltd
Module Name Mapping
• But your QEWD startup file can define
back-end module mapping, eg:
– ~/qewd/qewd.js:
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
},
moduleMap: {
test-app: '/path/to/myModule'
}
};
var qewd = require('qewd').master;
qewd.start(config);
Copyright © 2016 M/Gateway Developments Ltd
Module Name Mapping
• But your QEWD startup file can define
back-end module mapping, eg:
– ~/qewd/qewd.js:
var config = {
managementPassword: 'keepThisSecret!',
serverName: 'My QEWD Server',
port: 8080,
poolSize: 2,
database: {
type: 'gtm'
},
moduleMap: {
test-app: '/path/to/myModule'
}
};
var qewd = require('qewd').master;
qewd.start(config);
Your QEWD application
named test-app will use
/path/to/myModule
as its back-end module
Copyright © 2016 M/Gateway Developments Ltd
Modularising QEWD Back-end Logic
• It's all too easy for the back-end module of
a QEWD application to become a huge file
– Ever increasing number of message handler
functions
Copyright © 2016 M/Gateway Developments Ltd
Modularising QEWD Back-end Logic
• It's all too easy for the back-end module of
a QEWD application to become a huge file
– Ever increasing number of message handler
functions
• Each handler function can, itself, be
separated out into a module
Copyright © 2016 M/Gateway Developments Ltd
Modularising QEWD Back-end Logic
• So instead of:
module.exports = {
handlers: {
test: function(messageObj, session, send, finished) {
var incomingText = messageObj.params.text;
finished({text: 'You sent: ' + incomingText});
}
}
};
Copyright © 2016 M/Gateway Developments Ltd
Modularising QEWD Back-end Logic
• You could define each handler function in
its own sub-module:
var test = require('./handlers/test');
module.exports = {
handlers: {
test: test
}
};
function test(messageObj, session, send, finished) {
var incomingText = messageObj.params.text;
finished({text: 'You sent: ' + incomingText});
}
module.exports = test;
Copyright © 2016 M/Gateway Developments Ltd
Modularising QEWD by using Services
• Further opportunities for modularisation
are provided by QEWD Services
– A Service Module is exactly like an
application's own back-end module
• Can contain one or more handler functions
– But Service Modules can be shared between
applications
– Can be used as the basis for a QEWD-based
Micro-Service architecture
Copyright © 2016 M/Gateway Developments Ltd
Modularising QEWD by using Services
• QEWD Service Modules are good
candidates for packaging as fully-fledged
Node.js modules that can be published on
NPM
– That allows them to be re-used by other users
for their applications
Copyright © 2016 M/Gateway Developments Ltd
Modularising QEWD by using Services
• QEWD Service Modules can also be
name-mapped, separating:
– The name you use to refer to the service
– The actual physical Node.js module
name/path used for the service
• For more details about EWD Services, see
Part 16 of this course
Copyright © 2016 M/Gateway Developments Ltd
Modularising QEWD Applications
• So the back-end logic of QEWD
applications can be modularised
• How about the front-end?
Copyright © 2016 M/Gateway Developments Ltd
Modularising Front-end Code
• Two options available
– Using "Fragments"
– Using a bundler such as Browserify or
WebPack
Copyright © 2016 M/Gateway Developments Ltd
Fragments
• Becoming a bit of an old-fashioned
approach
– Not appropriate for modern frameworks such
as React.js or Angular
• But for more conventional HTML
development, it can be an effective means
of modularising an application
– Can be part of a Service
Copyright © 2016 M/Gateway Developments Ltd
Fragments
• The concept:
– You load an initial HTML file (eg index.html)
which typically contains very little markup, but
loads all the JavaScript and CSS required by
the application
– Subsequently, fragments of HTML are loaded
dynamically into <div> or other tags, as a
result of events occurring in the browser
• Via Ajax or WebSockets
Copyright © 2016 M/Gateway Developments Ltd
Fragments
• Fragments can be included as part of a
QEWD Service Module
– Back-end message handler logic
– Associated fragment(s) of markup
– Publishable on NPM
• An application could therefore use
fragments and back-end logic from
multiple services
– ie: re-usable logic & markup / UI components
Copyright © 2016 M/Gateway Developments Ltd
EWD.getFragment()
• To fetch a fragment, in your browser-side
logic use:
– EWD.getFragment(argObj, callback);
Copyright © 2016 M/Gateway Developments Ltd
EWD.getFragment()
• To fetch a fragment, in your browser-side
logic use:
– EWD.getFragment(argObj, callback);
• argObj: object containing:
– name, eg: loginForm.html
» By default, will fetch from same directory as
index.html file
– targetId:
» id of the tag into which the markup will be inserted
» eg <div id="myTargetDiv"></div>
Copyright © 2016 M/Gateway Developments Ltd
EWD.getFragment()
• To fetch a fragment, in your browser-side
logic use:
– EWD.getFragment(argObj, callback);
• callback: invoked when fragment is loaded into
browser
– Single optional argument: name
» Filename of the fragment that was loaded
– Use callback to load handlers needed by fragment's
markup, eg button click handlers
Copyright © 2016 M/Gateway Developments Ltd
Example: index.html
<html>
<head>
<title>Fragment Demo</title>
<link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" />
</head>
<body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/ewd-client.js"></script>
<script src="app2.js"></script>
<div id="loginFormDiv"></div>
</body>
</html>
Copyright © 2016 M/Gateway Developments Ltd
Example: app2.js
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv'
};
EWD.getFragment(params, function(name) {
toastr.info('fragment loaded: ' + JSON.stringify(name));
});
});
EWD.start('demo1', $, io);
});
Copyright © 2016 M/Gateway Developments Ltd
Example
<html>
<head>
<title>Demo ewd-xpress application</title>
<link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" />
</head>
<body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/ewd-client.js"></script>
<script src="app2.js"></script>
<div id="loginFormDiv"></div>
</body>
</html>
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv'
};
EWD.getFragment(params, function(name) {
toastr.info('fragment loaded: ' + JSON.stringify(name));
});
Copyright © 2016 M/Gateway Developments Ltd
Example: loginForm.html
<table id="loginForm">
<tr>
<td>Username:</td>
<td><input type="text" id="username" /></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" id="password" /></td>
</tr>
<tr>
<td colspan="2">
<button id="loginBtn">Login</button>
</td>
</tr>
</table>
Just the markup
for the login form
Copyright © 2016 M/Gateway Developments Ltd
Try it out!
Copyright © 2016 M/Gateway Developments Ltd
Form won't do anything
eg nothing happens if you click this button
Copyright © 2016 M/Gateway Developments Ltd
Edit: app2.js
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv'
};
EWD.getFragment(params, function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val();
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();}
});
});
});
Copyright © 2016 M/Gateway Developments Ltd
Edit: app2.js
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv'
};
EWD.getFragment(params, function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val();
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();}
});
});
});
Now it will add a click
handler to the login button
Copyright © 2016 M/Gateway Developments Ltd
Edit: app2.js
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv'
};
EWD.getFragment(params, function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val();
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();}
});
});
});
Now it will add a click
handler to the login button
When clicked, the username
and password will be sent
as a message, with type
'login', to the
QEWD back-end
Copyright © 2016 M/Gateway Developments Ltd
Now it works
Copyright © 2016 M/Gateway Developments Ltd
Now it works
• But we've had to define the fragment
loader logic within our application's main
app.js logic
• It would be more modular if the logic
associated with the loginForm fragment
could be kept in a separate place
– Separately maintainable
– Re-usable
Copyright © 2016 M/Gateway Developments Ltd
Create loginForm.js
• In same directory as app.js
– Or in its own subdirectory under ~/qewd/www
Copyright © 2016 M/Gateway Developments Ltd
loginForm.js
var loginForm = {
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
Copyright © 2016 M/Gateway Developments Ltd
Example: index.html
<html>
<head>
<title>Fragment Demo</title>
<link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" />
</head>
<body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/ewd-client.js"></script>
<script src="app2.js"></script>
<script src="loginForm.js"></script>
<div id="loginFormDiv"></div>
</body>
</html>
Copyright © 2016 M/Gateway Developments Ltd
Example: app2.js
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv'
};
EWD.getFragment(params, loginForm.loader);
});
EWD.start('demo1', $, io);
});
Copyright © 2016 M/Gateway Developments Ltd
Example: app2.js
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv'
};
EWD.getFragment(params, loginForm.loader);
});
EWD.start('demo1', $, io);
});
var loginForm = {
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
loginForm.js
Copyright © 2016 M/Gateway Developments Ltd
What about the back-end code?
Copyright © 2016 M/Gateway Developments Ltd
What about the back-end code?
var loginForm = {
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
A login message will be sent to
the back-end. Currently QEWD
will expect to find its handler in
the main application module (demo1.js)
So let's modularise it instead…
Copyright © 2016 M/Gateway Developments Ltd
Create a Login Service
function checkLogin(username, password) {
// hard-coded version for now
if (username !== 'rob') return {error: 'Invalid username'};
if (password !== 'secret') return {error: 'Invalid password'};
return {ok: true};
}
module.exports = {
handlers: {
login: function(messageObj, session, send, finished) {
if (session.authenticated) {
finished({error: 'You are already logged in!'});
return;
}
var username = messageObj.params.username;
if (username === '') {
finished({error: 'You must enter a username'});
return;
}
var password = messageObj.params.password;
if (password === '') {
finished({error: 'You must enter a password'});
return;
}
var status = checkLogin(username, password);
if (status.ok) {
session.authenticated = true;
session.timeout = 3600;
session.updateExpiry();
finished({ok: true});
}
else {
finished({error: status.error});
}
}
}
};
Save into
node_modules/Login.js
Contains the logic for
handling the login
authentication at the
back-end
Copyright © 2016 M/Gateway Developments Ltd
Edit loginForm.js
var loginForm = {
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
service: 'Login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
Use the Login service
for the login message
Copyright © 2016 M/Gateway Developments Ltd
Edit demo1.js
module.exports = {
servicesAllowed: {
Login: true
}
};
It now just needs to be
a minimal module that
allows the demo1 app
to use the Login service
Copyright © 2016 M/Gateway Developments Ltd
Try it out!
• Restart QEWD to ensure that the new
back-end modules are loaded
– Alternatively use qewd-monitor to stop the
worker processes
• The demo application should run
identically, but this time it's using the Login
service
Copyright © 2016 M/Gateway Developments Ltd
Further Front-end Modularisation?
• loginForm.html fragment is currently in
www/demo1 directory
• Could we make the fragment part of the
Login service?
Copyright © 2016 M/Gateway Developments Ltd
Create Login Module
• Create a new folder:
– C:qewdnode_modulesLogin or
– ~/qewd/node_modules/Login
Copyright © 2016 M/Gateway Developments Ltd
Create Login Module
• ~/qewd/node_modules/Login
package.json:
{
"name": "Login",
"version": "1.0.0",
"description": "Modular login system",
"main": "index.js"
}
index.js
'use strict';
module.exports = require('./lib/Login');
- index.js
- package.json
Copyright © 2016 M/Gateway Developments Ltd
Create Login Module
• Move ~/qewd/node_modules/Login.js to:
– ~/qewd/node_modules/Login/lib/Login.js
- lib
- Login.js
- index.js
- package.json
Copyright © 2016 M/Gateway Developments Ltd
Create Login Module
• Move:
– ~/qewd/www/demo1/loginForm.html
• To:
– ~/qewd/node_modules/Login/fragments/loginForm.html
- fragments
- loginForm.html
- lib
- Login.js
- index.js
- package.json
Copyright © 2016 M/Gateway Developments Ltd
Example: app2.js
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv',
service: 'Login'
};
EWD.getFragment(params, loginForm.loader);
});
EWD.start('demo1', $, io);
});
Load loginForm.html from
the Login Service module
ewd-xpress will look in
the Login/fragments
folder for the fragment
Copyright © 2016 M/Gateway Developments Ltd
Example: app2.js
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv',
service: 'Login'
};
EWD.getFragment(params, loginForm.loader);
});
EWD.start('demo1', $, io);
});
Load loginForm.html from
the Login Service module
ewd-xpress will look in
the Login/fragments
folder for the fragment
- fragments
- loginForm.html
- lib
- Login.js
- index.js
- package.json
Copyright © 2016 M/Gateway Developments Ltd
Client-side: loginForm.js
var loginForm = {
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
service: 'Login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
Use the Login service
for the login message
Copyright © 2016 M/Gateway Developments Ltd
Include loginForm.js in Service?
• Since loginForm.js defines the handlers for
the loginForm.html fragment, it would be
sensible for it to be part of the Login
Service too
• However, web browsers can't load
JavaScript from Node.js modules
– Not directly
• Two approaches…
Copyright © 2016 M/Gateway Developments Ltd
Add to Login Module
• Copy:
– ~/qewd/www/demo1/loginForm.js
• To:
– ~/qewd/node_modules/Login/client/loginForm.js
- client
- loginForm.js
- fragments
- loginForm.html
- lib
- Login.js
- index.js
- package.json
Copyright © 2016 M/Gateway Developments Ltd
Option 1
• Can be published and installed/used by others, but must:
• Copy:
– ~/qewd/node_modules/Login/client/loginForm.js
• To:
– ~/qewd/www/{{application name}}/loginForm.js
- client
- loginForm.js
- fragments
- loginForm.html
- lib
- Login.js
- index.js
- package.json
Copyright © 2016 M/Gateway Developments Ltd
Option 2: the modern approach
• Can be published and installed/used by others
• Make use of a bundler (eg Browserify or WebPack)
– Define the front-end JavaScript as if you were using Node.js
• So you can use: var loginForm = require('Login/client/loginForm');
– Use the bundler to convert to a single JavaScript file that can be loaded by the
browser using a <script> tag
– However, loginForm.js would have to be rewritten as a Module
• See later…
- client
- loginForm.js
- fragments
- loginForm.html
- lib
- Login.js
- index.js
- package.json
Copyright © 2016 M/Gateway Developments Ltd
Modular Front-end Development
• Install Browserify
– cd ~/qewd (or cd C:qewd )
– npm install babelify
– npm install –g browserify
Copyright © 2016 M/Gateway Developments Ltd
NPM versions of client-side JS
• All the libraries loaded using <script> tags
need to be accessed as Node.js modules
instead
– Most are available in this format these days
cd ~/qewd
npm install toastr jquery socket.io-client
// ewd-client is already an installed as a module
Copyright © 2016 M/Gateway Developments Ltd
Edit loginForm.js
• The loginForm.js client-side script file in our Login
module needs to be changed:
– Needs to be a Node.js module
• Very simple change…
- client
- loginForm.js
- fragments
- loginForm.html
- lib
- Login.js
- index.js
- package.json
Copyright © 2016 M/Gateway Developments Ltd
Edit loginForm.js
var toastr = require('toastr');
var EWD;
module.exports = {
init: function(ewd) {
EWD = ewd;
},
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error('You must enter a password');
return;
}
var message = {
type: 'login',
service: 'Login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
Copyright © 2016 M/Gateway Developments Ltd
Edit loginForm.js
var toastr = require('toastr');
var EWD;
module.exports = {
init: function(ewd) {
EWD = ewd;
},
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error ('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error ('You must enter a password');
return;
}
var message = {
type: 'login',
service: 'Login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
Node.js modules
must be entirely
self-contained.
toastr and EWD
have to come from
somewhere
Copyright © 2016 M/Gateway Developments Ltd
Edit loginForm.js
var toastr = require('toastr');
var EWD;
module.exports = {
init: function(ewd) {
EWD = ewd;
},
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error ('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error ('You must enter a password');
return;
}
var message = {
type: 'login',
service: 'Login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
In the case of
toastr, we can
require() it
Copyright © 2016 M/Gateway Developments Ltd
Edit loginForm.js
var toastr = require('toastr');
var EWD;
module.exports = {
init: function(ewd) {
EWD = ewd;
},
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error ('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error ('You must enter a password');
return;
}
var message = {
type: 'login',
service: 'Login',
params: {
username: username,
password: password
}
};
EWD.send (message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
But the EWD object is
more tricky
loginForm.loader must
use the post-register
version of EWD so it
can access the send()
API and the session token.
So we can't just use
require('ewd-client') as
we'd have the pre-registered
version!
Copyright © 2016 M/Gateway Developments Ltd
Edit loginForm.js
var toastr = require('toastr');
var EWD;
module.exports = {
init: function(ewd) {
EWD = ewd;
},
loader: function(name) {
$('#loginBtn').on('click', function(e) {
var username = $('#username').val();
if (username === '') {
toastr.error ('You must enter a username');
return;
}
var password = $('#password').val()
if (password === '') {
toastr.error ('You must enter a password');
return;
}
var message = {
type: 'login',
service: 'Login',
params: {
username: username,
password: password
}
};
EWD.send(message, function(responseObj) {
if (!responseObj.message.error) {
$('#loginForm').hide();
}
});
});
}
};
One approach is to use
an init() function that will
allow us to pass the
post-register version of EWD
into the module
We'll see how it's used
in app.js
Copyright © 2016 M/Gateway Developments Ltd
Edit app.jsvar io = require('socket.io-client')
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var toastr = require('toastr');
var EWD = require('ewd-client').EWD;
var Login = require('Login/client/loginForm');
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
Login.init(EWD);
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv',
service: 'Login'
};
EWD.getFragment(params, Login.loader);
});
EWD.start('demo1', $, io);
});
app.js now needs to
load everything it needs
by using require()
Copyright © 2016 M/Gateway Developments Ltd
Edit app.jsvar io = require('socket.io-client')
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var toastr = require('toastr');
var EWD = require('ewd-client').EWD;
var Login = require('Login/client/loginForm');
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
Login.init(EWD);
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv',
service: 'Login'
};
EWD.getFragment(params, Login.loader);
});
EWD.start('demo1', $, io);
});
The jQuery $ object
needs to be instantiated
as a property of window
This gives it Global scope
so none of the other
modules that refer to $
need to require() it again
eg $ is referred to in
loginForm.js
Copyright © 2016 M/Gateway Developments Ltd
Edit app.jsvar io = require('socket.io-client')
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var toastr = require('toastr');
var EWD = require('ewd-client').EWD;
var Login = require('Login/client/loginForm');
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
Login.init(EWD);
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv',
service: 'Login'
};
EWD.getFragment(params, Login.loader);
});
EWD.start('demo1', $, io);
});
The EWD object is loaded
from the ewd-client
module
Copyright © 2016 M/Gateway Developments Ltd
Edit app.jsvar io = require('socket.io-client')
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var toastr = require('toastr');
var EWD = require('ewd-client').EWD;
var Login = require('Login/client/loginForm');
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
Login.init(EWD);
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv',
service: 'Login'
};
EWD.getFragment(params, Login.loader);
});
EWD.start('demo1', $, io);
});
And now we load the
loginForm code from
the Login service module
Copyright © 2016 M/Gateway Developments Ltd
Edit app.jsvar io = require('socket.io-client')
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var toastr = require('toastr');
var EWD = require('ewd-client').EWD;
var Login = require('Login/client/loginForm');
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
Login.init(EWD);
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv',
service: 'Login'
};
EWD.getFragment(params, Login.loader);
});
EWD.start('demo1', $, io);
});
And now we load the
loginForm code from
the Login service module
And pass the post-register
EWD object into it
Copyright © 2016 M/Gateway Developments Ltd
Edit app.jsvar io = require('socket.io-client')
var jQuery = require('jquery');
window.$ = window.jQuery = jQuery;
var toastr = require('toastr');
var EWD = require('ewd-client').EWD;
var Login = require('Login/client/loginForm');
$(document).ready(function() {
EWD.log = true;
EWD.on('ewd-registered', function() {
EWD.on('error', function(responseObj) {
toastr.error(responseObj.message.error);
});
EWD.on('socketDisconnected', function() {
toastr.info('You have been logged out');
setTimeout(function() {
location.reload();
}, 1000);
});
Login.init(EWD);
var params = {
name: 'loginForm.html',
targetId: 'loginFormDiv',
service: 'Login'
};
EWD.getFragment(params, Login.loader );
});
EWD.start('demo1', $, io);
});
And now we load the
loginForm code from
the Login service module
And pass the post-register
EWD object into it
And then its loader function
can be used as the
getFragment callback,
and it now has
access to EWD to send
messages and handle
responses
Copyright © 2016 M/Gateway Developments Ltd
Edit index.html
• Leave the <link> tags that load the CSS
• Remove all the <script> tags but one:
– bundle.js
– Created from the modularised JavaScript by
Browserify
Copyright © 2016 M/Gateway Developments Ltd
Edit index.html
<html>
<head>
<title>Demo modularised ewd-xpress application</title>
<link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" />
</head>
<body>
<script src="bundle.js"></script>
<div id="loginFormDiv"></div>
</body>
</html>
Copyright © 2016 M/Gateway Developments Ltd
Now create the bundle file
• cd ~/qewd/www/demo1
• browserify -t [ babelify ] app.js -o bundle.js
• If no errors reported, you should now have
a bundle.js file in the demo1 directory
Copyright © 2016 M/Gateway Developments Ltd
Try it out
• The application should run as before
• Check in the JavaScript console that it's using
the bundle.js file
• You now have a fully modularised application
– Everything related to logging in is defined in its own
Login service module
• Both its front-end and back-end logic
• Could be re-used in any of your applications
Copyright © 2016 M/Gateway Developments Ltd
Bundling: things to note
• If you make any changes to front-end
JavaScript, in the application's own code
or any of the modules it uses, you MUST
re-run Browserify
• WebPack is an alternative to Browserify
– You may prefer it
• You may want to also minimise the
bundle.js file
– eg minify
Copyright © 2016 M/Gateway Developments Ltd
Automating
• Outside the scope of this training, but the
trend is to now automate the build chain
– When a file is changed, it triggers a re-build
• Bundles the file
• Minifies it
• Runs unit tests
• Updates the Git repository
– eg using tools such as Gulp
1 of 85

Recommended

EWD 3 Training Course Part 4: Installing & Configuring QEWD by
EWD 3 Training Course Part 4: Installing & Configuring QEWDEWD 3 Training Course Part 4: Installing & Configuring QEWD
EWD 3 Training Course Part 4: Installing & Configuring QEWDRob Tweed
2.9K views31 slides
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres... by
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...
EWD 3 Training Course Part 37: Building a React.js application with ewd-xpres...Rob Tweed
1.1K views53 slides
EWD 3 Training Course Part 34: QEWD Resilient Mode by
EWD 3 Training Course Part 34: QEWD Resilient ModeEWD 3 Training Course Part 34: QEWD Resilient Mode
EWD 3 Training Course Part 34: QEWD Resilient ModeRob Tweed
750 views20 slides
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data... by
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...
EWD 3 Training Course Part 1: How Node.js Integrates With Global Storage Data...Rob Tweed
3.9K views32 slides
EWD 3 Training Course Part 16: QEWD Services by
EWD 3 Training Course Part 16: QEWD ServicesEWD 3 Training Course Part 16: QEWD Services
EWD 3 Training Course Part 16: QEWD ServicesRob Tweed
671 views21 slides
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.js by
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.jsEWD 3 Training Course Part 44: Creating MicroServices with QEWD.js
EWD 3 Training Course Part 44: Creating MicroServices with QEWD.jsRob Tweed
1.5K views136 slides

More Related Content

What's hot

qewd-ripple: The Ripple OSI Middle Tier by
qewd-ripple: The Ripple OSI Middle Tierqewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle TierRob Tweed
720 views52 slides
EWD 3 Training Course Part 2: EWD 3 Overview by
EWD 3 Training Course Part 2: EWD 3 OverviewEWD 3 Training Course Part 2: EWD 3 Overview
EWD 3 Training Course Part 2: EWD 3 OverviewRob Tweed
2.1K views93 slides
EWD 3 Training Course Part 19: The cache.node APIs by
EWD 3 Training Course Part 19: The cache.node APIsEWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIsRob Tweed
905 views50 slides
EWD 3 Training Course Part 31: Using QEWD for Web and REST Services by
EWD 3 Training Course Part 31: Using QEWD for Web and REST ServicesEWD 3 Training Course Part 31: Using QEWD for Web and REST Services
EWD 3 Training Course Part 31: Using QEWD for Web and REST ServicesRob Tweed
3.3K views154 slides
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application by
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationRob Tweed
999 views33 slides
EWD 3 Training Course Part 3: Summary of EWD 3 Modules by
EWD 3 Training Course Part 3: Summary of EWD 3 ModulesEWD 3 Training Course Part 3: Summary of EWD 3 Modules
EWD 3 Training Course Part 3: Summary of EWD 3 ModulesRob Tweed
979 views23 slides

What's hot(20)

qewd-ripple: The Ripple OSI Middle Tier by Rob Tweed
qewd-ripple: The Ripple OSI Middle Tierqewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tier
Rob Tweed720 views
EWD 3 Training Course Part 2: EWD 3 Overview by Rob Tweed
EWD 3 Training Course Part 2: EWD 3 OverviewEWD 3 Training Course Part 2: EWD 3 Overview
EWD 3 Training Course Part 2: EWD 3 Overview
Rob Tweed2.1K views
EWD 3 Training Course Part 19: The cache.node APIs by Rob Tweed
EWD 3 Training Course Part 19: The cache.node APIsEWD 3 Training Course Part 19: The cache.node APIs
EWD 3 Training Course Part 19: The cache.node APIs
Rob Tweed905 views
EWD 3 Training Course Part 31: Using QEWD for Web and REST Services by Rob Tweed
EWD 3 Training Course Part 31: Using QEWD for Web and REST ServicesEWD 3 Training Course Part 31: Using QEWD for Web and REST Services
EWD 3 Training Course Part 31: Using QEWD for Web and REST Services
Rob Tweed3.3K views
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application by Rob Tweed
EWD 3 Training Course Part 5b: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
Rob Tweed999 views
EWD 3 Training Course Part 3: Summary of EWD 3 Modules by Rob Tweed
EWD 3 Training Course Part 3: Summary of EWD 3 ModulesEWD 3 Training Course Part 3: Summary of EWD 3 Modules
EWD 3 Training Course Part 3: Summary of EWD 3 Modules
Rob Tweed979 views
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle by Rob Tweed
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging CycleEWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
Rob Tweed720 views
EWD 3 Training Course Part 27: The QEWD Session by Rob Tweed
EWD 3 Training Course Part 27: The QEWD SessionEWD 3 Training Course Part 27: The QEWD Session
EWD 3 Training Course Part 27: The QEWD Session
Rob Tweed920 views
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap... by Rob Tweed
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
Rob Tweed932 views
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services by Rob Tweed
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Servicesewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
Rob Tweed333 views
QEWD.js: Have your Node.js Cake and Eat It Too by Rob Tweed
QEWD.js: Have your Node.js Cake and Eat It TooQEWD.js: Have your Node.js Cake and Eat It Too
QEWD.js: Have your Node.js Cake and Eat It Too
Rob Tweed628 views
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application by Rob Tweed
EWD 3 Training Course Part 5a: First Steps in Building a QEWD ApplicationEWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
Rob Tweed1.4K views
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD by Rob Tweed
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWDEWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
Rob Tweed1.2K views
EWD 3 Training Course Part 29: Running QEWD as a Service by Rob Tweed
EWD 3 Training Course Part 29: Running QEWD as a ServiceEWD 3 Training Course Part 29: Running QEWD as a Service
EWD 3 Training Course Part 29: Running QEWD as a Service
Rob Tweed1K views
EWD 3 Training Course Part 35: QEWD Session Locking by Rob Tweed
EWD 3 Training Course Part 35: QEWD Session LockingEWD 3 Training Course Part 35: QEWD Session Locking
EWD 3 Training Course Part 35: QEWD Session Locking
Rob Tweed532 views
EWD 3 Training Course Part 42: The QEWD Docker Appliance by Rob Tweed
EWD 3 Training Course Part 42: The QEWD Docker ApplianceEWD 3 Training Course Part 42: The QEWD Docker Appliance
EWD 3 Training Course Part 42: The QEWD Docker Appliance
Rob Tweed1.3K views
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD by Rob Tweed
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWDEWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
EWD 3 Training Course Part 15: Using a Framework other than jQuery with QEWD
Rob Tweed541 views
EWD 3 Training Course Part 11: Handling Errors in QEWD by Rob Tweed
EWD 3 Training Course Part 11: Handling Errors in QEWDEWD 3 Training Course Part 11: Handling Errors in QEWD
EWD 3 Training Course Part 11: Handling Errors in QEWD
Rob Tweed639 views
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services by Rob Tweed
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST ServicesEWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services
Rob Tweed891 views
EWD 3 Training Course Part 12: QEWD Session Timeout Control by Rob Tweed
EWD 3 Training Course Part 12: QEWD Session Timeout ControlEWD 3 Training Course Part 12: QEWD Session Timeout Control
EWD 3 Training Course Part 12: QEWD Session Timeout Control
Rob Tweed561 views

Viewers also liked

EWD 3 Training Course Part 26: Event-driven Indexing by
EWD 3 Training Course Part 26: Event-driven IndexingEWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven IndexingRob Tweed
678 views14 slides
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes by
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesEWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesRob Tweed
622 views19 slides
EWD 3 Training Course Part 25: Document Database Capabilities by
EWD 3 Training Course Part 25: Document Database CapabilitiesEWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database CapabilitiesRob Tweed
905 views49 slides
EWD 3 Training Course Part 21: Persistent JavaScript Objects by
EWD 3 Training Course Part 21: Persistent JavaScript ObjectsEWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript ObjectsRob Tweed
779 views31 slides
EWD 3 Training Course Part 20: The DocumentNode Object by
EWD 3 Training Course Part 20: The DocumentNode ObjectEWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode ObjectRob Tweed
1K views41 slides
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3 by
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3Rob Tweed
627 views19 slides

Viewers also liked(15)

EWD 3 Training Course Part 26: Event-driven Indexing by Rob Tweed
EWD 3 Training Course Part 26: Event-driven IndexingEWD 3 Training Course Part 26: Event-driven Indexing
EWD 3 Training Course Part 26: Event-driven Indexing
Rob Tweed678 views
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes by Rob Tweed
EWD 3 Training Course Part 24: Traversing a Document's Leaf NodesEWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
Rob Tweed622 views
EWD 3 Training Course Part 25: Document Database Capabilities by Rob Tweed
EWD 3 Training Course Part 25: Document Database CapabilitiesEWD 3 Training Course Part 25: Document Database Capabilities
EWD 3 Training Course Part 25: Document Database Capabilities
Rob Tweed905 views
EWD 3 Training Course Part 21: Persistent JavaScript Objects by Rob Tweed
EWD 3 Training Course Part 21: Persistent JavaScript ObjectsEWD 3 Training Course Part 21: Persistent JavaScript Objects
EWD 3 Training Course Part 21: Persistent JavaScript Objects
Rob Tweed779 views
EWD 3 Training Course Part 20: The DocumentNode Object by Rob Tweed
EWD 3 Training Course Part 20: The DocumentNode ObjectEWD 3 Training Course Part 20: The DocumentNode Object
EWD 3 Training Course Part 20: The DocumentNode Object
Rob Tweed1K views
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3 by Rob Tweed
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
EWD 3 Training Course Part 39: Building a React.js application with QEWD, Part 3
Rob Tweed627 views
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4 by Rob Tweed
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 4
Rob Tweed718 views
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects by Rob Tweed
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode ObjectsEWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
Rob Tweed732 views
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2 by Rob Tweed
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
EWD 3 Training Course Part 38: Building a React.js application with QEWD, Part 2
Rob Tweed597 views
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5 by Rob Tweed
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
EWD 3 Training Course Part 41: Building a React.js application with QEWD, Part 5
Rob Tweed621 views
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects by Rob Tweed
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode ObjectsEWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
Rob Tweed797 views
Mumps the Internet scale database by george.james
Mumps the Internet scale databaseMumps the Internet scale database
Mumps the Internet scale database
george.james14.2K views
EWD 3 Training Course Part 13: Putting Everything so far into Practice using ... by Rob Tweed
EWD 3 Training Course Part 13: Putting Everything so far into Practice using ...EWD 3 Training Course Part 13: Putting Everything so far into Practice using ...
EWD 3 Training Course Part 13: Putting Everything so far into Practice using ...
Rob Tweed805 views
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage by Rob Tweed
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global StorageEWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
Rob Tweed1.2K views
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS by Rob Tweed
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPSEWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
Rob Tweed608 views

Similar to EWD 3 Training Course Part 30: Modularising QEWD Applications

Advanced Node.JS Meetup by
Advanced Node.JS MeetupAdvanced Node.JS Meetup
Advanced Node.JS MeetupLINAGORA
2K views59 slides
Clocker - How to Train your Docker Cloud by
Clocker - How to Train your Docker CloudClocker - How to Train your Docker Cloud
Clocker - How to Train your Docker CloudAndrew Kennedy
693 views66 slides
CD in kubernetes using helm and ksonnet. Stas Kolenkin by
CD in kubernetes using helm and ksonnet. Stas KolenkinCD in kubernetes using helm and ksonnet. Stas Kolenkin
CD in kubernetes using helm and ksonnet. Stas KolenkinDataArt
568 views64 slides
Chris OBrien - Pitfalls when developing with the SharePoint Framework (SPFx) by
Chris OBrien - Pitfalls when developing with the SharePoint Framework (SPFx)Chris OBrien - Pitfalls when developing with the SharePoint Framework (SPFx)
Chris OBrien - Pitfalls when developing with the SharePoint Framework (SPFx)Chris O'Brien
9.8K views47 slides
Improving build solutions dependency management with webpack by
Improving build solutions  dependency management with webpackImproving build solutions  dependency management with webpack
Improving build solutions dependency management with webpackNodeXperts
248 views45 slides
Node js meetup by
Node js meetupNode js meetup
Node js meetupAnsuman Roy
275 views37 slides

Similar to EWD 3 Training Course Part 30: Modularising QEWD Applications(20)

Advanced Node.JS Meetup by LINAGORA
Advanced Node.JS MeetupAdvanced Node.JS Meetup
Advanced Node.JS Meetup
LINAGORA2K views
Clocker - How to Train your Docker Cloud by Andrew Kennedy
Clocker - How to Train your Docker CloudClocker - How to Train your Docker Cloud
Clocker - How to Train your Docker Cloud
Andrew Kennedy693 views
CD in kubernetes using helm and ksonnet. Stas Kolenkin by DataArt
CD in kubernetes using helm and ksonnet. Stas KolenkinCD in kubernetes using helm and ksonnet. Stas Kolenkin
CD in kubernetes using helm and ksonnet. Stas Kolenkin
DataArt568 views
Chris OBrien - Pitfalls when developing with the SharePoint Framework (SPFx) by Chris O'Brien
Chris OBrien - Pitfalls when developing with the SharePoint Framework (SPFx)Chris OBrien - Pitfalls when developing with the SharePoint Framework (SPFx)
Chris OBrien - Pitfalls when developing with the SharePoint Framework (SPFx)
Chris O'Brien9.8K views
Improving build solutions dependency management with webpack by NodeXperts
Improving build solutions  dependency management with webpackImproving build solutions  dependency management with webpack
Improving build solutions dependency management with webpack
NodeXperts248 views
Multi modularized project setup with gulp, typescript and angular.js by David Amend
Multi modularized project setup with gulp, typescript and angular.jsMulti modularized project setup with gulp, typescript and angular.js
Multi modularized project setup with gulp, typescript and angular.js
David Amend10.8K views
Introduction to node js - From "hello world" to deploying on azure by Colin Mackay
Introduction to node js - From "hello world" to deploying on azureIntroduction to node js - From "hello world" to deploying on azure
Introduction to node js - From "hello world" to deploying on azure
Colin Mackay28.8K views
Tech io nodejs_20130531_v0.6 by Ganesh Kondal
Tech io nodejs_20130531_v0.6Tech io nodejs_20130531_v0.6
Tech io nodejs_20130531_v0.6
Ganesh Kondal1.4K views
Introduction of webpack 4 by Vijay Shukla
Introduction of webpack 4Introduction of webpack 4
Introduction of webpack 4
Vijay Shukla124 views
CloudStack and cloud-init by MarcusS13
CloudStack and cloud-initCloudStack and cloud-init
CloudStack and cloud-init
MarcusS13146 views
WebSphere and Docker by David Currie
WebSphere and DockerWebSphere and Docker
WebSphere and Docker
David Currie3.6K views
Meet Magento Spain 2019 - Our Experience with Magento Cloud by Lyzun Oleksandr
Meet Magento Spain 2019 - Our Experience with Magento CloudMeet Magento Spain 2019 - Our Experience with Magento Cloud
Meet Magento Spain 2019 - Our Experience with Magento Cloud
Lyzun Oleksandr664 views
Docker dev ops for cd meetup 12-14 by Simon Storm
Docker dev ops for cd meetup 12-14Docker dev ops for cd meetup 12-14
Docker dev ops for cd meetup 12-14
Simon Storm1.1K views
betterCode Workshop: Effizientes DevOps-Tooling mit Go by QAware GmbH
betterCode Workshop:  Effizientes DevOps-Tooling mit GobetterCode Workshop:  Effizientes DevOps-Tooling mit Go
betterCode Workshop: Effizientes DevOps-Tooling mit Go
QAware GmbH692 views
DevHub 3 - Composer plus Magento by Magento Dev
DevHub 3 - Composer plus MagentoDevHub 3 - Composer plus Magento
DevHub 3 - Composer plus Magento
Magento Dev1.1K views
Extending OpenShift Origin: Build Your Own Cartridge with Bill DeCoste of Red... by OpenShift Origin
Extending OpenShift Origin: Build Your Own Cartridge with Bill DeCoste of Red...Extending OpenShift Origin: Build Your Own Cartridge with Bill DeCoste of Red...
Extending OpenShift Origin: Build Your Own Cartridge with Bill DeCoste of Red...
OpenShift Origin5K views
Introduction to Node (15th May 2017) by Lucas Jellema
Introduction to Node (15th May 2017)Introduction to Node (15th May 2017)
Introduction to Node (15th May 2017)
Lucas Jellema528 views

Recently uploaded

Quality Assurance by
Quality Assurance Quality Assurance
Quality Assurance interworksoftware2
5 views6 slides
How To Make Your Plans Suck Less — Maarten Dalmijn at the 57th Hands-on Agile... by
How To Make Your Plans Suck Less — Maarten Dalmijn at the 57th Hands-on Agile...How To Make Your Plans Suck Less — Maarten Dalmijn at the 57th Hands-on Agile...
How To Make Your Plans Suck Less — Maarten Dalmijn at the 57th Hands-on Agile...Stefan Wolpers
33 views38 slides
EV Charging App Case by
EV Charging App Case EV Charging App Case
EV Charging App Case iCoderz Solutions
9 views1 slide
Introduction to Gradle by
Introduction to GradleIntroduction to Gradle
Introduction to GradleJohn Valentino
5 views7 slides
Introduction to Maven by
Introduction to MavenIntroduction to Maven
Introduction to MavenJohn Valentino
6 views10 slides
Transport Management System - Shipment & Container Tracking by
Transport Management System - Shipment & Container TrackingTransport Management System - Shipment & Container Tracking
Transport Management System - Shipment & Container TrackingFreightoscope
5 views3 slides

Recently uploaded(20)

How To Make Your Plans Suck Less — Maarten Dalmijn at the 57th Hands-on Agile... by Stefan Wolpers
How To Make Your Plans Suck Less — Maarten Dalmijn at the 57th Hands-on Agile...How To Make Your Plans Suck Less — Maarten Dalmijn at the 57th Hands-on Agile...
How To Make Your Plans Suck Less — Maarten Dalmijn at the 57th Hands-on Agile...
Stefan Wolpers33 views
Transport Management System - Shipment & Container Tracking by Freightoscope
Transport Management System - Shipment & Container TrackingTransport Management System - Shipment & Container Tracking
Transport Management System - Shipment & Container Tracking
Freightoscope 5 views
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated... by TomHalpin9
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...
Dev-HRE-Ops - Addressing the _Last Mile DevOps Challenge_ in Highly Regulated...
TomHalpin96 views
Electronic AWB - Electronic Air Waybill by Freightoscope
Electronic AWB - Electronic Air Waybill Electronic AWB - Electronic Air Waybill
Electronic AWB - Electronic Air Waybill
Freightoscope 5 views
Navigating container technology for enhanced security by Niklas Saari by Metosin Oy
Navigating container technology for enhanced security by Niklas SaariNavigating container technology for enhanced security by Niklas Saari
Navigating container technology for enhanced security by Niklas Saari
Metosin Oy14 views
Quality Engineer: A Day in the Life by John Valentino
Quality Engineer: A Day in the LifeQuality Engineer: A Day in the Life
Quality Engineer: A Day in the Life
John Valentino7 views
Airline Booking Software by SharmiMehta
Airline Booking SoftwareAirline Booking Software
Airline Booking Software
SharmiMehta9 views
tecnologia18.docx by nosi6702
tecnologia18.docxtecnologia18.docx
tecnologia18.docx
nosi67025 views
Understanding HTML terminology by artembondar5
Understanding HTML terminologyUnderstanding HTML terminology
Understanding HTML terminology
artembondar57 views
ADDO_2022_CICID_Tom_Halpin.pdf by TomHalpin9
ADDO_2022_CICID_Tom_Halpin.pdfADDO_2022_CICID_Tom_Halpin.pdf
ADDO_2022_CICID_Tom_Halpin.pdf
TomHalpin95 views
Introduction to Git Source Control by John Valentino
Introduction to Git Source ControlIntroduction to Git Source Control
Introduction to Git Source Control
John Valentino7 views
Bootstrapping vs Venture Capital.pptx by Zeljko Svedic
Bootstrapping vs Venture Capital.pptxBootstrapping vs Venture Capital.pptx
Bootstrapping vs Venture Capital.pptx
Zeljko Svedic15 views

EWD 3 Training Course Part 30: Modularising QEWD Applications

  • 1. Copyright © 2016 M/Gateway Developments Ltd EWD 3 Training Course Part 30 Modularising QEWD Applications Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed
  • 2. Copyright © 2016 M/Gateway Developments Ltd Back-end Modules • So far we've just saved our module files directly as .js files into the node_modules folder – C:qewdnode_modules – ~/qewd/node_module • We used the convention: – Application name = module file name • So the back-end module for our demo1 application was named demo1.js
  • 3. Copyright © 2016 M/Gateway Developments Ltd Fully-fledged Node.js Modules • However, back-end QEWD modules can be fully-fledged Node.js modules – ie a complete module package directory, including: • index.js • package.json • README.md • etc – Can be published on NPM – Can be installed from NPM
  • 4. Copyright © 2016 M/Gateway Developments Ltd Example • Our demo1 application back-end module • Instead of it just being in C:ewd3node_modulesdemo1.js • Have a directory C:ewd3node_modulesdemo1 – package.json – index.js – /lib folder – Optional READme.md documentation file
  • 5. Copyright © 2016 M/Gateway Developments Ltd package.json • As a minimum, specify these 3 properties: { "name": "demo1", "version": "1.0.0", "main": "index.js", }
  • 6. Copyright © 2016 M/Gateway Developments Ltd package.json • More typically and usefully: { "name": "demo1", "version": "1.0.0", "description": "Back end message handlers for demo app", "main": "index.js", "author": "Rob Tweed", "license": "Apache-2.0", "repository": { "type": "git", "url": "git+https://github.com/robtweed/demo1.git " } }
  • 7. Copyright © 2016 M/Gateway Developments Ltd package.json { "name": "demo1", "version": "1.0.0", "description": "Back end message handlers for demo app", "main": "index.js", "author": "Rob Tweed", "license": "Apache-2.0", "repository": { "type": "git", "url": "git+https://github.com/robtweed/demo1.git " } } Tells Node.js where to find the main code/ script file
  • 8. Copyright © 2016 M/Gateway Developments Ltd index.js • The main script file • Can contain the code • Usually just a pointer to the code – Which is usually in a separate folder • eg /lib
  • 9. Copyright © 2016 M/Gateway Developments Ltd index.js 'use strict'; module.exports = require('./lib/demo1'); So the main code is actually in /lib/demo1.js
  • 10. Copyright © 2016 M/Gateway Developments Ltd /lib/demo1.js module.exports = { init: function() { servicesAllowed: { testService: true }, handlers: { login: function(messageObj, session, send, finished) { // etc }, // etc } }; So this is a standard QEWD back-end module
  • 11. Copyright © 2016 M/Gateway Developments Ltd Now a full Node.js/NPM module • This demo1 module could now be published to NPM if appropriate • Others could then use it by installing it – npm install demo1
  • 12. Copyright © 2016 M/Gateway Developments Ltd Module Name Mapping • QEWD default back-end module name mapping: – Registered application name (eg demo1) must be used as the name of either: • The module file, eg: – ~/qewd/node_modules/demo1.js • The module directory, eg: – ~/qewd/node_modules/demo1 » Within which is the package.json, etc
  • 13. Copyright © 2016 M/Gateway Developments Ltd Module Name Mapping • However, by using the moduleMap QEWD startup configuration array property, you can specify any Node.js module to be the back-end for a particular QEWD application – They don't have to have the same name
  • 14. Copyright © 2016 M/Gateway Developments Ltd Module Name Mapping • Remember: the QEWD application name is defined in its front-end app.js file when you invoke the ewd-client start() function, eg: – EWD.start('test-app', $, io); – This application will be registered on QEWD as test-app
  • 15. Copyright © 2016 M/Gateway Developments Ltd Module Name Mapping • But your QEWD startup file can define back-end module mapping, eg: – ~/qewd/qewd.js: var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, moduleMap: { test-app: '/path/to/myModule' } }; var qewd = require('qewd').master; qewd.start(config);
  • 16. Copyright © 2016 M/Gateway Developments Ltd Module Name Mapping • But your QEWD startup file can define back-end module mapping, eg: – ~/qewd/qewd.js: var config = { managementPassword: 'keepThisSecret!', serverName: 'My QEWD Server', port: 8080, poolSize: 2, database: { type: 'gtm' }, moduleMap: { test-app: '/path/to/myModule' } }; var qewd = require('qewd').master; qewd.start(config); Your QEWD application named test-app will use /path/to/myModule as its back-end module
  • 17. Copyright © 2016 M/Gateway Developments Ltd Modularising QEWD Back-end Logic • It's all too easy for the back-end module of a QEWD application to become a huge file – Ever increasing number of message handler functions
  • 18. Copyright © 2016 M/Gateway Developments Ltd Modularising QEWD Back-end Logic • It's all too easy for the back-end module of a QEWD application to become a huge file – Ever increasing number of message handler functions • Each handler function can, itself, be separated out into a module
  • 19. Copyright © 2016 M/Gateway Developments Ltd Modularising QEWD Back-end Logic • So instead of: module.exports = { handlers: { test: function(messageObj, session, send, finished) { var incomingText = messageObj.params.text; finished({text: 'You sent: ' + incomingText}); } } };
  • 20. Copyright © 2016 M/Gateway Developments Ltd Modularising QEWD Back-end Logic • You could define each handler function in its own sub-module: var test = require('./handlers/test'); module.exports = { handlers: { test: test } }; function test(messageObj, session, send, finished) { var incomingText = messageObj.params.text; finished({text: 'You sent: ' + incomingText}); } module.exports = test;
  • 21. Copyright © 2016 M/Gateway Developments Ltd Modularising QEWD by using Services • Further opportunities for modularisation are provided by QEWD Services – A Service Module is exactly like an application's own back-end module • Can contain one or more handler functions – But Service Modules can be shared between applications – Can be used as the basis for a QEWD-based Micro-Service architecture
  • 22. Copyright © 2016 M/Gateway Developments Ltd Modularising QEWD by using Services • QEWD Service Modules are good candidates for packaging as fully-fledged Node.js modules that can be published on NPM – That allows them to be re-used by other users for their applications
  • 23. Copyright © 2016 M/Gateway Developments Ltd Modularising QEWD by using Services • QEWD Service Modules can also be name-mapped, separating: – The name you use to refer to the service – The actual physical Node.js module name/path used for the service • For more details about EWD Services, see Part 16 of this course
  • 24. Copyright © 2016 M/Gateway Developments Ltd Modularising QEWD Applications • So the back-end logic of QEWD applications can be modularised • How about the front-end?
  • 25. Copyright © 2016 M/Gateway Developments Ltd Modularising Front-end Code • Two options available – Using "Fragments" – Using a bundler such as Browserify or WebPack
  • 26. Copyright © 2016 M/Gateway Developments Ltd Fragments • Becoming a bit of an old-fashioned approach – Not appropriate for modern frameworks such as React.js or Angular • But for more conventional HTML development, it can be an effective means of modularising an application – Can be part of a Service
  • 27. Copyright © 2016 M/Gateway Developments Ltd Fragments • The concept: – You load an initial HTML file (eg index.html) which typically contains very little markup, but loads all the JavaScript and CSS required by the application – Subsequently, fragments of HTML are loaded dynamically into <div> or other tags, as a result of events occurring in the browser • Via Ajax or WebSockets
  • 28. Copyright © 2016 M/Gateway Developments Ltd Fragments • Fragments can be included as part of a QEWD Service Module – Back-end message handler logic – Associated fragment(s) of markup – Publishable on NPM • An application could therefore use fragments and back-end logic from multiple services – ie: re-usable logic & markup / UI components
  • 29. Copyright © 2016 M/Gateway Developments Ltd EWD.getFragment() • To fetch a fragment, in your browser-side logic use: – EWD.getFragment(argObj, callback);
  • 30. Copyright © 2016 M/Gateway Developments Ltd EWD.getFragment() • To fetch a fragment, in your browser-side logic use: – EWD.getFragment(argObj, callback); • argObj: object containing: – name, eg: loginForm.html » By default, will fetch from same directory as index.html file – targetId: » id of the tag into which the markup will be inserted » eg <div id="myTargetDiv"></div>
  • 31. Copyright © 2016 M/Gateway Developments Ltd EWD.getFragment() • To fetch a fragment, in your browser-side logic use: – EWD.getFragment(argObj, callback); • callback: invoked when fragment is loaded into browser – Single optional argument: name » Filename of the fragment that was loaded – Use callback to load handlers needed by fragment's markup, eg button click handlers
  • 32. Copyright © 2016 M/Gateway Developments Ltd Example: index.html <html> <head> <title>Fragment Demo</title> <link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" /> </head> <body> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script src="/ewd-client.js"></script> <script src="app2.js"></script> <div id="loginFormDiv"></div> </body> </html>
  • 33. Copyright © 2016 M/Gateway Developments Ltd Example: app2.js $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); var params = { name: 'loginForm.html', targetId: 'loginFormDiv' }; EWD.getFragment(params, function(name) { toastr.info('fragment loaded: ' + JSON.stringify(name)); }); }); EWD.start('demo1', $, io); });
  • 34. Copyright © 2016 M/Gateway Developments Ltd Example <html> <head> <title>Demo ewd-xpress application</title> <link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" /> </head> <body> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script src="/ewd-client.js"></script> <script src="app2.js"></script> <div id="loginFormDiv"></div> </body> </html> var params = { name: 'loginForm.html', targetId: 'loginFormDiv' }; EWD.getFragment(params, function(name) { toastr.info('fragment loaded: ' + JSON.stringify(name)); });
  • 35. Copyright © 2016 M/Gateway Developments Ltd Example: loginForm.html <table id="loginForm"> <tr> <td>Username:</td> <td><input type="text" id="username" /></td> </tr> <tr> <td>Password:</td> <td><input type="password" id="password" /></td> </tr> <tr> <td colspan="2"> <button id="loginBtn">Login</button> </td> </tr> </table> Just the markup for the login form
  • 36. Copyright © 2016 M/Gateway Developments Ltd Try it out!
  • 37. Copyright © 2016 M/Gateway Developments Ltd Form won't do anything eg nothing happens if you click this button
  • 38. Copyright © 2016 M/Gateway Developments Ltd Edit: app2.js var params = { name: 'loginForm.html', targetId: 'loginFormDiv' }; EWD.getFragment(params, function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val(); if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide();} }); }); });
  • 39. Copyright © 2016 M/Gateway Developments Ltd Edit: app2.js var params = { name: 'loginForm.html', targetId: 'loginFormDiv' }; EWD.getFragment(params, function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val(); if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide();} }); }); }); Now it will add a click handler to the login button
  • 40. Copyright © 2016 M/Gateway Developments Ltd Edit: app2.js var params = { name: 'loginForm.html', targetId: 'loginFormDiv' }; EWD.getFragment(params, function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val(); if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide();} }); }); }); Now it will add a click handler to the login button When clicked, the username and password will be sent as a message, with type 'login', to the QEWD back-end
  • 41. Copyright © 2016 M/Gateway Developments Ltd Now it works
  • 42. Copyright © 2016 M/Gateway Developments Ltd Now it works • But we've had to define the fragment loader logic within our application's main app.js logic • It would be more modular if the logic associated with the loginForm fragment could be kept in a separate place – Separately maintainable – Re-usable
  • 43. Copyright © 2016 M/Gateway Developments Ltd Create loginForm.js • In same directory as app.js – Or in its own subdirectory under ~/qewd/www
  • 44. Copyright © 2016 M/Gateway Developments Ltd loginForm.js var loginForm = { loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } };
  • 45. Copyright © 2016 M/Gateway Developments Ltd Example: index.html <html> <head> <title>Fragment Demo</title> <link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" /> </head> <body> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script> <script src="/socket.io/socket.io.js"></script> <script src="/ewd-client.js"></script> <script src="app2.js"></script> <script src="loginForm.js"></script> <div id="loginFormDiv"></div> </body> </html>
  • 46. Copyright © 2016 M/Gateway Developments Ltd Example: app2.js $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); var params = { name: 'loginForm.html', targetId: 'loginFormDiv' }; EWD.getFragment(params, loginForm.loader); }); EWD.start('demo1', $, io); });
  • 47. Copyright © 2016 M/Gateway Developments Ltd Example: app2.js $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); var params = { name: 'loginForm.html', targetId: 'loginFormDiv' }; EWD.getFragment(params, loginForm.loader); }); EWD.start('demo1', $, io); }); var loginForm = { loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } }; loginForm.js
  • 48. Copyright © 2016 M/Gateway Developments Ltd What about the back-end code?
  • 49. Copyright © 2016 M/Gateway Developments Ltd What about the back-end code? var loginForm = { loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } }; A login message will be sent to the back-end. Currently QEWD will expect to find its handler in the main application module (demo1.js) So let's modularise it instead…
  • 50. Copyright © 2016 M/Gateway Developments Ltd Create a Login Service function checkLogin(username, password) { // hard-coded version for now if (username !== 'rob') return {error: 'Invalid username'}; if (password !== 'secret') return {error: 'Invalid password'}; return {ok: true}; } module.exports = { handlers: { login: function(messageObj, session, send, finished) { if (session.authenticated) { finished({error: 'You are already logged in!'}); return; } var username = messageObj.params.username; if (username === '') { finished({error: 'You must enter a username'}); return; } var password = messageObj.params.password; if (password === '') { finished({error: 'You must enter a password'}); return; } var status = checkLogin(username, password); if (status.ok) { session.authenticated = true; session.timeout = 3600; session.updateExpiry(); finished({ok: true}); } else { finished({error: status.error}); } } } }; Save into node_modules/Login.js Contains the logic for handling the login authentication at the back-end
  • 51. Copyright © 2016 M/Gateway Developments Ltd Edit loginForm.js var loginForm = { loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', service: 'Login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } }; Use the Login service for the login message
  • 52. Copyright © 2016 M/Gateway Developments Ltd Edit demo1.js module.exports = { servicesAllowed: { Login: true } }; It now just needs to be a minimal module that allows the demo1 app to use the Login service
  • 53. Copyright © 2016 M/Gateway Developments Ltd Try it out! • Restart QEWD to ensure that the new back-end modules are loaded – Alternatively use qewd-monitor to stop the worker processes • The demo application should run identically, but this time it's using the Login service
  • 54. Copyright © 2016 M/Gateway Developments Ltd Further Front-end Modularisation? • loginForm.html fragment is currently in www/demo1 directory • Could we make the fragment part of the Login service?
  • 55. Copyright © 2016 M/Gateway Developments Ltd Create Login Module • Create a new folder: – C:qewdnode_modulesLogin or – ~/qewd/node_modules/Login
  • 56. Copyright © 2016 M/Gateway Developments Ltd Create Login Module • ~/qewd/node_modules/Login package.json: { "name": "Login", "version": "1.0.0", "description": "Modular login system", "main": "index.js" } index.js 'use strict'; module.exports = require('./lib/Login'); - index.js - package.json
  • 57. Copyright © 2016 M/Gateway Developments Ltd Create Login Module • Move ~/qewd/node_modules/Login.js to: – ~/qewd/node_modules/Login/lib/Login.js - lib - Login.js - index.js - package.json
  • 58. Copyright © 2016 M/Gateway Developments Ltd Create Login Module • Move: – ~/qewd/www/demo1/loginForm.html • To: – ~/qewd/node_modules/Login/fragments/loginForm.html - fragments - loginForm.html - lib - Login.js - index.js - package.json
  • 59. Copyright © 2016 M/Gateway Developments Ltd Example: app2.js $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); var params = { name: 'loginForm.html', targetId: 'loginFormDiv', service: 'Login' }; EWD.getFragment(params, loginForm.loader); }); EWD.start('demo1', $, io); }); Load loginForm.html from the Login Service module ewd-xpress will look in the Login/fragments folder for the fragment
  • 60. Copyright © 2016 M/Gateway Developments Ltd Example: app2.js $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); var params = { name: 'loginForm.html', targetId: 'loginFormDiv', service: 'Login' }; EWD.getFragment(params, loginForm.loader); }); EWD.start('demo1', $, io); }); Load loginForm.html from the Login Service module ewd-xpress will look in the Login/fragments folder for the fragment - fragments - loginForm.html - lib - Login.js - index.js - package.json
  • 61. Copyright © 2016 M/Gateway Developments Ltd Client-side: loginForm.js var loginForm = { loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', service: 'Login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } }; Use the Login service for the login message
  • 62. Copyright © 2016 M/Gateway Developments Ltd Include loginForm.js in Service? • Since loginForm.js defines the handlers for the loginForm.html fragment, it would be sensible for it to be part of the Login Service too • However, web browsers can't load JavaScript from Node.js modules – Not directly • Two approaches…
  • 63. Copyright © 2016 M/Gateway Developments Ltd Add to Login Module • Copy: – ~/qewd/www/demo1/loginForm.js • To: – ~/qewd/node_modules/Login/client/loginForm.js - client - loginForm.js - fragments - loginForm.html - lib - Login.js - index.js - package.json
  • 64. Copyright © 2016 M/Gateway Developments Ltd Option 1 • Can be published and installed/used by others, but must: • Copy: – ~/qewd/node_modules/Login/client/loginForm.js • To: – ~/qewd/www/{{application name}}/loginForm.js - client - loginForm.js - fragments - loginForm.html - lib - Login.js - index.js - package.json
  • 65. Copyright © 2016 M/Gateway Developments Ltd Option 2: the modern approach • Can be published and installed/used by others • Make use of a bundler (eg Browserify or WebPack) – Define the front-end JavaScript as if you were using Node.js • So you can use: var loginForm = require('Login/client/loginForm'); – Use the bundler to convert to a single JavaScript file that can be loaded by the browser using a <script> tag – However, loginForm.js would have to be rewritten as a Module • See later… - client - loginForm.js - fragments - loginForm.html - lib - Login.js - index.js - package.json
  • 66. Copyright © 2016 M/Gateway Developments Ltd Modular Front-end Development • Install Browserify – cd ~/qewd (or cd C:qewd ) – npm install babelify – npm install –g browserify
  • 67. Copyright © 2016 M/Gateway Developments Ltd NPM versions of client-side JS • All the libraries loaded using <script> tags need to be accessed as Node.js modules instead – Most are available in this format these days cd ~/qewd npm install toastr jquery socket.io-client // ewd-client is already an installed as a module
  • 68. Copyright © 2016 M/Gateway Developments Ltd Edit loginForm.js • The loginForm.js client-side script file in our Login module needs to be changed: – Needs to be a Node.js module • Very simple change… - client - loginForm.js - fragments - loginForm.html - lib - Login.js - index.js - package.json
  • 69. Copyright © 2016 M/Gateway Developments Ltd Edit loginForm.js var toastr = require('toastr'); var EWD; module.exports = { init: function(ewd) { EWD = ewd; }, loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error('You must enter a password'); return; } var message = { type: 'login', service: 'Login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } };
  • 70. Copyright © 2016 M/Gateway Developments Ltd Edit loginForm.js var toastr = require('toastr'); var EWD; module.exports = { init: function(ewd) { EWD = ewd; }, loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error ('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error ('You must enter a password'); return; } var message = { type: 'login', service: 'Login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } }; Node.js modules must be entirely self-contained. toastr and EWD have to come from somewhere
  • 71. Copyright © 2016 M/Gateway Developments Ltd Edit loginForm.js var toastr = require('toastr'); var EWD; module.exports = { init: function(ewd) { EWD = ewd; }, loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error ('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error ('You must enter a password'); return; } var message = { type: 'login', service: 'Login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } }; In the case of toastr, we can require() it
  • 72. Copyright © 2016 M/Gateway Developments Ltd Edit loginForm.js var toastr = require('toastr'); var EWD; module.exports = { init: function(ewd) { EWD = ewd; }, loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error ('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error ('You must enter a password'); return; } var message = { type: 'login', service: 'Login', params: { username: username, password: password } }; EWD.send (message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } }; But the EWD object is more tricky loginForm.loader must use the post-register version of EWD so it can access the send() API and the session token. So we can't just use require('ewd-client') as we'd have the pre-registered version!
  • 73. Copyright © 2016 M/Gateway Developments Ltd Edit loginForm.js var toastr = require('toastr'); var EWD; module.exports = { init: function(ewd) { EWD = ewd; }, loader: function(name) { $('#loginBtn').on('click', function(e) { var username = $('#username').val(); if (username === '') { toastr.error ('You must enter a username'); return; } var password = $('#password').val() if (password === '') { toastr.error ('You must enter a password'); return; } var message = { type: 'login', service: 'Login', params: { username: username, password: password } }; EWD.send(message, function(responseObj) { if (!responseObj.message.error) { $('#loginForm').hide(); } }); }); } }; One approach is to use an init() function that will allow us to pass the post-register version of EWD into the module We'll see how it's used in app.js
  • 74. Copyright © 2016 M/Gateway Developments Ltd Edit app.jsvar io = require('socket.io-client') var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var toastr = require('toastr'); var EWD = require('ewd-client').EWD; var Login = require('Login/client/loginForm'); $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); Login.init(EWD); var params = { name: 'loginForm.html', targetId: 'loginFormDiv', service: 'Login' }; EWD.getFragment(params, Login.loader); }); EWD.start('demo1', $, io); }); app.js now needs to load everything it needs by using require()
  • 75. Copyright © 2016 M/Gateway Developments Ltd Edit app.jsvar io = require('socket.io-client') var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var toastr = require('toastr'); var EWD = require('ewd-client').EWD; var Login = require('Login/client/loginForm'); $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); Login.init(EWD); var params = { name: 'loginForm.html', targetId: 'loginFormDiv', service: 'Login' }; EWD.getFragment(params, Login.loader); }); EWD.start('demo1', $, io); }); The jQuery $ object needs to be instantiated as a property of window This gives it Global scope so none of the other modules that refer to $ need to require() it again eg $ is referred to in loginForm.js
  • 76. Copyright © 2016 M/Gateway Developments Ltd Edit app.jsvar io = require('socket.io-client') var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var toastr = require('toastr'); var EWD = require('ewd-client').EWD; var Login = require('Login/client/loginForm'); $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); Login.init(EWD); var params = { name: 'loginForm.html', targetId: 'loginFormDiv', service: 'Login' }; EWD.getFragment(params, Login.loader); }); EWD.start('demo1', $, io); }); The EWD object is loaded from the ewd-client module
  • 77. Copyright © 2016 M/Gateway Developments Ltd Edit app.jsvar io = require('socket.io-client') var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var toastr = require('toastr'); var EWD = require('ewd-client').EWD; var Login = require('Login/client/loginForm'); $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); Login.init(EWD); var params = { name: 'loginForm.html', targetId: 'loginFormDiv', service: 'Login' }; EWD.getFragment(params, Login.loader); }); EWD.start('demo1', $, io); }); And now we load the loginForm code from the Login service module
  • 78. Copyright © 2016 M/Gateway Developments Ltd Edit app.jsvar io = require('socket.io-client') var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var toastr = require('toastr'); var EWD = require('ewd-client').EWD; var Login = require('Login/client/loginForm'); $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); Login.init(EWD); var params = { name: 'loginForm.html', targetId: 'loginFormDiv', service: 'Login' }; EWD.getFragment(params, Login.loader); }); EWD.start('demo1', $, io); }); And now we load the loginForm code from the Login service module And pass the post-register EWD object into it
  • 79. Copyright © 2016 M/Gateway Developments Ltd Edit app.jsvar io = require('socket.io-client') var jQuery = require('jquery'); window.$ = window.jQuery = jQuery; var toastr = require('toastr'); var EWD = require('ewd-client').EWD; var Login = require('Login/client/loginForm'); $(document).ready(function() { EWD.log = true; EWD.on('ewd-registered', function() { EWD.on('error', function(responseObj) { toastr.error(responseObj.message.error); }); EWD.on('socketDisconnected', function() { toastr.info('You have been logged out'); setTimeout(function() { location.reload(); }, 1000); }); Login.init(EWD); var params = { name: 'loginForm.html', targetId: 'loginFormDiv', service: 'Login' }; EWD.getFragment(params, Login.loader ); }); EWD.start('demo1', $, io); }); And now we load the loginForm code from the Login service module And pass the post-register EWD object into it And then its loader function can be used as the getFragment callback, and it now has access to EWD to send messages and handle responses
  • 80. Copyright © 2016 M/Gateway Developments Ltd Edit index.html • Leave the <link> tags that load the CSS • Remove all the <script> tags but one: – bundle.js – Created from the modularised JavaScript by Browserify
  • 81. Copyright © 2016 M/Gateway Developments Ltd Edit index.html <html> <head> <title>Demo modularised ewd-xpress application</title> <link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet" /> </head> <body> <script src="bundle.js"></script> <div id="loginFormDiv"></div> </body> </html>
  • 82. Copyright © 2016 M/Gateway Developments Ltd Now create the bundle file • cd ~/qewd/www/demo1 • browserify -t [ babelify ] app.js -o bundle.js • If no errors reported, you should now have a bundle.js file in the demo1 directory
  • 83. Copyright © 2016 M/Gateway Developments Ltd Try it out • The application should run as before • Check in the JavaScript console that it's using the bundle.js file • You now have a fully modularised application – Everything related to logging in is defined in its own Login service module • Both its front-end and back-end logic • Could be re-used in any of your applications
  • 84. Copyright © 2016 M/Gateway Developments Ltd Bundling: things to note • If you make any changes to front-end JavaScript, in the application's own code or any of the modules it uses, you MUST re-run Browserify • WebPack is an alternative to Browserify – You may prefer it • You may want to also minimise the bundle.js file – eg minify
  • 85. Copyright © 2016 M/Gateway Developments Ltd Automating • Outside the scope of this training, but the trend is to now automate the build chain – When a file is changed, it triggers a re-build • Bundles the file • Minifies it • Runs unit tests • Updates the Git repository – eg using tools such as Gulp