Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

EWD 3 Training Course Part 30: Modularising QEWD Applications

618 views

Published on

This presentation is Part 30 of the EWD 3 Training Course. It explains the main techniques you can use for breaking a QEWD application into separate re-usable modules, allowing for easier maintenance and team development

Published in: Software
  • Be the first to comment

  • Be the first to like this

EWD 3 Training Course Part 30: Modularising QEWD Applications

  1. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 29. Copyright © 2016 M/Gateway Developments Ltd EWD.getFragment() • To fetch a fragment, in your browser-side logic use: – EWD.getFragment(argObj, callback);
  30. 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. 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. 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. 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. 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. 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. 36. Copyright © 2016 M/Gateway Developments Ltd Try it out!
  37. 37. Copyright © 2016 M/Gateway Developments Ltd Form won't do anything eg nothing happens if you click this button
  38. 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. 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. 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. 41. Copyright © 2016 M/Gateway Developments Ltd Now it works
  42. 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. 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. 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. 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. 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. 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. 48. Copyright © 2016 M/Gateway Developments Ltd What about the back-end code?
  49. 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. 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. 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. 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. 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. 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. 55. Copyright © 2016 M/Gateway Developments Ltd Create Login Module • Create a new folder: – C:qewdnode_modulesLogin or – ~/qewd/node_modules/Login
  56. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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

×