SlideShare a Scribd company logo
1 of 85
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

More Related Content

What's hot

qewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tierqewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle TierRob Tweed
 
EWD 3 Training Course Part 2: EWD 3 Overview
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
 
EWD 3 Training Course Part 19: The cache.node APIs
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
 
EWD 3 Training Course Part 31: Using QEWD for Web and REST Services
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
 
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
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
 
EWD 3 Training Course Part 3: Summary of EWD 3 Modules
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
 
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
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 CycleRob Tweed
 
EWD 3 Training Course Part 27: The QEWD Session
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 SessionRob 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...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...Rob Tweed
 
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
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 ServicesRob Tweed
 
QEWD.js: Have your Node.js Cake and Eat It Too
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 TooRob Tweed
 
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
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 ApplicationRob Tweed
 
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
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 QEWDRob Tweed
 
EWD 3 Training Course Part 29: Running QEWD as a Service
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 ServiceRob Tweed
 
EWD 3 Training Course Part 35: QEWD Session Locking
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 LockingRob Tweed
 
EWD 3 Training Course Part 42: The QEWD Docker Appliance
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 ApplianceRob Tweed
 
EWD 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 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 QEWDRob Tweed
 
EWD 3 Training Course Part 11: Handling Errors in QEWD
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 QEWDRob Tweed
 
EWD 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 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 ServicesRob Tweed
 
EWD 3 Training Course Part 12: QEWD Session Timeout Control
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 ControlRob Tweed
 

What's hot (20)

qewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tierqewd-ripple: The Ripple OSI Middle Tier
qewd-ripple: The Ripple OSI Middle Tier
 
EWD 3 Training Course Part 2: EWD 3 Overview
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
 
EWD 3 Training Course Part 19: The cache.node APIs
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
 
EWD 3 Training Course Part 31: Using QEWD for Web and REST Services
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
 
EWD 3 Training Course Part 5b: First Steps in Building a QEWD Application
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
 
EWD 3 Training Course Part 3: Summary of EWD 3 Modules
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
 
EWD 3 Training Course Part 8: Anatomy of the QEWD Messaging Cycle
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
 
EWD 3 Training Course Part 27: The QEWD Session
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
 
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...
EWD 3 Training Course Part 36: Accessing REST and Web Services from a QEWD ap...
 
ewd-qoper8-vistarpc: Exposing VistA's RPCs as REST Services
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
 
QEWD.js: Have your Node.js Cake and Eat It Too
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
 
EWD 3 Training Course Part 5a: First Steps in Building a QEWD Application
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
 
EWD 3 Training Course Part 28: Integrating Legacy Mumps Code with QEWD
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
 
EWD 3 Training Course Part 29: Running QEWD as a Service
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
 
EWD 3 Training Course Part 35: QEWD Session Locking
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
 
EWD 3 Training Course Part 42: The QEWD Docker Appliance
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
 
EWD 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 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
 
EWD 3 Training Course Part 11: Handling Errors in QEWD
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
 
EWD 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 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
 
EWD 3 Training Course Part 12: QEWD Session Timeout Control
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
 

Viewers also liked

EWD 3 Training Course Part 26: Event-driven Indexing
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
 
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
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
 
EWD 3 Training Course Part 25: Document Database Capabilities
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
 
EWD 3 Training Course Part 21: Persistent JavaScript Objects
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
 
EWD 3 Training Course Part 20: The DocumentNode Object
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
 
EWD 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 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
 
EWD 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 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 4Rob Tweed
 
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
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 ObjectsRob Tweed
 
EWD 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 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 2Rob Tweed
 
EWD 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 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 5Rob Tweed
 
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
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 ObjectsRob Tweed
 
Mumps the Internet scale database
Mumps the Internet scale databaseMumps the Internet scale database
Mumps the Internet scale databasegeorge.james
 
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 ...
EWD 3 Training Course Part 13: Putting Everything so far into Practice using ...Rob Tweed
 
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
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 StorageRob Tweed
 
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
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/HTTPSRob Tweed
 

Viewers also liked (15)

EWD 3 Training Course Part 26: Event-driven Indexing
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
 
