Mais conteĂșdo relacionado Semelhante a EWD 3 Training Course Part 30: Modularising QEWD Applications (20) EWD 3 Training Course Part 30: Modularising QEWD Applications1. 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
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
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
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