{"id":2192,"date":"2018-06-07T15:22:47","date_gmt":"2018-06-07T19:22:47","guid":{"rendered":"http:\/\/cs4760.csl.mtu.edu\/2018\/?page_id=2192"},"modified":"2018-08-28T21:07:35","modified_gmt":"2018-08-29T01:07:35","slug":"advance-offline-design-and-implementation","status":"publish","type":"page","link":"https:\/\/cs4760.csl.mtu.edu\/2018\/advance-offline-design-and-implementation\/","title":{"rendered":"Advance Offline Design and Implementation"},"content":{"rendered":"<h1>Offline Interaction Design Reconsidered<\/h1>\n<p>After implementing and deploying several offline apps, I discovered that many user were confused by the interaction design proposed in the previous lecture, <a href=\"..\/..\/lectures\/interaction-design-for-offline-use\">Design for Offline<\/a>. User were confused by the meaning of the &#8220;save&#8221; and &#8220;upload&#8221; buttons. Users would ask, &#8220;When do I save and when do I upload the data?&#8221; Although the users were aware when they might be offline, they did\u00a0not understand that the &#8220;save&#8221; button would save the data to the device and that &#8220;upload&#8221; would submit the data to server. Users understand that a website saves data on the sever. They are not aware that HTML5 allows websites to save data on their device. Consequently, they do not understand the need two different saves.<\/p>\n<p>Zack Duford, a former student, proposed new offline interaction. Zack&#8217;s interaction design eliminates the &#8220;save&#8221; button. Weather online or offline, the button for saving the data is &#8220;upload&#8221; (or &#8220;submit&#8221;). If the app is online then the app automatically saves the data to the server. If the app is offline the app saves the data on the device and displaces the data in a &#8220;queue&#8221;. While offline, the\u00a0user can view the &#8220;queue&#8221; which shows all the data already collected\u00a0offline. Consequently, users can view the queue receive feedback that the data &#8220;will be uploaded&#8221;. Zack proposed that user should not be permitted to edit the data in the queue.\u00a0At the time, Zack and I thought that <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Service_Worker_API\">HTML5 Service Workers<\/a>\u00a0would be able to detect online status and automatically upload the data in the queue to the server.<\/p>\n<p>Zack made a prototype of a fishing app using <a href=\"https:\/\/www.adobe.com\/products\/xd.html\">Adobe XD<\/a>. Fishermen can record catches that make out on lake on offline entering the location, description and photo of the catch. You can view screen shoots of Zack&#8217;s prototype in <a href=\"..\/..\/..\/resources\/advance-offline\/Zack_Offline_Fishing_App_Views.pdf\">Zack&#8217;s Offline Fishing App Views<\/a>. During the usability test, Zack tried to ascertain the participants mental model of the app by asking two questions after each test scenario:<\/p>\n<ul>\n<li>Is this a satisfactory interface for the interaction?<\/li>\n<li>Do you think you are being provided sufficient information about the data at any given point in time in the scenario?<\/li>\n<\/ul>\n<p>Zack tested 7 participants, 3 CS majors and 4 non-CS majors. Only\u00a0one non-CS major participant show some confusion by the questions. You can read <a href=\"..\/..\/..\/resources\/advance-offline\/Zack_Offline_Design_Usability_Report.pdf\">Zack&#8217;s Offline Design Usability\u00a0Report<\/a>\u00a0and <a href=\"..\/..\/..\/resources\/advance-offline\/Zack_Usability_Test_Plan_for_Offline_Fishing_App.pdf\">Zack&#8217;s Usability Test Plan for Offline Fishing App<\/a>.<\/p>\n<p>Later, I discovered that the Service Workers cannot automatically upload the app&#8217;s data when an internet connection is established. After an internet connection is established, the user must interact with the app and click a &#8220;submit&#8221; or &#8220;upload&#8221; button. Nevertheless this does not invalidate all of Zack&#8217;s design. The queue does give the user concrete and visual display of where the data is and that it ready for uploading.<\/p>\n<h1>Single Page Application and Service Workers<\/h1>\n<p>In my previous lecture about <a href=\"..\/..\/lectures\/offline-implementation\">Offline Implementation<\/a>, my students and I describe an architecture for the offline component of the app that mirrors the online design pattern, meaning Model-View-Controller design pattern. The offline line component of the app had separate views that mirrored the online views and JavaScript files that assumed the role of controllers of respective controller. Finally, they were JavaScript classes that assumed the role of domain objects in online component. I proposed this architectural design because I felt this would allow developers to first develop the online app using well established frameworks, e.g. Grails. But the architectural design was awkward to\u00a0maintain and inefficient. Both the offline and online views were very similar and change in one view would frequently require change the corresponding view. Also changing views would require retrieving the new view from cache.<\/p>\n<p>Recently, industry proposes an new architectural for offline app, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Single-page_application\">Single Page Application<\/a> (SPA).\u00a0 In a SPA, there is only one html or gsp file. The JavaScript code of the app control or simulate different views by hiding and showing divs. Developer has only a single code base to manage. JavaScript runs very fast and render the page quickly.<\/p>\n<p>A new HTML5 standard, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Service_Worker_API\">Service Worker<\/a>, will make <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Using_the_application_cache\">App Cache\u00a0obsolete<\/a>. The very humorous blog, <a href=\"https:\/\/alistapart.com\/article\/application-cache-is-a-douchebag\">Application Cache is a Douchebag<\/a>, illustrates the many problems with app cache. <a href=\"https:\/\/developers.google.com\/web\/fundamentals\/instant-and-offline\/offline-cookbook\/\">Service workers are more flexible<\/a>, but they are still <a href=\"https:\/\/caniuse.com\/#feat=serviceworkers\">not implemented in the iOS and Edge<\/a>. Although the app cache is limited, for our applications it suffices, but implementing offline caching using s<a href=\"https:\/\/developers.google.com\/web\/fundamentals\/primers\/service-workers\/\">ervice workers is not difficult for our applications<\/a>\u00a0once the initial configuration is set up.<\/p>\n<p>Jake Mager, a former student, developed a tutorial demonstrating implementing an offline SPA for the fishing app. To learn from Jake&#8217;s tutorial clone the repository, <a href=\"https:\/\/github.com\/2017-SD\/fishing-tutorial\">fishing-tutorial<\/a>, onto your development machine. Import the source code into IntelliJ. Be sure that the SDK used by IntelliJ is Java 8. In the repository you find a Tutorial directory with tutorial.html. Right click tutorial.html and select Open in Browser. Follow along with the tutorial developing the app in another project.<\/p>\n<p>Jake uses only JQuery show and hide functions to implement the view changes in the SPA:<\/p>\n<pre>$(\"#showCatchesButton\").hide()\r\n$(\"#editCatchButton\").show();\r\n<\/pre>\n<p>The views are primarily generated by the index.js file.<\/p>\n<p>Grails domain object are still used as <a href=\"https:\/\/en.wikipedia.org\/wiki\/Object-relational_mapping\">Object Relational Model<\/a> (ORM) to interface between the business code on the server and the database. Controller methods represent endpoints for AJAX calls made by JavaScript code.<\/p>\n<p>The tutorial also explains how to implement service workers in Grails for caching the app. In particular the grails resource pattern must be configured:<\/p>\n<pre>grails:\r\n    resources:\r\n            pattern: '\/**'\r\n<\/pre>\n<p>The service worker file in src\/main\/webapp\/sw.js is simple:<\/p>\n<pre>self.addEventListener('install', function (event) {\r\n    self.skipWaiting();\r\n    event.waitUntil(\r\n        caches.open('fishingApp').then(function (cache) {\r\n            return cache.addAll(\r\n                [\r\n                    '\/',\r\n                    '\/assets\/jquery-3.1.1.js?compile=false',\r\n                    '\/assets\/index.css?compile=false',\r\n                    '\/assets\/main.css?compile=false',\r\n                    '\/assets\/bootstrap.css?compile=false',\r\n                    '\/assets\/bootstrap-theme.css?compile=false',\r\n                    '\/assets\/bootstrap.js?compile=false',\r\n                    '\/assets\/index.js?compile=false',\r\n                    '\/assets\/application.js?compile=false',\r\n                    '\/assets\/logo.png',\r\n                    '\/assets\/localforage\/localforage.js?compile=false',\r\n                    '\/assets\/localforage\/localforage.min.js?compile=false',\r\n                    '\/assets\/localforage\/localforage.nopromises.js?compile=false',\r\n                    '\/assets\/localforage\/localforage.nopromises.min.js?compile=false',\r\n                ]\r\n            );\r\n        })\r\n    );\r\n    console.log('Install event:', event);\r\n});<\/pre>\n<p>The service worker is registered after the document has loaded in index.js<\/p>\n<pre>$(document).ready(function() {\r\n\r\n    \/\/ check if user is online with navigator\r\n    if (navigator.onLine) {\r\n\r\n        \/\/ check if a service worker is supported then register it\r\n        if ('serviceWorker' in navigator) {\r\n            navigator.serviceWorker\r\n                .register('\/sw.js')\r\n                .then(function() { console.log(\"Service Worker Registered\"); });\r\n        }\r\n...\r\n}<\/pre>\n<h1>React JavaScript Framework<\/h1>\n<p>There are several JavaScript framework for developing single page applications. For example:<\/p>\n<ul>\n<li>React &#8211;\u00a0<a href=\"https:\/\/reactjs.org\/\">https:\/\/reactjs.org\/<\/a><\/li>\n<li>Angular &#8211; <a href=\"https:\/\/angular.io\/\">https:\/\/angular.io\/<\/a><\/li>\n<li>Ember &#8211;\u00a0<a href=\"https:\/\/www.emberjs.com\/\">https:\/\/www.emberjs.com\/<\/a><\/li>\n<li>Vue.js &#8211;\u00a0<a href=\"https:\/\/vuejs.org\/\">https:\/\/vuejs.org\/<\/a><\/li>\n<\/ul>\n<p>React, in particular, is effective for SPA development because it is very light weight and responsive. It is also support by Facebook. Although Angular is also supported by a large company, Google, React is a simpler framework, and I think has a shorter learning curve. React is unique from the other JavaScript frameworks because it moves the HTML code into JavaScript code.\u00a0 Combining JavaScript and HTML code in the same file does invalidate &#8220;division of concerns&#8221;, specifically visual from logic. React recognizes the coupling of logic with the UI. React uses JSX to express HTML in the JavaScript code:<\/p>\n<p><a href=\"https:\/\/reactjs.org\/docs\/introducing-jsx.html\">https:\/\/reactjs.org\/docs\/introducing-jsx.html<\/a><\/p>\n<p>Instead of making the separation of concern between visual (or layout) and logic, using React with JSX separates concerns between <em>components<\/em> of the UI so that the logic associated with the view component are together in one file.\u00a0 For software engineering developers at MTU, separation by component view is better because the developers implement both the visual presentation and the logic of the view.<\/p>\n<p>The structure of the react components is hierarchical just like the DOM:<\/p>\n<p><a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Document_Object_Model\/Introduction\">https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Document_Object_Model\/Introduction<\/a><\/p>\n<p>Components of the app communicate by passing down properties, called <em>props<\/em>, down the\u00a0component hierarchy. Props that are objects send messages down the\u00a0component hierachy, and properties that are functions for event listener send messages up the component hierarchy.<\/p>\n<p>React introduces another concept, the application <em>state<\/em>. The state of the application is expressed as as object of a property of a component class. Ideally, there is only one state object for the entire web app, or a major portion of the app. This is advantageous because there is a single place of truth. It also facilitate the responsiveness of React because if the state should change then JSX knows to propagate down the component\u00a0hierarchy to change the view. There are cases were a single state object is not appropriate. An example for a small web app is a form. Forms need there own state object so that validation can occur during user input.<\/p>\n<p>Bryce Williams, a former student,\u00a0developed a second fishing app tutorial using the React framework. To learn React from Bryce&#8217;s tutorial, clone the repository:<\/p>\n<p><a href=\"https:\/\/github.com\/2017-SD\/fishing-tutorial-2\">https:\/\/github.com\/2017-SD\/fishing-tutorial-2<\/a><\/p>\n<p>Look for the tutorial folder for the GrailsReactOfflineAppTutorial.pdf<\/p>\n<p><a href=\"https:\/\/github.com\/2017-SD\/fishing-tutorial-2\/blob\/master\/tutorial\/GrailsReactOfflineAppTutorial.pdf\">https:\/\/github.com\/2017-SD\/fishing-tutorial-2\/blob\/master\/tutorial\/GrailsReactOfflineAppTutorial.pdf<\/a><\/p>\n<p>Bryce&#8217;s tutorial also uses Spring Security for authentication and authorization.\u00a0 The User controller includes an addition method, getLogin(), to determine if the user is logged in.<\/p>\n<pre>class UserController {\r\n    def springSecurityService\r\n\r\n    def index() { }\r\n\r\n     \/\/ this function checks if the user is logged in\r\n    def getLogin() {\r\n       User user = springSecurityService.currentUser\r\n\r\n       if (user != null) {\r\n          render user.fname\r\n       }\r\n       else {\r\n         render false\r\n      }\r\n   }\r\n}<\/pre>\n<p>Notice how springSecurityService is injected into the controller.<\/p>\n<p>The root of the component hierarchy is App.js:<\/p>\n<p><a href=\"https:\/\/github.com\/2017-SD\/fishing-tutorial-2\/blob\/master\/src\/main\/webapp\/App.js\">https:\/\/github.com\/2017-SD\/fishing-tutorial-2\/blob\/master\/src\/main\/webapp\/App.js<\/a><\/p>\n<p>The app state object is define in the App constructor:<\/p>\n<pre>class App extends Component {\r\n    constructor() {\r\n    super();\r\n\r\n    this.state = {\r\n        logged_in: false,\r\n        online: true,\r\n\r\n       showing_loading_modal: false, \/\/ modal indicating something is loading\r\n       loading_message: '', \/\/ message to display on the modal\r\n\r\n       showing_nc_modal: false, \/\/ new catch modal\r\n\r\n       queue: [], \/\/ offline upload queue\r\n       posted_catches: [], \/\/ catches in the db\r\n       showing_detail_modal: false, \/\/ catch detail modal\r\n       display_catch: {}, \/\/ catch info to display\r\n     }\r\n   }\r\n...\r\n}\r\n\r\n<\/pre>\n<p>The render method defines the the component hierarchy. in the App component, the state props are passed down to sub components.<\/p>\n<pre> render() {\r\n     const {\r\n       logged_in,\r\n       showing_nc_modal,\r\n       online,\r\n       queue,\r\n       posted_catches,\r\n       showing_detail_modal,\r\n       display_catch,\r\n       showing_loading_modal,\r\n       loading_message\r\n     } = this.state\r\n\r\n    \/\/ all items true if not empty\r\n   let items_in_queue = (!isEmpty(queue)) \/\/ for upload queue\r\n   let catches = (!isEmpty(posted_catches)) \/\/ for posted catches\r\n   let catch_to_display = (display_catch !== {}) \/\/ for catch detail modal\r\n\r\n   return (\r\n     &lt;div&gt;\r\n       &lt;AppNav\r\n       logged_in={logged_in}\r\n       online={online}\r\n     \/&gt;\r\n\r\n    { \/* shows if there is a queue *\/\r\n    items_in_queue &amp;&amp;\r\n    &lt;div&gt;\r\n       &lt;UploadQueue queue={queue}\/&gt;\r\n       &lt;br\/&gt;\r\n    &lt;\/div&gt;\r\n   }\r\n\r\n  { \/* only upload if online &amp; logged in *\/\r\n  items_in_queue &amp;&amp; online &amp;&amp; logged_in &amp;&amp;\r\n  &lt;div style={app_styles.button}&gt;\r\n     &lt;Button onClick={this.uploadQueue} bsStyle=\"success\"&gt;Submit Pending Catches&lt;\/Button&gt;\r\n     &lt;br\/&gt;\r\n   &lt;\/div&gt;\r\n  }\r\n\r\n &lt;div style={app_styles.button}&gt;\r\n    &lt;Button onClick={this.showNewCatchModal} bsStyle=\"success\"&gt;New Catch&lt;\/Button&gt;\r\n &lt;\/div&gt;\r\n &lt;br\/&gt;\r\n\r\n { \/* only show catches if online &amp; logged in *\/\r\n online &amp;&amp; logged_in &amp;&amp;\r\n &lt;div style={app_styles.button}&gt;\r\n    &lt;Button onClick={this.showCatches} bsStyle=\"success\"&gt;Show Your Catches&lt;\/Button&gt;\r\n    &lt;br\/&gt;\r\n &lt;\/div&gt;\r\n } \r\n\r\n { \/* only show if there have been catches posted *\/\r\n catches &amp;&amp;\r\n &lt;div&gt;\r\n    &lt;CatchTable\r\n       catches={posted_catches}\r\n       showDetailModal={this.showDetailModal}\r\n     \/&gt;\r\n &lt;\/div&gt;\r\n }\r\n\r\n {\/* modals *\/}\r\n &lt;LoadingModal\r\n    showing={showing_loading_modal}\r\n    hideModal={this.hideLoadingModal}\r\n    message={loading_message}\r\n  \/&gt;\r\n\r\n &lt;NewCatchModal\r\n    showing={showing_nc_modal}\r\n    hideModal={this.hideNewCatchModal}\r\n    submitNewCatch={this.submitNewCatch}\r\n \/&gt;\r\n\r\n {\r\n catch_to_display &amp;&amp;\r\n &lt;CatchDetailModal\r\n    showing={showing_detail_modal}\r\n    hideModal={this.hideDetailModal}\r\n    selected_catch={display_catch}\r\n \/&gt;\r\n }\r\n\r\n &lt;\/div&gt;\r\n\r\n );\r\n }\r\n}<\/pre>\n<p>For example the CatchDetailModal is passed the the showing and selected_catch state variables and the hideModal function.<\/p>\n<pre> &lt;CatchDetailModal\r\n     showing={showing_detail_modal}\r\n     hideModal={this.hideDetailModal}\r\n     selected_catch={display_catch}\r\n \/&gt;<\/pre>\n<p>Note that CatchDetailModal is another React component, in this case a function not a class because it does not have a state.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Offline Interaction Design Reconsidered After implementing and deploying several offline apps, I discovered that many user were confused by the interaction design proposed in the previous lecture, Design for Offline. User were confused by the meaning of the &#8220;save&#8221; and &#8220;upload&#8221; buttons. Users would ask, &#8220;When do I save and when do I upload the [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2192","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/cs4760.csl.mtu.edu\/2018\/wp-json\/wp\/v2\/pages\/2192","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cs4760.csl.mtu.edu\/2018\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/cs4760.csl.mtu.edu\/2018\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/cs4760.csl.mtu.edu\/2018\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/cs4760.csl.mtu.edu\/2018\/wp-json\/wp\/v2\/comments?post=2192"}],"version-history":[{"count":8,"href":"https:\/\/cs4760.csl.mtu.edu\/2018\/wp-json\/wp\/v2\/pages\/2192\/revisions"}],"predecessor-version":[{"id":2203,"href":"https:\/\/cs4760.csl.mtu.edu\/2018\/wp-json\/wp\/v2\/pages\/2192\/revisions\/2203"}],"wp:attachment":[{"href":"https:\/\/cs4760.csl.mtu.edu\/2018\/wp-json\/wp\/v2\/media?parent=2192"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}