EWD 3 Training Course Part 24: Traversing a Document's Leaf Nodes
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
 
EWD 3 Training Course Part 25: Document Database Capabilities
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
 
EWD 3 Training Course Part 21: Persistent JavaScript Objects
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
 
EWD 3 Training Course Part 20: The DocumentNode Object
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
 
EWD 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 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
 
EWD 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 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
 
EWD 3 Training Course Part 22: Traversing Documents using DocumentNode Objects
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
 
EWD 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 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
 
EWD 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 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
 
EWD 3 Training Course Part 23: Traversing a Range using DocumentNode Objects
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
 
Mumps the Internet scale database
Mumps the Internet scale databaseMumps the Internet scale database
Mumps the Internet scale database
 
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 ...
EWD 3 Training Course Part 13: Putting Everything so far into Practice using ...
 
EWD 3 Training Course Part 18: Modelling NoSQL Databases using Global Storage
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
 
EWD 3 Training Course Part 32: Configuring QEWD to use SSL/HTTPS
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
 

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

Advanced Node.JS Meetup
Advanced Node.JS MeetupAdvanced Node.JS Meetup
Advanced Node.JS MeetupLINAGORA
 
Clocker - How to Train your Docker Cloud
Clocker - How to Train your Docker CloudClocker - How to Train your Docker Cloud
Clocker - How to Train your Docker CloudAndrew Kennedy
 
Cloudsim_openstack_aws_lastunit_bsccs_cloud computing
Cloudsim_openstack_aws_lastunit_bsccs_cloud computingCloudsim_openstack_aws_lastunit_bsccs_cloud computing
Cloudsim_openstack_aws_lastunit_bsccs_cloud computingMrSameerSTathare
 
CD in kubernetes using helm and ksonnet. Stas Kolenkin
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
 
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 OBrien - Pitfalls when developing with the SharePoint Framework (SPFx)Chris O'Brien
 
Improving build solutions dependency management with webpack
Improving build solutions  dependency management with webpackImproving build solutions  dependency management with webpack
Improving build solutions dependency management with webpackNodeXperts
 
Multi modularized project setup with gulp, typescript and angular.js
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.jsDavid Amend
 
Introduction to node js - From "hello world" to deploying on azure
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 azureColin Mackay
 
Tech io nodejs_20130531_v0.6
Tech io nodejs_20130531_v0.6Tech io nodejs_20130531_v0.6
Tech io nodejs_20130531_v0.6Ganesh Kondal
 
Introduction of webpack 4
Introduction of webpack 4Introduction of webpack 4
Introduction of webpack 4Vijay Shukla
 
CloudStack and cloud-init
CloudStack and cloud-initCloudStack and cloud-init
CloudStack and cloud-initMarcusS13
 
WebSphere and Docker
WebSphere and DockerWebSphere and Docker
WebSphere and DockerDavid Currie
 
Meet Magento Spain 2019 - Our Experience with Magento Cloud
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 CloudLyzun Oleksandr
 
Docker dev ops for cd meetup 12-14
Docker dev ops for cd meetup 12-14Docker dev ops for cd meetup 12-14
Docker dev ops for cd meetup 12-14Simon Storm
 
betterCode Workshop: Effizientes DevOps-Tooling mit Go
betterCode Workshop:  Effizientes DevOps-Tooling mit GobetterCode Workshop:  Effizientes DevOps-Tooling mit Go
betterCode Workshop: Effizientes DevOps-Tooling mit GoQAware GmbH
 
DevHub 3 - Composer plus Magento
DevHub 3 - Composer plus MagentoDevHub 3 - Composer plus Magento
DevHub 3 - Composer plus MagentoMagento Dev
 
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...
Extending OpenShift Origin: Build Your Own Cartridge with Bill DeCoste of Red...OpenShift Origin
 
Introduction to Node (15th May 2017)
Introduction to Node (15th May 2017)Introduction to Node (15th May 2017)
Introduction to Node (15th May 2017)Lucas Jellema
 

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

Advanced Node.JS Meetup
Advanced Node.JS MeetupAdvanced Node.JS Meetup
Advanced Node.JS Meetup
 
Clocker - How to Train your Docker Cloud
Clocker - How to Train your Docker CloudClocker - How to Train your Docker Cloud
Clocker - How to Train your Docker Cloud
 
Cloudsim_openstack_aws_lastunit_bsccs_cloud computing
Cloudsim_openstack_aws_lastunit_bsccs_cloud computingCloudsim_openstack_aws_lastunit_bsccs_cloud computing
Cloudsim_openstack_aws_lastunit_bsccs_cloud computing
 
CD in kubernetes using helm and ksonnet. Stas Kolenkin
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
 
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 OBrien - Pitfalls when developing with the SharePoint Framework (SPFx)
 
Improving build solutions dependency management with webpack
Improving build solutions  dependency management with webpackImproving build solutions  dependency management with webpack
Improving build solutions dependency management with webpack
 
Node js meetup
Node js meetupNode js meetup
Node js meetup
 
Multi modularized project setup with gulp, typescript and angular.js
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
 
Introduction to node js - From "hello world" to deploying on azure
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
 
Tech io nodejs_20130531_v0.6
Tech io nodejs_20130531_v0.6Tech io nodejs_20130531_v0.6
Tech io nodejs_20130531_v0.6
 
Introduction of webpack 4
Introduction of webpack 4Introduction of webpack 4
Introduction of webpack 4
 
Webpack: from 0 to 2
Webpack: from 0 to 2Webpack: from 0 to 2
Webpack: from 0 to 2
 
CloudStack and cloud-init
CloudStack and cloud-initCloudStack and cloud-init
CloudStack and cloud-init
 
WebSphere and Docker
WebSphere and DockerWebSphere and Docker
WebSphere and Docker
 
Meet Magento Spain 2019 - Our Experience with Magento Cloud
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
 
Docker dev ops for cd meetup 12-14
Docker dev ops for cd meetup 12-14Docker dev ops for cd meetup 12-14
Docker dev ops for cd meetup 12-14
 
betterCode Workshop: Effizientes DevOps-Tooling mit Go
betterCode Workshop:  Effizientes DevOps-Tooling mit GobetterCode Workshop:  Effizientes DevOps-Tooling mit Go
betterCode Workshop: Effizientes DevOps-Tooling mit Go
 
DevHub 3 - Composer plus Magento
DevHub 3 - Composer plus MagentoDevHub 3 - Composer plus Magento
DevHub 3 - Composer plus Magento
 
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...
Extending OpenShift Origin: Build Your Own Cartridge with Bill DeCoste of Red...
 
Introduction to Node (15th May 2017)
Introduction to Node (15th May 2017)Introduction to Node (15th May 2017)
Introduction to Node (15th May 2017)
 

Recently uploaded

MUT4SLX: Extensions for Mutation Testing of Stateflow Models
MUT4SLX: Extensions for Mutation Testing of Stateflow ModelsMUT4SLX: Extensions for Mutation Testing of Stateflow Models
MUT4SLX: Extensions for Mutation Testing of Stateflow ModelsUniversity of Antwerp
 
Enterprise Content Managements Solutions
Enterprise Content Managements SolutionsEnterprise Content Managements Solutions
Enterprise Content Managements SolutionsIQBG inc
 
Practical Advice for FDA’s 510(k) Requirements.pdf
Practical Advice for FDA’s 510(k) Requirements.pdfPractical Advice for FDA’s 510(k) Requirements.pdf
Practical Advice for FDA’s 510(k) Requirements.pdfICS
 
Telebu Social -Whatsapp Business API : Mastering Omnichannel Business Communi...
Telebu Social -Whatsapp Business API : Mastering Omnichannel Business Communi...Telebu Social -Whatsapp Business API : Mastering Omnichannel Business Communi...
Telebu Social -Whatsapp Business API : Mastering Omnichannel Business Communi...telebusocialmarketin
 
If your code could speak, what would it tell you? Let GitHub Copilot Chat hel...
If your code could speak, what would it tell you? Let GitHub Copilot Chat hel...If your code could speak, what would it tell you? Let GitHub Copilot Chat hel...
If your code could speak, what would it tell you? Let GitHub Copilot Chat hel...Maxim Salnikov
 
Unlocking AI: Navigating Open Source vs. Commercial Frontiers
Unlocking AI:Navigating Open Source vs. Commercial FrontiersUnlocking AI:Navigating Open Source vs. Commercial Frontiers
Unlocking AI: Navigating Open Source vs. Commercial FrontiersRaphaël Semeteys
 
MinionLabs_Mr. Gokul Srinivas_Young Entrepreneur
MinionLabs_Mr. Gokul Srinivas_Young EntrepreneurMinionLabs_Mr. Gokul Srinivas_Young Entrepreneur
MinionLabs_Mr. Gokul Srinivas_Young EntrepreneurPriyadarshini T
 
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptxCYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptxBarakaMuyengi
 
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...Splashtop Inc
 
Mobile App Development process | Expert Tips
Mobile App Development process | Expert TipsMobile App Development process | Expert Tips
Mobile App Development process | Expert Tipsmichealwillson701
 
Boost Efficiency: Sabre API Integration Made Easy
Boost Efficiency: Sabre API Integration Made EasyBoost Efficiency: Sabre API Integration Made Easy
Boost Efficiency: Sabre API Integration Made Easymichealwillson701
 
Flutter the Future of Mobile App Development - 5 Crucial Reasons.pdf
Flutter the Future of Mobile App Development - 5 Crucial Reasons.pdfFlutter the Future of Mobile App Development - 5 Crucial Reasons.pdf
Flutter the Future of Mobile App Development - 5 Crucial Reasons.pdfMind IT Systems
 
renewable energy renewable energy renewable energy renewable energy
renewable energy renewable energy renewable energy  renewable energyrenewable energy renewable energy renewable energy  renewable energy
renewable energy renewable energy renewable energy renewable energyjeyasrig
 
Leveling Up your Branding and Mastering MERN: Fullstack WebDev
Leveling Up your Branding and Mastering MERN: Fullstack WebDevLeveling Up your Branding and Mastering MERN: Fullstack WebDev
Leveling Up your Branding and Mastering MERN: Fullstack WebDevpmgdscunsri
 
8 Steps to Build a LangChain RAG Chatbot.
8 Steps to Build a LangChain RAG Chatbot.8 Steps to Build a LangChain RAG Chatbot.
8 Steps to Build a LangChain RAG Chatbot.Ritesh Kanjee
 
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...jackiepotts6
 
BusinessGPT - SECURITY AND GOVERNANCE FOR GENERATIVE AI.pptx
BusinessGPT  - SECURITY AND GOVERNANCE  FOR GENERATIVE AI.pptxBusinessGPT  - SECURITY AND GOVERNANCE  FOR GENERATIVE AI.pptx
BusinessGPT - SECURITY AND GOVERNANCE FOR GENERATIVE AI.pptxAGATSoftware
 
Building Generative AI-infused apps: what's possible and how to start
Building Generative AI-infused apps: what's possible and how to startBuilding Generative AI-infused apps: what's possible and how to start
Building Generative AI-infused apps: what's possible and how to startMaxim Salnikov
 
User Experience Designer | Kaylee Miller Resume
User Experience Designer | Kaylee Miller ResumeUser Experience Designer | Kaylee Miller Resume
User Experience Designer | Kaylee Miller ResumeKaylee Miller
 

Recently uploaded (20)

MUT4SLX: Extensions for Mutation Testing of Stateflow Models
MUT4SLX: Extensions for Mutation Testing of Stateflow ModelsMUT4SLX: Extensions for Mutation Testing of Stateflow Models
MUT4SLX: Extensions for Mutation Testing of Stateflow Models
 
Enterprise Content Managements Solutions
Enterprise Content Managements SolutionsEnterprise Content Managements Solutions
Enterprise Content Managements Solutions
 
Practical Advice for FDA’s 510(k) Requirements.pdf
Practical Advice for FDA’s 510(k) Requirements.pdfPractical Advice for FDA’s 510(k) Requirements.pdf
Practical Advice for FDA’s 510(k) Requirements.pdf
 
Telebu Social -Whatsapp Business API : Mastering Omnichannel Business Communi...
Telebu Social -Whatsapp Business API : Mastering Omnichannel Business Communi...Telebu Social -Whatsapp Business API : Mastering Omnichannel Business Communi...
Telebu Social -Whatsapp Business API : Mastering Omnichannel Business Communi...
 
If your code could speak, what would it tell you? Let GitHub Copilot Chat hel...
If your code could speak, what would it tell you? Let GitHub Copilot Chat hel...If your code could speak, what would it tell you? Let GitHub Copilot Chat hel...
If your code could speak, what would it tell you? Let GitHub Copilot Chat hel...
 
Unlocking AI: Navigating Open Source vs. Commercial Frontiers
Unlocking AI:Navigating Open Source vs. Commercial FrontiersUnlocking AI:Navigating Open Source vs. Commercial Frontiers
Unlocking AI: Navigating Open Source vs. Commercial Frontiers
 
MinionLabs_Mr. Gokul Srinivas_Young Entrepreneur
MinionLabs_Mr. Gokul Srinivas_Young EntrepreneurMinionLabs_Mr. Gokul Srinivas_Young Entrepreneur
MinionLabs_Mr. Gokul Srinivas_Young Entrepreneur
 
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptxCYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
CYBER SECURITY AND CYBER CRIME COMPLETE GUIDE.pLptx
 
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
Splashtop Enterprise Brochure - Remote Computer Access and Remote Support Sof...
 
Mobile App Development process | Expert Tips
Mobile App Development process | Expert TipsMobile App Development process | Expert Tips
Mobile App Development process | Expert Tips
 
Boost Efficiency: Sabre API Integration Made Easy
Boost Efficiency: Sabre API Integration Made EasyBoost Efficiency: Sabre API Integration Made Easy
Boost Efficiency: Sabre API Integration Made Easy
 
Flutter the Future of Mobile App Development - 5 Crucial Reasons.pdf
Flutter the Future of Mobile App Development - 5 Crucial Reasons.pdfFlutter the Future of Mobile App Development - 5 Crucial Reasons.pdf
Flutter the Future of Mobile App Development - 5 Crucial Reasons.pdf
 
renewable energy renewable energy renewable energy renewable energy
renewable energy renewable energy renewable energy  renewable energyrenewable energy renewable energy renewable energy  renewable energy
renewable energy renewable energy renewable energy renewable energy
 
20140812 - OBD2 Solution
20140812 - OBD2 Solution20140812 - OBD2 Solution
20140812 - OBD2 Solution
 
Leveling Up your Branding and Mastering MERN: Fullstack WebDev
Leveling Up your Branding and Mastering MERN: Fullstack WebDevLeveling Up your Branding and Mastering MERN: Fullstack WebDev
Leveling Up your Branding and Mastering MERN: Fullstack WebDev
 
8 Steps to Build a LangChain RAG Chatbot.
8 Steps to Build a LangChain RAG Chatbot.8 Steps to Build a LangChain RAG Chatbot.
8 Steps to Build a LangChain RAG Chatbot.
 
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
03.2024_North America VMUG Optimizing RevOps using the power of ChatGPT in Ma...
 
BusinessGPT - SECURITY AND GOVERNANCE FOR GENERATIVE AI.pptx
BusinessGPT  - SECURITY AND GOVERNANCE  FOR GENERATIVE AI.pptxBusinessGPT  - SECURITY AND GOVERNANCE  FOR GENERATIVE AI.pptx
BusinessGPT - SECURITY AND GOVERNANCE FOR GENERATIVE AI.pptx
 
Building Generative AI-infused apps: what's possible and how to start
Building Generative AI-infused apps: what's possible and how to startBuilding Generative AI-infused apps: what's possible and how to start
Building Generative AI-infused apps: what's possible and how to start
 
User Experience Designer | Kaylee Miller Resume
User Experience Designer | Kaylee Miller ResumeUser Experience Designer | Kaylee Miller Resume
User Experience Designer | Kaylee Miller Resume
 

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