Aging CoderJekyll2018-07-13T08:29:40-07:00http://agingcoder.com/Kris Ericksonhttp://agingcoder.com/kristian.erickson@gmail.comhttp://agingcoder.com/programming/2018/07/11/three-more-years-of-phonegapcordova-lessons2018-07-11T00:00:00-07:002018-07-11T00:00:00-07:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<p>Has it really been more than three years since I wrote <a href="/programming/2015/02/21/lessons-learned-from-5-years-of-phonegapcordova-development/">Lessons Learned</a>, hard to believe. So much time
has past and yet it is definitely still the most viewed article on this website. After re-reading it I felt it was
time to update its recommendations to be more recent, and add some new ones. Also if you find that post interesting,
there are a couple other articles on PhoneGap/Cordova on this website that you might find useful:</p>
<ul>
<li><a href="/programming/2016/09/03/should-i-use-cordovaphonegap-or-go-native/">Should I use Cordova / Phonegap or go Native?</a>, although a couple of years old is still a valid articles on the benefits of Native or Phone Gap. I feel that with the rise of modern JavaScript (which you can actually use due to pre-processors like Babel) and TypeScript Hybrid apps have become much more maintainable and a reasonable choice for larger projects.</li>
<li><a href="http://kriserickson.com/phonegap/2016/03/20/phonegap-be-wary-of-plugins/">Phonegap, be Wary of Plugins</a>, plugins are the best and worst part of Cordova. Some tips for choosing and using them.</li>
</ul>
<p>I kind of see this series of articles as having the following arc, this article will take a brief look at what has changed
since I wrote the last article and review the suggestions (lessons as I called them) to see if they are still valid advice.
Article two will be some more tips (mostly for people starting out with Cordova) that I have learned in the past few years.
And article three will be some of the hard learned pain points from maintaining a large cordova codebase. So lets begin:</p>
<h3 id="lesson-1---node-is-your-best-friend">Lesson 1 - Node is your best friend.</h3>
<p>One of the points I was trying to make here, which was never clearly spelled out is that you should probably do some
pre-processing on your code and use some form of node build tool (whether that be <a href="http://gulpjs.com/">gulp</a>, <a href="https://gruntjs.com/">grunt</a>,
<a href="http://broccolijs.com/">brocolli</a>, or just a set of npm scripts in a build directory). I didn’t speak of <a href="https://webpack.js.org/">webpack</a>
back then, as it wasn’t really on my radar but you might consider it as an option as well. I would definitely recommend
you organize your project directory something like:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>+.git
+.idea (I always use WebStorm for Cordova Development, but VsCode is great too)
+app
+-+css
+-+js
+--cordova.js (I genally write a cordova shim for when running in the apps directory).
+--index.html
+cordova
+-+node_modules
+-+platforms
+-+plugins
+-+res
+-+scripts
+-+www
+--build.json (see in Article 2)
+--config.xml
+--package.json
+dist (sometimes required for grunt and non-streaming build systems, this is just a scratch directory).
+node_modules
+tasks
-.babelrc (ignore if you are using typescript)
-.eslintrc.js (ignore if you are using typescript)
-.gitignore
-changelog.md
-Gruntfile.js (ignore if you are using gulp)
-gulpfile.js (ignore if you are using grunt)
-package.json
-README.md (always include readme instructions, future you will appreciate it).
-tsconfig.json (if you are using typescript, ignore if Javascript or Flow)
-tslint.json (if you are using typescript, ignore if Javascript or Flow)
</code></pre>
</div>
<p>Your pre-processing script should then output into cordova\www. We will speak more about suggestions for build tooling and other build and JavaScript/Typescript hints in the following articles in this series. But after 8 years of developing on cordova I feel it is a bad idea to develop directly in the
cordova directory.</p>
<h3 id="lesson-2---node-is-your-worst-enemy">Lesson 2 - Node is your worst enemy.</h3>
<p>Node (and especially Node on windows) is in a much better place than it was 3 years ago (good grief, we were talking about
node v0.12 and the mess that <a href="https://iojs.org/">io.js</a> was). Most (not all) of the windows issues are solved, and unless
you want to do some image minification (using <a href="https://github.com/lovell/sharp">sharp</a> or <a href="https://github.com/Automattic/node-canvas">node-canvas</a>
or <a href="https://www.npmjs.com/package/jimp">jimp</a>) you should have no problems running on Windows – with one exception.</p>
<p>Node on windows has tendency to lock files that either Cordova or Gradle need to build (this happens sometimes on
on OSX, but it is much rarer) - frequently running <code class="highlighter-rouge">cordova build</code> again will solve the problem, but sometimes
you have to <a href="https://superuser.com/a/643312/1462">kill the node process</a> that is locking the file.</p>
<p>Also, node has got much more aggressive with unhandled excpetions in Promises, meaning that the cordova tooling error
reports are hidden in a mess of node promise rejection errors. Here, for example, is an attempt at running cordova in the wrong
directory:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>>cordova run android
(node:20692) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): CordovaError: Current working directory is not a Cordova-based project.
(node:20692) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
</code></pre>
</div>
<p>The error is there (<strong>CordovaError: Current working directory is not a Cordova-based project.</strong>), but you have to look for it and
know where it is going to be. Sometimes you don’t get an error, just a rejection.</p>
<p>Last important thing to note about node (and this is true for all node projects, not just cordova) is that the versioning
in node can cause a lot of heartache (especially if you are on a team). The default behavior for <code class="highlighter-rouge">npm install --save</code>
is to add a ^ to the version number – in the case of a lot of immature node modules this is insanity. For example, if you
install <a href="https://www.npmjs.com/package/gulp-cordova-builder">gulp-cordova-builder</a> (I know nothing about this project, and have
never used it, but it has a very low version number of 0.0.1 which is appropriate for out example) through the npm command
line <code class="highlighter-rouge">npm install gulp-cordova-builder --save</code> it will get saved into your package.json as <code class="highlighter-rouge">"gulp-cordova-builder" : "^0.0.1"</code>.
This means that if you update npm at some time, or someone else checks out your project and it’s version has updated from 0.0.1 to
0.1.0 (which allows for some breaking changes in SemVer as any project under version 1.0.0 can change anything for at any time as
it is not stable, and that is assuming the authors are respecting SemVer) you will get a new version of the package installed.
This can lead to huge debugging issues where projects are not the same. I would recommend that, unless you are working by
yourself and you know when you do an <code class="highlighter-rouge">npm update</code> and what effects that may have, remove all the ~,^,>, etc modifiers from
your npm packages and only upgrade those packages explicitly, you will save your self a bunch of hair pulling. If you don’t
at least commit the <a href="https://docs.npmjs.com/files/package-lock.json">package-lock.json</a> so that you all start in the same place.</p>
<h3 id="lesson-3---test-on-real-devices-but-develop-in-chrome">Lesson 3 - Test on real devices but develop in Chrome.</h3>
<p>Chrome Dev tools have only gotten better over time, so spend the time to learn as many of their features as possible (most importantly
you should know about <a href="https://developers.google.com/web/tools/chrome-devtools/device-mode/">Device Emulation</a>, but the more
you know about Dev Tools the better. Personally I would read the “What’s New in Chrome Dev Tools” from
<a href="https://developers.google.com/web/updates/2017/03/devtools-release-notes">58</a>, and watch all the <a href="https://developers.google.com/web/updates/2017/04/devtools-release-notes">videos</a>
(they are only 5 minutes or so) from that point on. I learned a ton of features I didn’t know about (and now have come quite
handy with Run Command, if you don’t know about that, hit Control/Command Shift-P you will see a list of all the
commands you can perform from “Capture Screenshot” to “Help” which shows the latest Release Notes).</p>
<p>I usually run a simple node <a href="https://www.npmjs.com/package/http-server">http-server</a> in the <code class="highlighter-rouge">app</code> directory (where I have a mocked
version of cordova.js), but you can also look at using the browser platform now for debugging:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>>cordova platform add browser
>cordova run browser
</code></pre>
</div>
<p>Most of the plugins do not fully support browser, so it is not as useful as it could be, and frequently just mocking the
cordova.js in your app directory like this:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">window</span><span class="p">.</span><span class="nx">Connection</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">Connection</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">UNKNOWN</span><span class="p">:</span> <span class="s1">'unknown'</span><span class="p">,</span>
<span class="na">ETHERNET</span><span class="p">:</span> <span class="s1">'ethernet'</span><span class="p">,</span>
<span class="na">WIFI</span><span class="p">:</span> <span class="s1">'wifi'</span><span class="p">,</span>
<span class="na">CELL_2G</span><span class="p">:</span> <span class="s1">'2g'</span><span class="p">,</span>
<span class="na">CELL_3G</span><span class="p">:</span> <span class="s1">'3g'</span><span class="p">,</span>
<span class="na">CELL_4G</span><span class="p">:</span> <span class="s1">'4g'</span><span class="p">,</span>
<span class="na">NONE</span><span class="p">:</span> <span class="s1">'none'</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">navigator</span><span class="p">.</span><span class="nx">connection</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">navigator</span><span class="p">.</span><span class="nx">connection</span> <span class="o">=</span> <span class="p">{</span><span class="na">type</span><span class="p">:</span> <span class="nb">window</span><span class="p">.</span><span class="nx">Connection</span><span class="p">.</span><span class="nx">ETHERNET</span><span class="p">};</span>
<span class="p">}</span>
<span class="nx">navigator</span><span class="p">.</span><span class="nx">app</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">exitApp</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'App exited'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">window</span><span class="p">.</span><span class="nx">device</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">device</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">name</span><span class="p">:</span> <span class="s1">'DeviceBrowser'</span><span class="p">,</span>
<span class="na">model</span><span class="p">:</span> <span class="s1">'Browser'</span><span class="p">,</span>
<span class="na">version</span><span class="p">:</span> <span class="s1">'7.1'</span><span class="p">,</span>
<span class="na">platform</span><span class="p">:</span> <span class="s1">'Browser'</span><span class="p">,</span>
<span class="na">uuid</span><span class="p">:</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">cordovaFakeUUID</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">cordova</span><span class="p">.</span><span class="nx">InAppBrowser</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">open</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">target</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">target</span><span class="p">,</span> <span class="nx">options</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'DOMContentLoaded'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">getScript</span><span class="p">(</span><span class="s1">'js/fake-camera-gallery.js'</span><span class="p">);</span>
<span class="nx">setTimeout</span><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">cordova</span><span class="p">.</span><span class="nx">fireDocumentEvent</span><span class="p">(</span><span class="s1">'deviceready'</span><span class="p">);</span>
<span class="p">},</span> <span class="mi">500</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">})();</span></code></pre></figure>
<p>will produce results similar to the browser platform, but all for faster reloads as you don’t have to do</p>
<div class="highlighter-rouge"><pre class="highlight"><code>>cordova run browser
</code></pre>
</div>
<p>every time you want to reload the code. Of course this requirement can be avoided by using <a href="https://www.npmjs.com/package/cordova-plugin-browsersync">cordova-browser-sync</a>
with the –live-reload option, or <a href="http://docs.phonegap.com/references/phonegap-cli/serve/"><code class="highlighter-rouge">phonegap serve</code></a>,
but if you are taking my advice and working in the apps directory this requires you push a build from the apps directory
to your cordova/www every time you want to update.</p>
<p>As for the testing on real devices, that is still definitely true. Test on as many devices as you can find, and try to test
on all the major platforms and versions you are targeting. Of course unless you are a large development shop, this will be
very difficult but the more testing you do on different devices before you release the less bugs you will have to track down
after you release.</p>
<h3 id="lesson-4---genymotion-for-android-development-is-awesome">Lesson 4 - Genymotion for Android Development is awesome</h3>
<p><a href="https://www.genymotion.com/">Genymotion</a> was great compared to the old Android Emulator, but Google has improved the
Android emulator so much that it isn’t necessary any more. <a href="https://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager-intel-haxm">Haxm</a>
is installed automatically when you install Android Studio these days, and the new Emulator is fast, has almost all the
features of GenyMotion. You might want to turn Snapshots off in the Emulator:</p>
<p><img src="/img/cordova-more/turn_off_snapshots.gif" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>as they seem to sometimes cause problems, but other than that, ignore Lesson 4 from 2015.</p>
<h3 id="lesson-5---multiple-osx-machines">Lesson 5 - Multiple OSX Machines</h3>
<p>Yeah, you pretty much need an OSX machine if you plan to release an iOS verson of your app,
<a href="https://www.genuitec.com/products/gapdebug-end-of-life-notice/">GapDebug</a> is long gone and the
<a href="https://github.com/artygus/ios-webkit-debug-proxy-win32">ios-webkit-debug-proxy-win32</a> is no longer maintained
and doesn’t work on Windows 10 or with iOS 11. So if you are planning to release an iOS version of your app, you need to either
use something like <a href="https://www.macincloud.com/">MacInCloud</a>, which is at best a painful experience, or your own OSX machine.
If you have a ton of money to burn, you could buy an iMac Pro (at $5000K plus), or a 2015 Macbook Pro (I can’t recommend
the new MacBook Pros due to their <a href="https://www.imore.com/macbook-pro-butterfly-keyboard-effect">innate keyboard issues</a>), or you
can pick up an old 2012 Mac Mini (don’t get the currently for sale 2014 version as they are not upgradable) off of EBay and
upgrade the RAM and replace the hard drive with an SSD (you can probably get a reasonable Mac Mini with 16GB of RAM and a 256GB SSD for under a grand this way if you are willing to do a little work).</p>
<p>Another solution, if you don’t have a Mac, is to use a build service like <a href="https://build.phonegap.com/">PhoneGap Build</a>,
or <a href="https://ionicframework.com/pro">Ionic Pro</a> or <a href="https://monaca.io/">Monaca</a>. Telerik seems to have abandoned their
<a href="https://www.telerik.com/platform-next-level">build platform</a>, to focus on NativeScript so this may not be the most profitable
SAAS to be in. However, even if you are building your Apps in the Cloud, you will need some way to interact with debugger
with them (unless you somehow manage to write to write perfect code, and all the underlying plugins are perfect, and there
are no issues with Safari or Cordova on iOS) so I recommend having at least one Mac that can run a modern version of XCode.</p>
<h3 id="lesson-6---remote-device-debugging">Lesson 6 - Remote Device Debugging</h3>
<p>No question about it, it was new in 2015, but Remote Device debugging is a necessity. Get to know the url <a href="chrome://inspect/#devices">chrome://inspect/#devices</a>,
I have it on my Bookmark bar, as it is not totally obvious how to get the device tab without the url. Yes you can get their by
going to the Developer Tools, Hitting the Hamburger menu, Hitting more Tools, and Selecting Remote Devices:</p>
<p><img src="/img/cordova-more/device_inspector.gif" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>but the Url is far more convenient.</p>
<p><a href="https://people.apache.org/~pmuellr/weinre/docs/1.x/1.5.0/Home.html">Weinre</a>, thank god, has become a thing of the past that hopefully we will never have to deal with again.
It’s something interesting to know about, and I have been forced to use it to debug release builds (where I hid it behind a long
press) that just wouldn’t replicate in debug build mode (the Remote Debugging is not available as soon as you make a release build,
which generally is a good thing.</p>
<p>Also, if you, like me, find the Safari Web Inspector basically unusable (there still is no way to pick a file except with the
alphabetical list in the Debugger or Resources Panel) you can debug in Chrome with the awesome
<a href="https://github.com/RemoteDebug/remotedebug-ios-webkit-adapter">remotedebug-ios-webkit-adapter</a> (there are pretty long and involved,
but clear instructions in the Readme on how to get it set up and running). It doesn’t work perfectly – some minor features are unavailable
or don’t work 100% (but they do take pull requests to improve the experience), but does a pretty amazing job for what it is.</p>
<h3 id="lesson-7---avoid-loading-the-platform-project-in-an-ide">Lesson 7 - Avoid loading the Platform project in an IDE</h3>
<p>This was true in 2015, as back then Cordova was using the deprecated <a href="https://ant.apache.org/">Ant</a> for building on Android
(it hadn’t migrated to <a href="https://gradle.org/">gradle</a> yet) and caused problems in XCode. These days PhoneGap plays much better with both platforms
(though on Android, the ADB and gradle daemon that Cordova uses can conflict with Android Studio, so you may have to do a
clean and/or shutdown of Android Studio to get builds to work) so as long as you follow lesson 8, feel free to open your project
in your IDE as it now rarely causes problems.</p>
<p>In fact, I find that you really need to break out the IDE to get release builds to work on XCode (the code signing for iOS is one of the
few places where I practice cargo cultish rituals to hopefully get a signed build out that iTunes Connect will accept). And sometimes
on Android you have to break out Android Studio to determine why a build is failing (Gradle or the cordova tools will give some
cryptic error that Android Studio will have a quick fix for, or at least have a more expressive and understandable error message).</p>
<h3 id="lesson-8---always-be-prepared-to-blow-away-a-platform">Lesson 8 - Always be prepared to blow away a platform</h3>
<p>Good advice then, still good advice now. At some point yoo will need to blow away a platform and you shouldn’t worry that you will lose changes when you do so.</p>
<p>If you have to change the code (AndroidManifest.xml, build.gradle, some *-Info.plist), do
it in a <a href="https://cordova.apache.org/docs/en/latest/guide/appdev/hooks/">hook</a> rather than manually changing a file. Hooks have
changed a fair bit over the past couple of years, but I believe that the correct way to trigger them now is to add a hook node
in your config xml with the hook type (before_build, before_compile,etc ), the old way of having a directory with magically named
directories and script files is thankfully gone. However, in the various articles you may see reference to this old way
of triggering hooks (for example in this <a href="https://www.thepolyglotdeveloper.com/2015/01/hooks-apache-cordova-mobile-applications/">Tutorial on Hooks</a>,
and in <a href="http://devgirl.org/2013/11/12/three-hooks-your-cordovaphonegap-project-needs/">Holly Schinsky’s Classic “Three hooks your Cordova/Phonegap project needs</a>)
they both reference the directory approach to hooks, just take those same scripts and throw them into a scripts directory and reference them in
your config.xml (as described in the guide). One thing the guide is unfortunately brief about is the context object that gets passed into <br />
the function. It basically contains two important features, opts which is an object that looks like:</p>
<div class="highlighter-rouge"><pre class="highlight"><code>opts: {
projectRoot: string;
cordova: {
platforms: string[];
plugins: string[];
version: string;
};
}
</code></pre>
</div>
<p>and the requireCordovaModule function, which is basically node require but for the cordova modules (you can get access to fs, path, q, glob, etc). See
<a href="https://github.com/Microsoft/cordova-samples/blob/master/cordova-hooks/HooksSample/hooks/hook-symlink-fix.js">hook-symlink-fix.js</a> for a well
written example of a more complex but modernish (ok it’s from 2015 when Microsoft was still all in on Cordova) hook.</p>
<h3 id="lesson-9---use-as-few-plugins-as-possible">Lesson 9 - Use as Few Plugins As Possible</h3>
<p>Also still good advice, plugins tend to be fragile (even the ones supported by Adobe/Apache that are part of cordova, though they
tend to be the best of the bunch). See <a href="http://kriserickson.com/phonegap/2016/03/20/phonegap-be-wary-of-plugins/">Phonegap, be Wary of Plugins</a>,
for the pain I have experienced myself. Don’t get me wrong, plugins are definitely one of Cordova’s strengths, but they
each one you add adds potential for someone elses code to go wrong. Frequently writers of a plugin (unless they are a team),
will have expertise in one platform and only a passing familiarity with the other platform and this can cause plugins to work
great on one platform but poorly or cause issues on another platform. Just to do a TLDR on the linked to article: before
including a plugin look at how many people are using the plugin on npm (for example at the time of writing about 800 people
a week are downloading <a href="https://www.npmjs.com/package/cordova-plugin-inapppurchase">cordova-pluign-inapppurcahse</a>, while only
20 are using <a href="https://www.npmjs.com/package/cordova-plugin-purchase">cordova-plugin-purchase</a> which leads me to think
that inapppurchase is probably the way to go). Go over the current <a href="https://github.com/AlexDisler/cordova-plugin-inapppurchase/issues">issues in GitHub</a>
and notice that it has 116 open issues (again at the time of writing). One of the other <a href="https://www.npmjs.com/package/cc.fovea.cordova.purchase">purchase plugins</a>
also has around 800 weekly downloads, but only has <a href="https://github.com/j3k0/cordova-plugin-purchase/issues">26 open issues</a>. If you look at the two issue trackers, you will see
that one has fewer total issues, but less closed issues and the other has far more issues, but only a few open issues. Without
spending some time in those issue trackers you will have no idea whether one developer just doesn’t clear out the bogus
issues fast enough and both plugins are of the same quality, or one of the plugins really has more active than the other.</p>
<p>Mix that with the possibility that Libraries (for example the android support version that the plugin requires on Android, or the CocoaPod on
iOS) will conflict and you will realize that the more plugins you add, the chance of these conflicts expands exponentially. Right
now I am fighting with the fact that the published version of the <a href="https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-camera/">Cordova Camera Plugin</a>,
version 4.0.3, published 3 months ago requires the support library 24 (from over 2 years ago), while another <a href="https://github.com/dpa99c/cordova-diagnostic-plugin">plugin I am using</a>
requires support version 26 and thus the app requires support libraries and breaks during the build process. The @head
version of the camera plugin uses support library 27 (which doesn’t help, and there is no tagged version of camera that
uses support library 26). So to get the current build to work you have to manually change the app/build.gradle file,
which breaks one of my rules (Always be prepared to blow away a platform), and means that I can’t use automation to
make the build. The solution is either to figure out a way to change app/build.gradle (which has been unsuccessful so
far as no matter where I seem to put the hook the build.gradle file is getting written over), or forking the camera plugin and
hard coding support version 26 in and then going through this mess again when either plugin updates.</p>
<p>And that is kind of the problem: deciding on which plugin to use, even with tools like <a href="https://cordova.apache.org/plugins/">Cordova’s Plugin Registry</a>
and the quickly Sherlocked <a href="http://www.plugreg.com/">PlugReg</a>, is a time consuming and fraught process. Picking plugins
from the core Cordova team makes that less fraught, but I have fought with bugs in the
<a href="https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-media/">Cordova Media Player Plugin</a> as well as the
<a href="https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-camera/">Cordova Camera Plugin</a> that never should have been
released and yet remained the published plugin for months.</p>
<h3 id="lesson-10---dont-write-your-own-framework">Lesson 10 - Don’t write your own Framework</h3>
<p>Even more true today than in 2015. There was a time when it would have made sense, but we are so past that time that you
would have to be a masochist to develop your own Framework now. If you are in the Angular world, use <a href="https://ionicframework.com/">Ionic</a>,
or if you (like me) prefer Vue look at <a href="https://quasar-framework.org/">Quasar</a>, <a href="https://framework7.io/vue/">Framework7 Vue</a>, or <a href="https://onsen.io/vue/">Onsen</a>.
If you like React then look at <a href="https://onsen.io/react/">OnSen</a>, or <a href="https://framework7.io/react/">Framework7 React</a> and if you like
kicking it Old School without having a binding framework as such, you can go with the standard <a href="https://framework7.io/docs/">Framework7</a>.</p>
<p>Personally I am using Framework7 Vue for my greenfield projects now, and mostly loving it (no framework is perfect, and
some of the issues merging Vue with Framework7 cause some feelings of Ugh, but it is bar far the best framework I have
used so far).</p>
<p>The other thing I would add about Frameworks, is that they evolve over time, and unless you want to spend all of your maintenance
work in a world of pain, I would try to keep your code up to date as the framework changes. For Ionic this is especially painful
as the shift from AngularJS to Angular2/3/4/5/6/7 whatever they call it now was basically a rewrite.</p>
<h2 id="conclusion">Conclusion</h2>
<p>As we have seen, some of these tips/lessons have held up better over time and some have not. Hopefully updating this has made
it a bit more relevant for today and help you build awesome apps in Cordova.</p>
<p><a href="http://agingcoder.com/programming/2018/07/11/three-more-years-of-phonegapcordova-lessons/">Three More Years of PhoneGap/Cordova Lessons</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on July 11, 2018.</p>http://agingcoder.com/programming/2018/07/04/react-native-a-tempting-quagmire2018-07-04T00:00:00-07:002018-07-04T00:00:00-07:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<p>A recent experience being asked to look into a React Native mobile has clarified some of the questions I brought up
in <a href="/programming/2015/11/07/a-quick-dive-into-react-native/">my last aritcle on React Native</a>, lo those 2 1/2 years ago.
The app was crashing, and not functioning as expected and I was asked if I could look over it and see whether it could
salvaged or was going to require a complete rewrite. I explained that I hadn’t really done much in <a href="https://facebook.github.io/react-native/">React Native</a> other than write a few toy apps, but I would be happy to see what could be done. So I installed the latest version of the react-native cli, and dug in.</p>
<p>The app itself was not very well written, it looked like an app that was written by programmers (and there were multiple
contributors, or at least the development company made it look like there were multiple developers from the Git commits) <br />
who were learning both React and React Native while they wrote the app, so the instability was more a reflection of that
than React Native itself (although the crashing was only really happening in release builds and not in the dev build). It was
hardly a complicated app (it really only consisted of 4 screens including the login screen, and only had 7 server side API calls total).
The developers used both an event bus, and a very simple home rolled store (not <a href="https://facebook.github.io/flux/">Flux</a>) that wasn’t properly reactive (which was causing
a lot of the State violation troubles which caused the app to crash).</p>
<p>Since this isn’t an article about fixing a React Native app, or an article on how not to write React Native app so I won’t
go into too much detail about that App, and how I fixed the seven or eight issues that the client had with the app.<br />
Service to say that the majority of the problems were resolved by adding error handling to the build (in an attempt to
discover were things were crashing for users) and the client is happy enough that
the app isn’t crashing as much and that they aren’t willing to spend the extra time and money to fix the crashes so long as they don’t
take the App down (I know yikes) – and are probably going to just do a rewrite rather than invest more time and money
in the React Native route.</p>
<p>In spending just a few hours fixing these issues I came to understand a fair bit more real world use of React Native, however,
and I thought I would share my findings of what was good and bad about React Native in my brief experience. These are the
experiences of someone who isn’t developing an App from scratch, or who has spent a long time working with React Native, but
someone who spent a short amount of time in React Native with the specific purpose of fixing a few bugs and creating a duct
tape solution to get the client up and running again. For a full featured look at what works in React Native and what
doesn’t in a large team I highly recommend the <a href="https://medium.com/airbnb-engineering/react-native-at-airbnb-f95aa460be1c">Blog Post on AirBnb and their experiences with React Native</a>,
as well as the two <a href="http://fragmentedpodcast.com/">Fragment Podcasts</a> with <a href="https://twitter.com/gpeal8">Gabriel Peal</a> about
the decision at AirBnb – <a href="http://fragmentedpodcast.com/episodes/129/">Part 1</a> and <a href="http://fragmentedpodcast.com/episodes/130/">Part 2</a>. AirBnb had
a lot of experience with React Native (in fact, one of the first things I did with codebase was get eslint working and use the
<a href="https://www.npmjs.com/package/babel-preset-airbnb">AirBnd React preset</a>, which helped find a fix a ton of bugs in the
codebase).</p>
<h1 id="the-good">The Good</h1>
<ul>
<li>Once the app is up and running, being able to change things very quickly through hot-reload is very nice (though that feature is shared with <a href="https://www.nativescript.org/">NativeScript</a>, and <a href="https://cordova.apache.org">Cordova</a> through a <a href="https://github.com/nparashuram/cordova-plugin-browsersync">plugin</a>.</li>
<li><a href="https://reactjs.org/">React</a> itself is a nice framework, that is well designed and relatively easy to learn.</li>
<li>Although not as nice as <a href="https://www.typescriptlang.org/">Typescript</a>, <a href="https://flow.org/en/">Flow</a> is better than plain Javascript.</li>
<li>Nice to have an alternative to Cordova or Nativescript for multiplatform development.</li>
<li>Because of WebPack and Babel you are writing in a modern version of JavaScript.</li>
<li>It has the backing of Facebook (which is still using it internally), and there are quite a few high profile <a href="https://brainhub.eu/blog/famous-apps-built-with-react-native/">apps being developed in React Native</a>.</li>
</ul>
<h1 id="the-bad">The Bad</h1>
<ul>
<li>The tooling is still pretty immature, potentially because I was using a version of React (not the react-client, but the library included with the app) that was a year old, but it was very fragile.</li>
<li>You constantly had to build and build again because of locked files or random build failures.</li>
<li>I never successfully was able to add a plugin “automatically”, I always had to manually</li>
<li>Simple things like starting the emulator were not done (in Cordova if there is no device and no emulator running, run android is smart enough to start the emulator) where on React Native it just fails and tells you start the emulator before running again (the react-native run-android or react-native run ios command can take up to 5 minutes).</li>
<li>The debugger mostly works but is kind of Ugh (if you haven’t used it, what happens is a browser window is launched and then you debug that browser window, the only part that works is Sources and then you have to click on debuggerWorker.js and localhost:8080). If you can get the dev-tools to work you can also view the component hierarchy in a seperate app. If you want to debug things like Network on Android you have to install <a href="http://facebook.github.io/stetho/">Stetho</a> or manage to get the React Native debugger to work (see below).</li>
<li>I never managed to get the <a href="https://github.com/jhen0409/react-native-debugger">React Native debugger</a> to work on Windows, it would launch but never connect to the App, I had a little more success with the react-devtools but it was more miss than hit in getting it to connect to the app.</li>
<li>Upgrading versions is not simple. The build I had was stuck at React Native 0.46 (and React 016.0.0-alpha.12), ReactNative (as of July 1st, 2018) is currently at 0.56. I tried blindly updating everything to the latest of everything and nothing worked. I spent a bunch of time slowly bumping the versions, and seeing where I could get, but each clean and release takes about 10 minutes and figuring out a magic combination of what works. The suggested approach is to go through every release notes (here are the release notes for <a href="https://github.com/react-native-community/react-native-releases/blob/master/CHANGELOG.md#056">React-Native 56</a> all 2500 words) for every version and update. If you are developing React-Native you basically have to update your app once a month or you will get to a very bad place pretty quickly.</li>
<li>The plugins are not linked to a specific version of React-Native, but you will find that some plugins do not work with certain versions of React and if someone stops supporting a plugin then the plugin will become unusable proably after 6 months or so.</li>
<li>The plugins are all linked from the <code class="highlighter-rouge">node_modules</code> directory, this causes problems on Windows, and a lot of the fixes for various problems relied on editing Java and Objective C files linked in the node_modules directory which I never did since it seemed like such a bad idea.</li>
<li>Babel’s handling of async and other modern Javascript features make it very difficult to Debug (async for example get turned into
giant switch statements, variables get renamed, the handling of this is not as consitent as TypeScript). I tried to follow <a href="https://twitter.com/kuizinas">Gajus Kuizinas</a> suggestion and <a href="https://medium.com/@gajus/dont-use-babel-transpilers-when-debugging-an-application-890ee528a5b3">not use Babel Transpilers when debugging</a> but because the app was written in Flow rather than JavaScipt and since the react-native packager is black box to me, I never managed to do this.</li>
<li>The app behaves differently in Development mode and Release mode. Very differently, almost all the crashes that were being reported by the client only happened in Release mode. This may be because of react-native pacakger running in two modes, but more probably because when you debug the app in Chrome you are running in Chrome’s V8 engine and not the V8 engine that is packaged on Android with React. This might not be true on iOS, since (as seen below) I never managed to get a Release app built.</li>
<li>Once again, maybe it was the version I was stuck working with, but the App was most of the time a slideshow in the Android Emulator. You would click and as much as 2 minutes would pass before the app responded, and change screen animation could take over a minute. I found claims that this was fixed in .52, but other claims that it was still happening (and yes I am using the Haxm Emulator that runs everything else just fine). The app worked fine on a real device, however.</li>
<li>Also when debugging on Android there was an error that happened whenever you clicked on a button (well, technically a TouchableHighlight) you got an error <a href="https://github.com/facebook/react-native/issues/5823">Attempted to transition from state <code class="highlighter-rouge">RESPONDER_INACTIVE_PRESS_IN</code> to <code class="highlighter-rouge">RESPONDER_ACTIVE_LONG_PRESS_IN</code>, which is not supported.</a> which was apparently fixable by reseting the clock on your phone to exactly match your computer but I never got it to work.</li>
<li>I don’t know whether it is Apple and XCode’s fault, or ReactNative’s fault, but you can’t <a href="https://github.com/facebook/react-native/issues/18638">make a release build</a> (debug builds work fine) on version 9.3+ of Xcode and ReactNative less than 54. The solution is to either upgrade (see upgrading above), or downgrade your version of XCode to 9.2.</li>
<li>Compared to Cordova you really have to be willing to go into the various toolings (Android Studio and edit Gradle and Android Manifest files, and even some Java Code, and Xcode and
tweak a bunch of settings in the Gui or edit PList files). Since I never managed to get an upgrade to work (I ended having to <code class="highlighter-rouge">git reset</code> to get back when I attempted upgrading), I don’t know how editing these files plays with <a href="https://facebook.github.io/react-native/docs/upgrading.html"><code class="highlighter-rouge">react-native-git-upgrade</code></a>, but I can imagine the mess of conflicts that will produce.</li>
<li>Also unlike Cordova you won’t be able to re-use most of ReactNative UI in a <a href="https://developers.google.com/web/progressive-web-apps/">PWA</a>, because the components
are unique to ReactNative (though it looks like there is at least <a href="https://github.com/necolas/react-native-web">one project</a> that is trying to rectify this).</li>
</ul>
<h1 id="tips">Tips</h1>
<ul>
<li>Get a bug reporting system working asap, as you will probably have bugs that only happen in Release Mode and other
spotty reporting in LogCat and the XCode console you will have no way to figure out what is going on. I used
<a href="https://github.com/corymsmith/react-native-fabric">React Native Fabric</a>, mostly because I am familiar with Fabric
and it is free. You will also probably need a <a href="https://github.com/mozilla/source-map">sourcemap</a> tool to convert from
the single WebPack file to something more legible. I used <a href="https://github.com/master-atul/react-native-exception-handler">React Native Exception Handler</a> to
catch uncaught errors (because it caught native errors too), but you can probably get away with just using the
ErrorUtils.setGlobalHandler. Also if you are on Android and are rethrowing the caught Exception, you need to do that
on a setTimeout of at least 1 second as you will lose the Error Report into the ether otherwise – React Native crashes
hard.</li>
<li>Use a linter (as I said before, I used the AirBnb linter to React), it will catch a lot of stupid errors, if I had
time and this was a bigger project I would have converted to TypeScript but if not at least get the Flow that react native
projects ship with.</li>
<li>Test in release mode often, I had gotten used to Dev and Release performing similarly and they really don’t in React native (especially
if you are debugging in Chrome – remember they are different Javascript Runtimes). You don’t have to do an actual build,
but <code class="highlighter-rouge">react-native run-android --variant=release</code> will put on your device a build that isn’t the hot-reloading, debug
enabled build.</li>
<li>If you are having trouble with Android builds, clean with:</li>
</ul>
<div class="highlighter-rouge"><pre class="highlight"><code>cd android
gradlew clean
</code></pre>
</div>
<p>I spent a bunch of time trying to get a build to work, only to find that by cleaning everything worked fine.
* If the JS Debugger isn’t attaching, I find that killing the App on the mobile device and restarting frequently solved
that problem (also the problem of the app just having a White Screen).
* Stay on the latest version, if you get behind you are in for a world of hurt, I am pretty sure that a lot of the pain
I had with React Native was because of versioning issues, or bugs that had been fixed for months but I was seeing because
I was on a year old version of React Native.</p>
<h1 id="conclusion">Conclusion</h1>
<p>React Native is still an interesting emergent technology, but it is not a Silver Bullet for turning Web Developers into Mobile
Developers. Since it so raw around edges when dealing with the Native bits, you really need to have a Native Expert willing
to splunk into the guts of React Native on each platform to be successful. My guidance would be, if you have a large
team of Web Developers and a couple Mobile Developers for each platform and you plan to do
<a href="https://en.wikipedia.org/wiki/Continuous_delivery">Continuous Delivery</a> then React Native may be a good fit for your
company. If you are a small shop, or are creating a small app, I would recommend sticking with more traditional
methods like going full Native or going with a more more Cross Platform solution like <a href="https://cordova.apache.org">Cordova</a>
or <a href="http://xamarin.com/">Xamarin</a>.</p>
<p><a href="http://agingcoder.com/programming/2018/07/04/react-native-a-tempting-quagmire/">React Native -- A Tempting Quagmire</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on July 04, 2018.</p>http://agingcoder.com/programming/2018/02/24/creating-a-google-home-app-part-3-refactoring-and-scoring2018-02-24T00:00:00-08:002018-02-24T00:00:00-08:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<h3 id="introduction">Introduction</h3>
<p><strong>Note:</strong> This is the third article in a probably four part series, it will not make much sense without reading the other two:</p>
<ul>
<li><a href="https://agingcoder.com/programming/2018/02/04/creating-a-google-home-app/">Creating A Google Home App</a> where we get a very simple Google Home App up and running.</li>
<li><a href="https://agingcoder.com/programming/2018/02/08/creating-a-google-home-app-part-2-the-game/">Creating A Google Home App Part 2 - The Game</a> where we turn that simple app into a game.</li>
</ul>
<p>In the previous <a href="https://agingcoder.com/programming/2018/02/08/creating-a-google-home-app-part-2-the-game/">article</a> we converted our very simple app into a version of Rock, Paper, Scissors, Lizard, Spock. In this article we will refactor our game, and then add scoring so the app will keep track of your wins and losses (initially it will be per session, and eventually we will have it keep track for all time).</p>
<p>As we stated in the previous article, our if else engine for determining game winners was very ugly. Let’s replace it
with an array based engine, that will clean it up, reduce the amount of code, and remove a ton of hard coded strings. First we will create a pseudo enum with Object.freeze that has all of the 10 possible combinations of two people playing Rock, Paper, Scissors, Lizard, Spock (place this at the top of the rpsls.js file, after the options):</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">GAME_CHOICES</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">freeze</span><span class="p">({</span><span class="na">SCISSORS_VS_PAPER</span> <span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">PAPER_VS_ROCK</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">ROCK_VS_LIZARD</span><span class="p">:</span>
<span class="mi">2</span><span class="p">,</span> <span class="na">LIZARD_VS_SPOCK</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span> <span class="na">SPOCK_VS_SCISSOR</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span> <span class="na">SCISSOR_VS_LIZARD</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span> <span class="na">LIZARD_VS_PAPER</span><span class="p">:</span>
<span class="mi">6</span><span class="p">,</span> <span class="na">PAPER_VS_SPOCK</span><span class="p">:</span> <span class="mi">7</span><span class="p">,</span> <span class="na">SPOCK_VS_ROCK</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span> <span class="na">ROCK_VS_SCISSOR</span><span class="p">:</span> <span class="mi">9</span><span class="p">});</span></code></pre></figure>
<p>If you haven’t seen <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze">Object.freeze</a> before it is a new feature added to JavaScript 5.1, that allows us to force immutable objects (in case you hadn’t noticed in the past, const isn’t really const as
you can always add to or remove items from an array or object, with Object.freeze you will get an exception thrown if you try to mutate a frozen object). Javascript doesn’t really have enums, but this is pretty close. Next we are going to create an array of text outcomes based on our ‘enumeration’ (this also goes near the top of the rpsls.js file):</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">OUTCOME_DESCRIPTIONS</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SCISSORS_VS_PAPER</span><span class="p">]</span> <span class="p">:</span> <span class="s1">'Scissors cuts Paper.'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SCISSOR_VS_LIZARD</span><span class="p">]:</span> <span class="s1">'Scissors decapitates Lizard.'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">PAPER_VS_ROCK</span><span class="p">]</span> <span class="p">:</span> <span class="s1">'Paper covers Rock.'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">PAPER_VS_SPOCK</span><span class="p">]:</span> <span class="s1">'Paper disproves Spock.'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">LIZARD_VS_SPOCK</span><span class="p">]</span> <span class="p">:</span> <span class="s1">'Lizard poisons Spock'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">LIZARD_VS_PAPER</span><span class="p">]:</span> <span class="s1">'Lizard eats Paper.'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SPOCK_VS_SCISSOR</span><span class="p">]</span> <span class="p">:</span> <span class="s1">'Spock smashes Scissors.'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SPOCK_VS_ROCK</span><span class="p">]:</span> <span class="s1">'Spock vaporizes Rock.'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">ROCK_VS_LIZARD</span><span class="p">]</span> <span class="p">:</span><span class="s1">'Rock crushes Lizard'</span><span class="p">,</span>
<span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">ROCK_VS_SCISSOR</span><span class="p">]:</span> <span class="s1">'(and as it always has), Rock crushes Scissors.'</span>
<span class="p">};</span></code></pre></figure>
<p>This way instead of duplicating our results like we previously were, we can use the same text for either computer beating the player, or losing to the player. Of course we could freeze this text too, as it won’t change, but for reasons only known to the author at the time he wrote the code, we have decided not to.</p>
<p>Next, instead of repeating strings throughout the code (and having the chance of misspelling scissors as scisors or sissors somewhere), we turn all of the user choices into another frozen Object Collection (place this at the top of the rpsls.js file as well):</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">CHOICE_NAMES</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">freeze</span><span class="p">({</span><span class="na">ROCK</span><span class="p">:</span> <span class="s1">'rock'</span><span class="p">,</span> <span class="na">PAPER</span><span class="p">:</span> <span class="s1">'paper'</span><span class="p">,</span> <span class="na">SCISSOR</span><span class="p">:</span> <span class="s1">'scissors'</span><span class="p">,</span>
<span class="na">LIZARD</span><span class="p">:</span> <span class="s1">'lizard'</span><span class="p">,</span> <span class="na">SPOCK</span><span class="p">:</span> <span class="s1">'spock'</span><span class="p">});</span></code></pre></figure>
<p>Next we create out choice matrix, this basically produces all the possible choices and contains their output. The users choice is the first part of the key, and the computer’s choice the second. Each element in the collection contains an outcome which is a string, and win – a boolean that indicates whether the player won or lost. We are placing this matrix again near the top of the rpsls.js file:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">CHOICE_MATRIX</span> <span class="o">=</span> <span class="p">{</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">ROCK</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SCISSOR</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">ROCK_VS_SCISSOR</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">ROCK</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">LIZARD</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">ROCK_VS_LIZARD</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">ROCK</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">PAPER</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">PAPER_VS_ROCK</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">ROCK</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SPOCK</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SPOCK_VS_ROCK</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">PAPER</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">ROCK</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">PAPER_VS_ROCK</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">PAPER</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SPOCK</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">PAPER_VS_SPOCK</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">PAPER</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SCISSOR</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SCISSORS_VS_PAPER</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">PAPER</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">LIZARD</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">LIZARD_VS_PAPER</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SCISSOR</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">PAPER</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SCISSORS_VS_PAPER</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SCISSOR</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">LIZARD</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SCISSOR_VS_LIZARD</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SCISSOR</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">ROCK</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">ROCK_VS_SCISSOR</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SCISSOR</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SPOCK</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SPOCK_VS_SCISSOR</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">LIZARD</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SPOCK</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">LIZARD_VS_SPOCK</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">LIZARD</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">PAPER</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">LIZARD_VS_PAPER</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">LIZARD</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">ROCK</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">ROCK_VS_LIZARD</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">LIZARD</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SCISSOR</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SCISSOR_VS_LIZARD</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SPOCK</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SCISSOR</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SPOCK_VS_SCISSOR</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SPOCK</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">ROCK</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">SPOCK_VS_ROCK</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">true</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SPOCK</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">PAPER</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">PAPER_VS_SPOCK</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">[</span><span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">SPOCK</span> <span class="o">+</span> <span class="nx">CHOICE_NAMES</span><span class="p">.</span><span class="nx">LIZARD</span><span class="p">]:</span>
<span class="p">{</span><span class="na">outcome</span><span class="p">:</span> <span class="nx">OUTCOME_DESCRIPTIONS</span><span class="p">[</span><span class="nx">GAME_CHOICES</span><span class="p">.</span><span class="nx">LIZARD_VS_SPOCK</span><span class="p">],</span> <span class="na">win</span><span class="p">:</span> <span class="kc">false</span><span class="p">},</span>
<span class="p">};</span></code></pre></figure>
<p>Now we can greatly simplify our returnResults code:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">returnResults</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="nx">userChoice</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">computerChoice</span> <span class="o">=</span> <span class="nx">CHOICE_ARRAY</span><span class="p">[</span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">CHOICE_ARRAY</span><span class="p">.</span><span class="nx">length</span><span class="p">)];</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="nx">userChoice</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">></span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">I</span> <span class="nx">also</span> <span class="nx">picked</span> <span class="nx">$</span><span class="p">{</span><span class="nx">userChoice</span><span class="p">}.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Try</span> <span class="nx">again</span><span class="p">,</span> <span class="k">do</span> <span class="nx">you</span> <span class="nx">pick</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span> <span class="nx">or</span> <span class="nx">Spock</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/p</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/speak>`</span><span class="se">))</span><span class="err">;
</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">CHOICE_MATRIX</span><span class="p">[</span><span class="nx">userChoice</span> <span class="o">+</span> <span class="nx">computerChoice</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">userChoice</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Impossible choice: '</span> <span class="o">+</span> <span class="nx">userChoice</span><span class="p">);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="s1">'Something has gone wrong, you picked: '</span> <span class="o">+</span> <span class="nx">userChoice</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">let</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">buildList</span><span class="p">(</span><span class="s1">'Start Game or Instructions'</span><span class="p">);</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">addItems</span><span class="p">([</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_AGAIN</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Start'</span><span class="p">,</span> <span class="s1">'Yes'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="s1">'Again'</span><span class="p">),</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_END</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Stop'</span><span class="p">,</span> <span class="s1">'No'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="s1">'End'</span><span class="p">)</span>
<span class="p">]);</span>
<span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="s1">'<s>'</span> <span class="o">+</span> <span class="nx">result</span><span class="p">.</span><span class="nx">outcome</span> <span class="o">+</span> <span class="s1">'</s>'</span> <span class="o">+</span>
<span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="s1">'<s>You Won!</s>'</span> <span class="p">:</span> <span class="s1">'<s>You lost.</s>'</span><span class="p">);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">askWithList</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">><</span><span class="nx">p</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">message</span><span class="p">}</span><span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">play</span> <span class="nx">again</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">again</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">quit</span> <span class="nx">say</span> <span class="nx">end</span><span class="p">.</span><span class="o"><</span><span class="sr">/s></</span><span class="nx">p</span><span class="o">><</span><span class="sr">/speak>`</span><span class="se">)</span><span class="sr">, list</span><span class="se">)</span><span class="sr">; </span><span class="err">
</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>To see exactly what this looks like, you can look at the tag: <a href="https://github.com/kriserickson/google-actions-rpsls/tree/PART_3_STEP_1">PART_3_STEP_1</a>.<br />
Nothing here is particularly complicated, and it really has nothing to do with Google Actions so let’s skip along to our next change.</p>
<p>Next we are going to add per session scoring, as well as a bit of state so that we can provide better responses when we don’t get
an answer we want. Our game state is going to be an enumeration, and there are basically 3 states: Initial, StartGame (when we are
waiting for the user to choose Rock/Paper/Scissors/Lizard/Spock), and EndGame when we are waiting to see if they want to play again. Let’s
create our version of an enumeration and place it at the top of the rpsls.js file:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">STATES</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">freeze</span><span class="p">({</span><span class="na">INITIAL_STATE</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">START_GAME</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">END_GAME</span><span class="p">:</span> <span class="mi">2</span><span class="p">});</span></code></pre></figure>
<p>Next we need to keep the state somewhere, and this is done by adding the state to to every ask method (and askWIthList method). What is the state variable?
It can be any JSON serializable object (i.e. you can’t shove something in that has <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value">circular structure</a>, and an Object will lose any of its methods, but basically you
can put any structure into the state object). If at any time during the interaction you don’t pass the state, it will revert to null
so remember you have to set it everywhere, even if you don’t change it! The other thing to realize about
state is that you are passing it back to the Google Servers and the Google Servers are passing it back to you with every request, so even though
you can store fairly large objects in it, it may not be the most efficient way of doing things (you might want to store large objects
in a local database instead).</p>
<p>The first place to add the state is in our mainIntentHandler. Since this is only executed when a user begins their conversation with our game, we
can completely reset the state (including setting their wins and losses for this conversation to 0 – we will use these wins and losses later in the article).</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">mainIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'MAIN intent triggered.'</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">getStartList</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">askWithList</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">><</span><span class="nx">p</span><span class="o">><</span><span class="nx">s</span><span class="o">></span><span class="nx">Welcome</span> <span class="nx">to</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span><span class="p">,</span> <span class="nx">Spock</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">If</span> <span class="nx">you</span> <span class="nx">need</span> <span class="nx">instructions</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">Instructions</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">play</span> <span class="nx">the</span> <span class="nx">game</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">Start</span> <span class="nx">Game</span><span class="o"><</span><span class="sr">/s></</span><span class="nx">p</span><span class="o">><</span><span class="sr">/speak>`,</span><span class="err">
</span> <span class="p">[</span><span class="s1">'Say Instructions or Start Game'</span><span class="p">]),</span> <span class="nx">list</span><span class="p">,</span> <span class="p">{</span><span class="na">state</span><span class="p">:</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">INITIAL_STATE</span><span class="p">,</span> <span class="na">wins</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">loss</span><span class="p">:</span> <span class="mi">0</span><span class="p">});</span>
<span class="p">}</span></code></pre></figure>
<p>the only change in this function is the addition of the state at the end:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"></speak></span>`, ['Say Instructions or Start Game']), list);</code></pre></figure>
<p>becomes:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"></speak></span>`, ['Say Instructions or Start Game']), list,
{state: STATES.INITIAL_STATE, wins: 0, loss: 0});</code></pre></figure>
<p>Now we have to go and add state to all the rest of our Ask Commands. And to add state (unless we want it to revert every time), we
have to get the state, which we do through the app method <a href="https://developers.google.com/actions/reference/nodejs/ActionsSdkApp#getDialogState">getDialogState</a>.</p>
<p>So our readInstructions function (removing the ssml content) becomes:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">readInstructions</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">getStartList</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">dialogState</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">getDialogState</span><span class="p">();</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">INITIAL_STATE</span><span class="p">;</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">askWithList</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="err">`</span><span class="p">..</span><span class="nx">snip</span><span class="p">..</span><span class="err">`</span><span class="p">),</span> <span class="nx">list</span><span class="p">,</span> <span class="nx">dialogState</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>We set the state of the state variable (OK, perhaps a bit confusing, maybe we should have called it gameState), to be INITIAL when we
reading the instructions. To start the game we do the same thing, except set the state to START_GAME:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">startGame</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">dialogState</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">getDialogState</span><span class="p">();</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">START_GAME</span><span class="p">;</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">><</span><span class="nx">p</span><span class="o">><</span><span class="nx">s</span><span class="o">></span><span class="nx">Do</span> <span class="nx">you</span> <span class="nx">pick</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span> <span class="nx">or</span> <span class="nx">Spock</span><span class="o"><</span><span class="sr">/s></</span><span class="nx">p</span><span class="o">><</span><span class="sr">/speak>`</span><span class="se">)</span><span class="sr">, dialogState</span><span class="se">)</span><span class="err">;
</span><span class="p">}</span></code></pre></figure>
<p>It is in returnResults that things really change:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">returnResults</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="nx">userChoice</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">computerChoice</span> <span class="o">=</span> <span class="nx">CHOICE_ARRAY</span><span class="p">[</span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">CHOICE_ARRAY</span><span class="p">.</span><span class="nx">length</span><span class="p">)];</span>
<span class="kr">const</span> <span class="nx">dialogState</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">getDialogState</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="nx">userChoice</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">START_GAME</span><span class="p">;</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">><</span><span class="nx">p</span><span class="o">><</span><span class="nx">s</span><span class="o">></span><span class="nx">I</span> <span class="nx">also</span> <span class="nx">picked</span> <span class="nx">$</span><span class="p">{</span><span class="nx">userChoice</span><span class="p">}.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Try</span> <span class="nx">again</span><span class="p">,</span> <span class="k">do</span> <span class="nx">you</span> <span class="nx">pick</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span> <span class="nx">or</span> <span class="nx">Spock</span><span class="o"><</span><span class="sr">/s></</span><span class="nx">p</span><span class="o">><</span><span class="sr">/speak>`</span><span class="se">)</span><span class="sr">,</span><span class="err">
</span> <span class="nx">dialogState</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">CHOICE_MATRIX</span><span class="p">[</span><span class="nx">userChoice</span> <span class="o">+</span> <span class="nx">computerChoice</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">userChoice</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Impossible choice: '</span> <span class="o">+</span> <span class="nx">userChoice</span><span class="p">);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="s1">'Something has gone wrong, you picked: '</span> <span class="o">+</span> <span class="nx">userChoice</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">let</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">buildList</span><span class="p">(</span><span class="s1">'Start Game or Instructions'</span><span class="p">);</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">addItems</span><span class="p">([</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_AGAIN</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Start'</span><span class="p">,</span> <span class="s1">'Yes'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="s1">'Again'</span><span class="p">),</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_END</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Stop'</span><span class="p">,</span> <span class="s1">'No'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="s1">'End'</span><span class="p">)</span>
<span class="p">]);</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">END_GAME</span><span class="p">;</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">wins</span> <span class="o">+=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="mi">1</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">loss</span> <span class="o">+=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">winLost</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="s1">'You Won!'</span> <span class="p">:</span> <span class="s1">'You lost.'</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="err">`</span><span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">result</span><span class="p">.</span><span class="nx">outcome</span><span class="p">}</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">winLost</span><span class="p">}</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="k">break</span> <span class="nx">time</span><span class="o">=</span><span class="s2">".5s"</span><span class="o">/><</span><span class="nx">s</span> <span class="o">></span><span class="nx">You</span> <span class="nx">have</span> <span class="nx">won</span>
<span class="o"><</span><span class="nx">say</span><span class="o">-</span><span class="nx">as</span> <span class="nx">interpret</span><span class="o">-</span><span class="nx">as</span><span class="o">=</span><span class="s2">"cardinal"</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">dialogState</span><span class="p">.</span><span class="nx">wins</span><span class="p">}</span><span class="o"><</span><span class="sr">/say-as> games,</span><span class="err">
</span> <span class="nx">and</span> <span class="nx">lost</span> <span class="o"><</span><span class="nx">say</span><span class="o">-</span><span class="nx">as</span> <span class="nx">interpret</span><span class="o">-</span><span class="nx">as</span><span class="o">=</span><span class="s2">"cardinal"</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">dialogState</span><span class="p">.</span><span class="nx">loss</span><span class="p">}</span><span class="o"><</span><span class="sr">/say-as> games</span><span class="err">.
</span> <span class="o"><</span><span class="sr">/s><break time=".7s"/</span><span class="o">></span><span class="err">`</span><span class="p">;</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">askWithList</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">><</span><span class="nx">p</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">message</span><span class="p">}</span><span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">play</span> <span class="nx">again</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">again</span><span class="p">.</span><span class="o"><</span><span class="k">break</span> <span class="nx">time</span><span class="o">=</span><span class="s2">".3s"</span><span class="o">/><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">quit</span> <span class="nx">say</span> <span class="nx">end</span><span class="p">.</span><span class="o"><</span><span class="sr">/s></</span><span class="nx">p</span><span class="o">><</span><span class="sr">/speak>`</span><span class="se">)</span><span class="err">,
</span> <span class="nx">list</span><span class="p">,</span> <span class="nx">dialogState</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>The main change is that we update the state’s win and losses depending upon whether the play won or lost, and we tell the
user their current win/loss state when telling them the outcome of the game.</p>
<p>The last change we make is to the textIntentHandler, because we now know the state of the game, we can handle invalid users
much more intelligently, and give the user better feedback:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">textIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'TEXT intent triggered.'</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">rawInput</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">getRawInput</span><span class="p">();</span>
<span class="kd">let</span> <span class="nx">res</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span> <span class="o">=</span> <span class="nx">rawInput</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/^</span><span class="se">\s</span><span class="sr">*</span><span class="se">(</span><span class="sr">rock|paper|scissors|lizard|spock</span><span class="se">)\s</span><span class="sr">*$/i</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">returnResults</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="nx">res</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">toLowerCase</span><span class="p">());</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">dialogState</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">getDialogState</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">===</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">START_GAME</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">><</span><span class="nx">p</span><span class="o">><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">rawInput</span><span class="p">}</span> <span class="nx">is</span> <span class="nx">not</span> <span class="nx">a</span> <span class="nx">valid</span> <span class="nx">choice</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Do</span> <span class="nx">you</span> <span class="nx">pick</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span> <span class="nx">or</span> <span class="nx">Spock</span><span class="o"><</span><span class="sr">/s></</span><span class="nx">p</span><span class="o">><</span><span class="sr">/speak>`</span><span class="se">)</span><span class="sr">,</span><span class="err">
</span> <span class="nx">dialogState</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">===</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">INITIAL_STATE</span> <span class="o">||</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">===</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">END_GAME</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">getStartList</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">askWithList</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">><</span><span class="nx">p</span><span class="o">><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">rawInput</span><span class="p">}</span> <span class="nx">is</span> <span class="nx">not</span> <span class="nx">a</span> <span class="nx">valid</span> <span class="nx">choice</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">If</span> <span class="nx">you</span> <span class="nx">need</span> <span class="nx">instructions</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">Instructions</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">play</span> <span class="nx">the</span> <span class="nx">game</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">Start</span> <span class="nx">Game</span><span class="o"><</span><span class="sr">/s></</span><span class="nx">p</span><span class="o">><</span><span class="sr">/speak>`,</span><span class="err">
</span> <span class="p">[</span><span class="s1">'Say Instructions or Start Game'</span><span class="p">]),</span> <span class="nx">list</span><span class="p">,</span> <span class="nx">dialogState</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">tell</span><span class="p">(</span><span class="s1">'Impossible choice, ending!'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span> </code></pre></figure>
<p>So, as you can see, we can tailor our results based upon our state. Also note that we don’t pass state to the app.tell method as
that ends the conversation, and state is only kept around during the conversation. So everything to this point is available
at the tag <a href="https://github.com/kriserickson/google-actions-rpsls/tree/PART_3_STEP_2">PART_3_STEP_2</a>. Lets get our Simulator up and
running again, so to do that we probably have to restart ngrok, get our new ngrok address:</p>
<p><code class="highlighter-rouge">ngrok http 5050</code></p>
<p>and place it in our rpsls.json:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="w">
</span><span class="s2">"conversations"</span><span class="err">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"main"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"main"</span><span class="p">,</span><span class="w">
</span><span class="nt">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://CHANGE-ME.ngrok.io"</span><span class="p">,</span><span class="w">
</span><span class="nt">"fulfillmentApiVersion"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w"> </span></code></pre></figure>
<p>update gactions</p>
<p><code class="highlighter-rouge">gactions update --action_package PACKAGE_NAME --project rpsls-XXXXX</code></p>
<p>Start the app:</p>
<p><code class="highlighter-rouge">node index.js</code></p>
<p>And then go to <a href="https://console.actions.google.com">https://console.actions.google.com</a>, go to the Overview of our project, and hit
“Test Draft”. And now our app should be able to keep score:</p>
<p><img src="/img/google-home/3-step1.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>You will notice that state is kept in variable called ‘conversationToken’, but we don’t have to worry about that as the
Google Actions Node SDK takes care of all of that for us.</p>
<p>But what if we want to keep a running total of our wins and losses for all of our conversations with the Google Assistant?<br />
Then we need to switch from state to user storage. The userStorage is available on the app object, and is maintained
from conversation to conversation. It has the same restrictions as the state does in what can be serialized on it,
and you should remember that anything you store in there will be kept (presumably) for ever (until you delete it)
and will always be sent to your service so remember to clean up items you don’t need. It is important to note (not that we would ever approach that size) that the serialized JSON for the userStorage is <a href="https://developers.google.com/actions/reference/rest/Shared.Types/AppRequest#user">limited to 10K</a>.</p>
<p>For our game, we can switch from using state to userStorage very easily. All we have to do is change a few lines of code, first in the middle of the returnResults function, were we set the dialog state, change the way scoring is tracked from:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">END_GAME</span><span class="p">;</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">wins</span> <span class="o">+=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="mi">1</span> <span class="p">:</span> <span class="mi">0</span><span class="p">;</span>
<span class="nx">dialogState</span><span class="p">.</span><span class="nx">loss</span> <span class="o">+=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="mi">1</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">winLost</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="s1">'You Won!'</span> <span class="p">:</span> <span class="s1">'You lost.'</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="err">`</span><span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">result</span><span class="p">.</span><span class="nx">outcome</span><span class="p">}</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">winLost</span><span class="p">}</span><span class="o"><</span><span class="sr">/s><break time=".5s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span> <span class="o">></span><span class="nx">You</span> <span class="nx">have</span> <span class="nx">won</span> <span class="o"><</span><span class="nx">say</span><span class="o">-</span><span class="nx">as</span> <span class="nx">interpret</span><span class="o">-</span><span class="nx">as</span><span class="o">=</span><span class="s2">"cardinal"</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">dialogState</span><span class="p">.</span><span class="nx">wins</span><span class="p">}</span><span class="o"><</span><span class="sr">/say-as> games,</span><span class="err">
</span> <span class="nx">and</span> <span class="nx">lost</span> <span class="o"><</span><span class="nx">say</span><span class="o">-</span><span class="nx">as</span> <span class="nx">interpret</span><span class="o">-</span><span class="nx">as</span><span class="o">=</span><span class="s2">"cardinal"</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">dialogState</span><span class="p">.</span><span class="nx">loss</span><span class="p">}</span><span class="o"><</span><span class="sr">/say-as> games.</</span><span class="nx">s</span><span class="o">></span>
<span class="o"><</span><span class="k">break</span> <span class="nx">time</span><span class="o">=</span><span class="s2">".7s"</span><span class="o">/></span><span class="err">`</span><span class="p">;</span></code></pre></figure>
<p>to</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="nx">dialogState</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">STATES</span><span class="p">.</span><span class="nx">END_GAME</span><span class="p">;</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">userStorage</span><span class="p">.</span><span class="nx">wins</span> <span class="o">=</span> <span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">userStorage</span><span class="p">.</span><span class="nx">wins</span> <span class="o">||</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="mi">1</span> <span class="p">:</span> <span class="mi">0</span><span class="p">);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">userStorage</span><span class="p">.</span><span class="nx">losses</span> <span class="o">=</span> <span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">userStorage</span><span class="p">.</span><span class="nx">losses</span> <span class="o">||</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="mi">0</span> <span class="p">:</span> <span class="mi">1</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">winLost</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">win</span> <span class="p">?</span> <span class="s1">'You Won!'</span> <span class="p">:</span> <span class="s1">'You lost.'</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="err">`</span><span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">result</span><span class="p">.</span><span class="nx">outcome</span><span class="p">}</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">winLost</span><span class="p">}</span><span class="o"><</span><span class="sr">/s> <break time=".5s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span> <span class="o">></span><span class="nx">You</span> <span class="nx">have</span> <span class="nx">won</span> <span class="o"><</span><span class="nx">say</span><span class="o">-</span><span class="nx">as</span> <span class="nx">interpret</span><span class="o">-</span><span class="nx">as</span><span class="o">=</span><span class="s2">"cardinal"</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">app</span><span class="p">.</span><span class="nx">userStorage</span><span class="p">.</span><span class="nx">wins</span><span class="p">}</span><span class="o"><</span><span class="sr">/say-as> games,</span><span class="err">
</span> <span class="nx">and</span> <span class="nx">lost</span> <span class="o"><</span><span class="nx">say</span><span class="o">-</span><span class="nx">as</span> <span class="nx">interpret</span><span class="o">-</span><span class="nx">as</span><span class="o">=</span><span class="s2">"cardinal"</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">app</span><span class="p">.</span><span class="nx">userStorage</span><span class="p">.</span><span class="nx">losses</span><span class="p">}</span><span class="o"><</span><span class="sr">/say-as> games.</</span><span class="nx">s</span><span class="o">></span>
<span class="o"><</span><span class="k">break</span> <span class="nx">time</span><span class="o">=</span><span class="s2">".7s"</span><span class="o">/></span><span class="err">`</span><span class="p">;</span></code></pre></figure>
<p>also we should remove the setting of the original win and loss state in the main intent handler, so that:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"></speak></span>`, ['Say Instructions or Start Game']), list, {state: STATES.INITIAL_STATE,
wins: 0, loss: 0});</code></pre></figure>
<p>becomes:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt"></speak></span>`, ['Say Instructions or Start Game']), list, {state: STATES.INITIAL_STATE});</code></pre></figure>
<p>Now when we restart app, we see that our request object instead of storing wins and losses in the conversationToken, they are now stored in userStorage.</p>
<p><img src="/img/google-home/3-step2.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>You can also see that I am apparently very good at Rock/Paper/Scissors/Lizard/Spock with a seven and one record! So what else could we possibly do to extend our Rock, Paper, Scissors, Lizard, Spock game? Well, we know how good I am at the
game because I posted a chunk of JSON, but that is obviously no way to share it with our friends. To do that we need a site were we can have accounts were our scores are visible beyond the Google Assistant, and to the entire web. To do that we will have to</p>
<ol>
<li>Create a website with a login, a leader board, a sign up page, etc.</li>
<li>Link that account to our Google Assistant Account through OAuth</li>
<li>Profit!</li>
</ol>
<p>But that will have to wait for the next (and probably final article) in this series on Google Home and the Google Assistant.</p>
<p><a href="http://agingcoder.com/programming/2018/02/24/creating-a-google-home-app-part-3-refactoring-and-scoring/">Creating A Google Home App Part 3 - Refactoring and Scoring</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on February 24, 2018.</p>http://agingcoder.com/programming/2018/02/08/creating-a-google-home-app-part-2-the-game2018-02-08T00:00:00-08:002018-02-08T00:00:00-08:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<h3 id="introduction">Introduction</h3>
<p>In the previous article, <a href="https://agingcoder.com/programming/2018/02/04/creating-a-google-home-app/">Creating A Google Home App</a>
I detailed how to get a very simple Google Home App up and running. Mostly it was about setting up the node project, the Google
Actions project, but in the end you had an App that when you talked to it, it would say – ‘Hey Ma, It Worked!’. And while it wasn’t
much of an achievement, but I know I felt pretty cool when I got my Google Speaker in my kitchen saying things I wanted it to say.<br />
Somehow it felt more impressive that just having a website that dumped out HTML, and now in this article we are going to learn how to
actually create something (somewhat) useful, and learn a fair bit more about the GoogleActionSDK.</p>
<p>A couple of quick notes before we jump into the application, because we are handling all of the parsing of text (Google handles
the Text to Speech for us, and does a very impressive job of that), this does not create the cleanest possible code as we are
stuck using default intents. If you want to have a cleaner set of intents to map to, and not have to manually parse responses
you might want to look at <a href="https://dialogflow.com/docs/fulfillment">WebHooks in DialogFlow</a>, this is not that tutorial however.</p>
<p>Another note, I have been messing around with Google Actions Console for a while, and sometimes it is just buggy. I’ve had to
delete projects and create fresh projects to get things to work, hence I would recommend not filling out App Information section
of the Overview until you are ready to go for two reasons.</p>
<ol>
<li>It’s frustrating to fill out the App Information multiple times.</li>
<li>The App Name you choose in their is like a URL, it has to be unique and if you have to recreate your app because it is
acting funny remember to change the old apps name BEFORE you delete it. Also you have to wait about an hour before changing
an apps name to be able to create a new app with that name.</li>
</ol>
<p>Also, a quite note: Google For Business accounts seem to work, until they don’t.<br />
I spent over an hour trying to figure out why the app would stop sending requests after the first one.<br />
Finally after a lot of googling, I saw someone post about how they were having a similar problem until they blew away their app
and recreated it on a gmail account (rather than a Google for Business Account) and all my problems went away
– you have been warned, not all Google Accounts are equal (and surprisingly the ones you pay for are less equal),
or it could be the act of creating a new project fixed things, as I have said a couple of times sometimes the only way
to fix a project is to create a new one.</p>
<h3 id="the-code">The Code</h3>
<h4 id="changes-to-the-indexjs-file">Changes to the index.js file</h4>
<p>The index.js file is not going to change a lot, basically we are going to do all of the heavy lifting in a file called rpsls.js, so in the index file add a const reference to those exported functions before we create the expressApp.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="kr">const</span> <span class="nx">bodyParser</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'body-parser'</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">rpsls</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'./rpsls'</span><span class="p">);</span> <span class="c1">// <-- Add Me</span>
<span class="kr">const</span> <span class="nx">expressApp</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span></code></pre></figure>
<p>You can remove the entire mainIntentHandler function from index.js (it is going to be expanded, improved in rpsls.js). And then we have to update our actionMap a little.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="c1">// actionMap.set(actionApp.StandardIntents.MAIN, mainIntentHandler); COMMENT ME OUT OR REMOVE ME</span>
<span class="nx">actionMap</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">actionApp</span><span class="p">.</span><span class="nx">StandardIntents</span><span class="p">.</span><span class="nx">MAIN</span><span class="p">,</span> <span class="nx">rpsls</span><span class="p">.</span><span class="nx">mainIntentHandler</span><span class="p">);</span>
<span class="nx">actionMap</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">actionApp</span><span class="p">.</span><span class="nx">StandardIntents</span><span class="p">.</span><span class="nx">OPTION</span><span class="p">,</span> <span class="nx">rpsls</span><span class="p">.</span><span class="nx">optionIntentHandler</span><span class="p">);</span>
<span class="nx">actionMap</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">actionApp</span><span class="p">.</span><span class="nx">StandardIntents</span><span class="p">.</span><span class="nx">TEXT</span><span class="p">,</span> <span class="nx">rpsls</span><span class="p">.</span><span class="nx">textIntentHandler</span><span class="p">);</span></code></pre></figure>
<p>Ok, so we have two new standard Intents that we have added. And that is all the changes we need to make to index.js.</p>
<p>Lets quickly go over what we have done. We’ve removed our old main handler and added one that we are going to add to the rpsls.js file.
We have also added two new handlers that are used when the user enters responses to our asks.</p>
<p>The OPTION standard intent handles lists (and carousels, but since we are mainly focused on Google Home, we are going use the simple list for our demo).</p>
<p>The TEXT standard intent gives us raw text to parse, which allows the user to text free form (thus be prepared to handle anything).</p>
<h4 id="the-rpslsjs-file">The rpsls.js file</h4>
<p>Create a new file in the main directory called rpsls.js. It is going to have three exported functions, so lets create those first (this will enable us
to run the app even though it isn’t complete because all the pieces will at least be in place).</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">mainIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'MAIN intent triggered.'</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">optionIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'OPTION intent triggered.'</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">textIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'TEXT intent triggered.'</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">mainIntentHandler</span><span class="p">:</span> <span class="nx">mainIntentHandler</span><span class="p">,</span>
<span class="na">optionIntentHandler</span><span class="p">:</span> <span class="nx">optionIntentHandler</span><span class="p">,</span>
<span class="na">textIntentHandler</span><span class="p">:</span> <span class="nx">textIntentHandler</span>
<span class="p">};</span></code></pre></figure>
<h4 id="the-main-intent-handler">The Main Intent Handler</h4>
<p>Just like our mainIntentHandler in previous version, all intents that are mapped from the main SDK get an instance of the
actionSDKApp object passed to them. This is used for many things, and is pretty well documented at
<a href="https://developers.google.com/actions/reference/nodejs/ActionsSdkApp">https://developers.google.com/actions/reference/nodejs/ActionsSdkApp</a>, and
<a href="https://developers.google.com/actions/reference/nodejs/AssistantApp">https://developers.google.com/actions/reference/nodejs/AssistantApp</a> – it is
important to note that ActionsSdkApp is a subclass of the AssistantApp. The first thing we are going to do welcome our user and give them the option
to either get instructions for Rock, Paper, Scissors, Lizard, Spock or play the game. We could do this with text parsing, but since there are only two
possible options this seems like the perfect place to ask for a list. So lets update our mainIntentHandler:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">mainIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'MAIN intent triggered.'</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">buildList</span><span class="p">(</span><span class="s1">'Start Game or Instructions'</span><span class="p">);</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">addItems</span><span class="p">([</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="s1">'Start Game'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Start'</span><span class="p">,</span> <span class="s1">'New Game'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="s1">'Start Game'</span><span class="p">),</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="s1">'Instructions'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Help'</span><span class="p">,</span> <span class="s1">'Read Instructions'</span><span class="p">,</span> <span class="s1">'Tell Me Instructions'</span><span class="p">,</span>
<span class="s1">'Repeat Instructions'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="s1">'Instructions'</span><span class="p">)</span>
<span class="p">]);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">askWithList</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">></span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Welcome</span> <span class="nx">to</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span><span class="p">,</span> <span class="nx">Spock</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">If</span> <span class="nx">you</span> <span class="nx">need</span> <span class="nx">instructions</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">Instructions</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">play</span> <span class="nx">the</span> <span class="nx">game</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">Start</span> <span class="nx">Game</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/p</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/speak>`, </span><span class="se">[</span><span class="sr">'Say Instructions or Start Game'</span><span class="se">])</span><span class="sr">, list</span><span class="se">)</span><span class="err">;
</span>
<span class="p">}</span></code></pre></figure>
<p>First we are going to create a list of options we are going to present our user. We do with the
<a href="https://developers.google.com/actions/reference/nodejs/AssistantApp#buildList">buildList</a> method of the ActionSdkApp,
build list takes an optional title. This obviously will not be visible on the Google Home, but it will show up on your phone.<br />
Plus it can be useful debug information, so we will include it. Next we add items to the list, each item is constructed
with the <a href="https://developers.google.com/actions/reference/nodejs/AssistantApp#buildOptionItem">buildOptionItem</a> method.<br />
The buildOptionItem takes a key, and a list of synonyms. Also, we are setting the title of the optionItem (optionItem methods
and the buildOptionItem method are chainable). Now that we have that list, we are ready to
<a href="https://developers.google.com/actions/reference/nodejs/ActionsSdkApp#askWithList">askWithList</a>.</p>
<p>The askWithList method, takes an InputPrompt and a list. The buildInputPrompt takes an option
(whether or not the text is <a href="https://en.wikipedia.org/wiki/Speech_Synthesis_Markup_Language">SSML</a>), some text (either SSML or plain text),
and a list of messages that can be output if the user fails to enter any text. If you want the ability to properly read things fractions,
numbers, and have more control of the timing of the speech I would recommend producing your text in SSML. Our SSML is very simple here,
we create a single paragraph (<code class="highlighter-rouge"><p></code>), in which we have three sentences (<code class="highlighter-rouge"><s></code>).</p>
<p>OK, now that we have a slightly better opening for application, lets make sure it worked. The first thing we have to do is restart the app</p>
<p><code class="highlighter-rouge">node index.js</code></p>
<p>If there are no errors, then fire up ngrok, if ngrok is already running with the same url as last time, skip this step and the following 2 steps.</p>
<p><code class="highlighter-rouge">ngrok http 5050</code></p>
<p>Next we need to change the url for our conversation in in rpsls.json.</p>
<p><code class="highlighter-rouge">"url": "https://2f29038a.ngrok.io",</code></p>
<p>And finally we have to update gactions:</p>
<p><code class="highlighter-rouge">gactions update --action_package rpsls.json --project rpsls-XXXXX</code></p>
<p>Go to the gactions console and test the app:</p>
<p><img src="/img/google-home/2-step1.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>Note, if you are getting “My Test App Is Not Responding Right Now”, try going to the Overview, and click on “Test Draft”</p>
<p><img src="/img/google-home/2-step2.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<h3 id="the-list-intent-handler">The List Intent Handler</h3>
<p>Of course, nothing else will work, so lets parse the response we are going to get. This is handled by the actionApp.StandardIntents.OPTION, that we saw earlier.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// React to list or carousel selection</span>
<span class="kd">function</span> <span class="nx">optionIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'OPTION intent triggered.'</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">param</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">getSelectedOption</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">param</span> <span class="o">===</span> <span class="s1">'Instructions'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">readInstructions</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">param</span> <span class="o">===</span> <span class="s1">'Start Game'</span> <span class="o">||</span> <span class="nx">param</span> <span class="o">===</span> <span class="s1">'Again'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">startGame</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">param</span> <span class="o">===</span> <span class="s1">'End'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">tell</span><span class="p">(</span><span class="s1">'Bye'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>The option intent handler is also rather simple, the key thing is that to get text from an option, we use the getSelectedOption method. This method will give
us back the exact string we used as the key when we built the list (even if the user types in ‘start game’ we will get back ‘Start Game’). In fact, since we
need the exact string, lets replace all the string instances with constants. At the top of the rpsls.js lets add the following constants:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">OPTION_INSTRUCTIONS</span> <span class="o">=</span> <span class="s1">'Instructions'</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">OPTION_START_GAME</span> <span class="o">=</span> <span class="s1">'Start Game'</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">OPTION_AGAIN</span> <span class="o">=</span> <span class="s1">'Again'</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">OPTION_END</span> <span class="o">=</span> <span class="s1">'End'</span><span class="p">;</span></code></pre></figure>
<p>and lets fix the main intent, by replacing the list with said options:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">list</span><span class="p">.</span><span class="nx">addItems</span><span class="p">([</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_START_GAME</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Start'</span><span class="p">,</span> <span class="s1">'New Game'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="nx">OPTION_START_GAME</span><span class="p">),</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_INSTRUCTIONS</span><span class="p">,</span>
<span class="p">[</span><span class="s1">'Help'</span><span class="p">,</span> <span class="s1">'Read Instructions'</span><span class="p">,</span> <span class="s1">'Tell Me Instructions'</span><span class="p">,</span> <span class="s1">'Repeat Instructions'</span><span class="p">]</span>
<span class="p">).</span><span class="nx">setTitle</span><span class="p">(</span><span class="nx">OPTION_INSTRUCTIONS</span><span class="p">)</span>
<span class="p">])</span></code></pre></figure>
<p>and clean up the optionIntentHandler:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// React to list or carousel selection</span>
<span class="kd">function</span> <span class="nx">optionIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'OPTION intent triggered.'</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">param</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">getSelectedOption</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">param</span> <span class="o">===</span> <span class="nx">OPTION_INSTRUCTIONS</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">readInstructions</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">param</span> <span class="o">===</span> <span class="nx">OPTION_START_GAME</span> <span class="o">||</span> <span class="nx">param</span> <span class="o">===</span> <span class="nx">OPTION_AGAIN</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">startGame</span><span class="p">(</span><span class="nx">app</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">param</span> <span class="o">===</span> <span class="nx">OPTION_END</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">tell</span><span class="p">(</span><span class="s1">'Bye'</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>We will be using OPTION_AGAIN and OPTION_END when the game is over, so you can ignore those for now. What happens when the Google Home
user responds with something that is not in the list? Instead of firing the list intent handler, the app fires the text intent handler,
which we will see <a href="#the-text-intent-handler">later in this tutorial</a>, so we will handle invalid choices in that portion of code.</p>
<h4 id="reading-the-instructions">Reading the Instructions</h4>
<p>Reading the instructions is similar to our main intent, we create the same list (eventually we can factor out this
duplicated code and turn creating a list into a function), and give the user the instructions in an input prompt. To
have the speech sound more natural I have added short breaks after each instruction sentence. The break can be inside
or outside the <a href="https://www.w3.org/TR/speech-synthesis11/">sentence</a>, quoting from the W3C recommendation:</p>
<blockquote>The s element can only contain text to be rendered and the following elements: audio, break, emphasis, lang, lookup, mark, phoneme, prosody, say-as, sub, token, voice, w.</blockquote>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">readInstructions</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Read Instructions.'</span><span class="p">);</span>
<span class="kd">let</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">buildList</span><span class="p">(</span><span class="s1">'Start Game or Instructions'</span><span class="p">);</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">addItems</span><span class="p">([</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_START_GAME</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Start'</span><span class="p">,</span> <span class="s1">'New Game'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="nx">OPTION_START_GAME</span><span class="p">),</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_INSTRUCTIONS</span><span class="p">,</span>
<span class="p">[</span><span class="s1">'Help'</span><span class="p">,</span> <span class="s1">'Read Instructions'</span><span class="p">,</span> <span class="s1">'Tell Me Instructions'</span><span class="p">,</span> <span class="s1">'Repeat Instructions'</span><span class="p">]</span>
<span class="p">).</span><span class="nx">setTitle</span><span class="p">(</span><span class="nx">OPTION_INSTRUCTIONS</span><span class="p">)</span>
<span class="p">])</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">askWithList</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">></span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Scissors</span> <span class="nx">cuts</span> <span class="nx">Paper</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Paper</span> <span class="nx">covers</span> <span class="nx">Rock</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".5s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Rock</span> <span class="nx">crushes</span> <span class="nx">Lizard</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Lizard</span> <span class="nx">poisons</span> <span class="nx">Spock</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Spock</span> <span class="nx">smashes</span> <span class="nx">Scissors</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Scissors</span> <span class="nx">decapitates</span> <span class="nx">Lizard</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Lizard</span> <span class="nx">eats</span> <span class="nx">Paper</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Paper</span> <span class="nx">disproves</span> <span class="nx">Spock</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".3s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Spock</span> <span class="nx">vaporizes</span> <span class="nx">Rock</span><span class="p">.</span><span class="o"><</span><span class="sr">/s><break time=".7s"/</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="p">(</span><span class="nx">and</span> <span class="nx">as</span> <span class="nx">it</span> <span class="nx">always</span> <span class="nx">has</span><span class="p">),</span> <span class="nx">Rock</span> <span class="nx">crushes</span> <span class="nx">Scissors</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/p</span><span class="err">>
</span> <span class="o"><</span><span class="k">break</span> <span class="nx">time</span><span class="o">=</span><span class="s2">"1s"</span><span class="o">/></span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">play</span> <span class="nx">the</span> <span class="nx">game</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">Start</span> <span class="nx">Game</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">repeat</span> <span class="nx">the</span> <span class="nx">instructions</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">instructions</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/p</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/speak>`</span><span class="se">)</span><span class="sr">, list</span><span class="se">)</span><span class="err">;
</span><span class="p">}</span> </code></pre></figure>
<p>Once we done, you should feel free to play with elements like <a href="https://www.w3.org/TR/speech-synthesis11/#edef_prosody">prosody</a>,
<a href="https://www.w3.org/TR/speech-synthesis11/#edef_say-as">say-as</a>, <a href="https://www.w3.org/TR/speech-synthesis11/#edef_emphasis">emphasis</a>,
<a href="https://www.w3.org/TR/speech-synthesis11/#edef_phoneme">phoneme</a>, and even the <a href="https://www.w3.org/TR/speech-synthesis11/#edef_audio">audio</a>
tag to become comfortable with SSML.</p>
<p>To hear our instructions, restart the app, go back to the simulator, restart the interaction (if you are in bad a state you can
always type ‘quit’ in the simulator and then ‘Talk to My Test App’ to start it again), and choose instructions from the main interaction.</p>
<h4 id="starting-the-game">Starting the Game</h4>
<p>To code in the startGame function, add</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">startGame</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">></span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Do</span> <span class="nx">you</span> <span class="nx">pick</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span> <span class="nx">or</span> <span class="nx">Spock</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/p</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/speak>`</span><span class="se">))</span><span class="err">;
</span><span class="p">}</span></code></pre></figure>
<p>When starting a game, we aren’t asking for a list anymore, we are asking for a text input (the difference between askWithList and ask is that
the response to an ask fires the actionApp.StandardIntents.TEXT intent). While we easily could have used a list here (there are only 5
possible answers), I did want to demonstrate the difference between handling a list and handling a text response, plus our option
handler would have gotten pretty big.</p>
<h4 id="the-text-intent-handler">The Text Intent Handler</h4>
<p>Like the option intent handler, the text intent handler is pretty simple. We grab the rawInput from the app, then we are going
to make sure we have valid input (being a rock, paper, scissors, lizard or spock) and pass that on to main game engine (returnResults).</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">textIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'TEXT intent triggered.'</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">rawInput</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">getRawInput</span><span class="p">();</span>
<span class="kd">let</span> <span class="nx">res</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">res</span> <span class="o">=</span> <span class="nx">rawInput</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/^</span><span class="se">\s</span><span class="sr">*</span><span class="se">(</span><span class="sr">rock|paper|scissors|lizard|spock</span><span class="se">)\s</span><span class="sr">*$/i</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">returnResults</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="nx">res</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="nx">toLowerCase</span><span class="p">());</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">></span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">$</span><span class="p">{</span><span class="nx">rawInput</span><span class="p">}</span> <span class="nx">is</span> <span class="nx">not</span> <span class="nx">a</span> <span class="nx">valid</span> <span class="nx">choice</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Do</span> <span class="nx">you</span> <span class="nx">pick</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span> <span class="nx">or</span> <span class="nx">Spock</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/p</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/speak>`</span><span class="se">))</span><span class="err">;
</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>Currently we are not keeping track of any state, and we will wait until the next article to start maintaining state, so we
don’t know here if the user has actually started the game or whether this was an invalid answer to the initial question of
play game or instructions. If the user had chosen an invalid list item (not start game or instructions) they are going to get
a nonsensical answer but, like I said, we will fix that in the next article. Also note that we are not controlling the input
in the same way as we do with askWithList so we force our input to lower case for parsing.</p>
<h4 id="the-game-engine">The Game Engine</h4>
<p>The returnResults game engine is overly long (we will improve it in the next article), but simple piece of code that randomly
generates a choice of Rock, Paper, Scissors, Lizard or Spock and tells the user if they have won or not. It plays fairly,
though there should probably be some added weight to throwing Rock or Spock more often. At the top of the rpsls.js file, add the
following constant:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">CHOICE_ARRAY</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'rock'</span><span class="p">,</span><span class="s1">'paper'</span><span class="p">,</span><span class="s1">'scissors'</span><span class="p">,</span><span class="s1">'lizard'</span><span class="p">,</span><span class="s1">'spock'</span><span class="p">];</span></code></pre></figure>
<p>Next add the returnResults function.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">returnResults</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="nx">userChoice</span><span class="p">)</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">computerChoice</span> <span class="o">=</span> <span class="nx">CHOICE_ARRAY</span><span class="p">[</span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">CHOICE_ARRAY</span><span class="p">.</span><span class="nx">length</span><span class="p">)];</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="nx">userChoice</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">></span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">I</span> <span class="nx">also</span> <span class="nx">picked</span> <span class="nx">$</span><span class="p">{</span><span class="nx">userChoice</span><span class="p">}.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">Try</span> <span class="nx">again</span><span class="p">,</span> <span class="k">do</span> <span class="nx">you</span> <span class="nx">pick</span> <span class="nx">Rock</span><span class="p">,</span> <span class="nx">Paper</span><span class="p">,</span> <span class="nx">Scissors</span><span class="p">,</span> <span class="nx">Lizard</span> <span class="nx">or</span> <span class="nx">Spock</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/p</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/speak>`</span><span class="se">))</span><span class="err">;
</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">win</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">result</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">userChoice</span> <span class="o">===</span> <span class="s1">'rock'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'spock'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Spock vaporizes Rock'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'paper'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Paper covers Rocks'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'lizard'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Rock crushes Lizard'</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'scissors'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'as always, Rock crushes scissors'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">userChoice</span> <span class="o">===</span> <span class="s1">'paper'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'spock'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Paper disproves Spock'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'rock'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Paper covers Rocks'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'lizard'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Lizard eats Paper'</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'scissors'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Scissors cuts Paper'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">userChoice</span> <span class="o">===</span> <span class="s1">'scissors'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'spock'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Spock smashes Scissors'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'rock'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'as always, Rock crushes scissors'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'lizard'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Scissors decapitates Lizard'</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'paper'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Scissors cuts Paper'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">userChoice</span> <span class="o">===</span> <span class="s1">'lizard'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'paper'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Lizard eats Paper'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'rock'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Rock crushes Lizard'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'spock'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Lizard poisons Spock'</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'scissors'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Scissors decapitates Lizard'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">userChoice</span> <span class="o">===</span> <span class="s1">'spock'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'paper'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Paper disproves Spock'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'rock'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Spock vaporizes Rock'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'lizard'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Lizard poisons Spock'</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">computerChoice</span> <span class="o">===</span> <span class="s1">'scissors'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">win</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="s1">'Spock smashes Scissors'</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Impossible choice: '</span> <span class="o">+</span> <span class="nx">userChoice</span><span class="p">);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="s1">'Something has gone wrong, you picked: '</span> <span class="o">+</span> <span class="nx">userChoice</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">let</span> <span class="nx">list</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">buildList</span><span class="p">(</span><span class="s1">'Start Game or Instructions'</span><span class="p">);</span>
<span class="nx">list</span><span class="p">.</span><span class="nx">addItems</span><span class="p">([</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_AGAIN</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Start'</span><span class="p">,</span> <span class="s1">'Yes'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="s1">'Again'</span><span class="p">),</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">buildOptionItem</span><span class="p">(</span><span class="nx">OPTION_END</span><span class="p">,</span> <span class="p">[</span><span class="s1">'Stop'</span><span class="p">,</span> <span class="s1">'No'</span><span class="p">]).</span><span class="nx">setTitle</span><span class="p">(</span><span class="s1">'End'</span><span class="p">)</span>
<span class="p">]);</span>
<span class="kr">const</span> <span class="nx">message</span> <span class="o">=</span> <span class="s1">'<s>'</span> <span class="o">+</span> <span class="nx">result</span> <span class="o">+</span> <span class="s1">'</s>'</span> <span class="o">+</span> <span class="p">(</span><span class="nx">win</span> <span class="p">?</span> <span class="s1">'<s>You Won!</s>'</span> <span class="p">:</span> <span class="s1">'<s>You lost.</s>'</span><span class="p">);</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">askWithList</span><span class="p">(</span><span class="nx">app</span><span class="p">.</span><span class="nx">buildInputPrompt</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span>
<span class="err">`</span><span class="o"><</span><span class="nx">speak</span><span class="o">></span>
<span class="o"><</span><span class="nx">p</span><span class="o">></span>
<span class="nx">$</span><span class="p">{</span><span class="nx">message</span><span class="p">}</span>
<span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">play</span> <span class="nx">again</span><span class="p">,</span> <span class="nx">say</span> <span class="nx">again</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="nx">s</span><span class="o">></span><span class="nx">To</span> <span class="nx">quit</span> <span class="nx">say</span> <span class="nx">end</span><span class="p">.</span><span class="o"><</span><span class="sr">/s</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/p</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/speak>`</span><span class="se">)</span><span class="sr">, list</span><span class="se">)</span><span class="err">;
</span> <span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>It’s long code, but it is simple code and was written for ease of readability, not for elegance (we will convert it into a
tighter array based engine next article). There isn’t anything new here, we determine the winner, build a list of whether
we want to play again or end. Inform the user of whether they won or lost.</p>
<p>Now that everything is done, lets start up the simulator again.</p>
<p><img src="/img/google-home/2-step3.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>Now that it is working in the simulator, try it on your Google Home device. Also play around with SSML, and the various
inflections to improve the feel of our little game.</p>
<p>If you want to download the code for the state that it is in, it is <a href="https://github.com/kriserickson/google-actions-rpsls/tree/PART_2_STEP_1">TAG PART_2_STEP_1</a> on
GitHub: <a href="https://github.com/kriserickson/google-actions-rpsls/tree/PART_2_STEP_1">https://github.com/kriserickson/google-actions-rpsls/tree/PART_2_STEP_1</a>.</p>
<p>Next article we will start keeping score, add a login to our app, add account linking, and finish off some of the rough
edges in the app.</p>
<p>As always, if you see any bugs, or have suggestions feel free to do so either in the comments below, or as an issue or pull request in the
<a href="https://github.com/kriserickson/google-actions-rpsls">GitHub repo</a>.</p>
<p><a href="http://agingcoder.com/programming/2018/02/08/creating-a-google-home-app-part-2-the-game/">Creating A Google Home App Part 2 - The Game</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on February 08, 2018.</p>http://agingcoder.com/programming/2018/02/04/creating-a-google-home-app2018-02-04T00:00:00-08:002018-02-04T00:00:00-08:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<h2 id="introduction">Introduction</h2>
<p>A while ago I got an email from a <a href="https://recipe-folder.com">Recipe Folder</a> user asking how to get the Google Home
integration working with Recipe Folder. I have no idea why they thought that there was an integration, but it got me
thinking, maybe I should add one. I hadn’t looked at <a href="https://en.wikipedia.org/wiki/Google_Home">Google Home</a> or
<a href="https://en.wikipedia.org/wiki/Amazon_Alexa">Amazon Alexa</a> development before, but I thought it might be fun to add
at least Google Home integration (I own a Google Home but not an Alexa yet) to Recipe Folder. So I responded to the
user saying that there was currently no integration but it was something that I was going to look into.</p>
<p>So I started looking into writing a Google Home app. A bit of research, and I discovered that you don’t really write
Google Home app’s, but you write <a href="https://developers.google.com/actions/">Google Actions</a> that run on any of the Google Assistants (the Google Home being one,
but also there is an assistant built into all modern Android Phones and Tablets and Google has licensed the Assistant to
multiple companies so they can create their own Google Assistants some of which are starting to show up now
lie the <a href="https://9to5google.com/2018/02/22/zolo-mojo-review-google-assistant/">Zolo</a>, <a href="https://www.mobvoi.com/collections/tichome-mini">TicHome Mini</a>,
the <a href="https://www.engadget.com/2018/01/29/sony-lf-s50g-review-google-assistant-speaker/">Sony LF-S50G</a>,
the <a href="https://www.jbl.com/voice-activated-speaker/JBL+LINK+10.html">JBL Link 10</a>, and
soon to be many others). Most of the tutorials that I
discovered were built around <a href="https://dialogflow.com/">api.ai</a> which is now called dialogflow, and used Google
Cloud platform to run. I needed access to all the data on my own servers, so I wanted to write a stand-alone that didn’t
require outside services (other than the Google Action API, obviously). While there were some example apps from
Google in the <a href="https://github.com/actions-on-google">Actions of Google Github Page</a>, these mostly seemed to be using
Firebase or Google cloud, so I figured that someone should write a simple tutorial on how to get a Google Action
running on your own server from nothing. We are going to write a very simple game (in the next article) you can quickly play with your
Google Assistant, <a href="https://bigbangtheory.wikia.com/wiki/Rock_Paper_Scissors_Lizard_Spock">Rock, Paper, Scissors, Lizard, Spock</a>, in the third
article in the series we are going keep track of your winnings and losings over time so we will required a login.<br />
To this end we are going to include <a href="https://oauth.net/2/">OAuth 2.0</a> and <a href="https://developers.google.com/actions/identity/account-linking">Account Linking</a>,
so we can store those wins and losses (and because one of the larger hassles I had writing my integration was implementing the OAuth 2 integration).</p>
<h2 id="intended-audience">Intended Audience</h2>
<p>Although anyone who has some experience with command line tooling and programming should be able to follow this article, it assumes that you
have some familiarity with <a href="https://nodejs.org/en/">NodeJS</a>, <a href="https://developer.mozilla.org/bm/docs/Web/JavaScript">JavaScript</a>, and
you will need a Google Account. Having a Google Home for testing is nice, but is not required as you can test your app on either your
Phone or the Simulator. If you are unfamiliar with <a href="http://es6-features.org/#Constants">modern JavaScript</a> (<a href="https://en.wikipedia.org/wiki/ECMAScript#6th_Edition_-_ECMAScript_2015">ES6 or ES2015</a>), I will explain some of the new features as we go along. For now just know that
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const">const</a> is a var that cannot be changed after being
initialized (if you want to read it as var, go ahead), and other things will be explained as we run into them. Although this looks like
a fairly long article (and that is the reason it is broken into 3 parts), getting your first application up and running should take
under half an hour if everything goes right.</p>
<h2 id="required-tooling">Required Tooling</h2>
<p>We are going to do this in nodejs, using the <a href="https://github.com/actions-on-google/actions-on-google-nodejs">Action on Google Node Client Library</a>,
<a href="https://ngrok.com">ngrok.io</a> to provide a tunnel from the open web, to our application. In the end, we will us <a href="https://www.mongodb.com/">MongoDb</a> to store
that user login and the score from the games. The web server that servers both the JSON, and the login web pages is going to be Express, and we will be using modern
Javascript so you will need NodeJS at least 8 installed. The more OAuth and Mongo is coming in a later installment, so
for now just make sure that you have the following installed:</p>
<ol>
<li><a href="https://nodejs.org/en/">NodeJS LTS</a> (8.9.4 at the time of writing this, but feel free to go with 9.5 which is the current).</li>
<li><a href="https://ngrok.com/download">NGrok client</a> and have that available on your path. You will need to sign up for a Free Account as well.</li>
<li><a href="https://developers.google.com/actions/tools/gactions-cli">GActions CLI</a> and have that available on your path. You will need a Google Account, and if you have never used any Google API’s or Cloud Services you may be required to click through some agreements.</li>
</ol>
<p>I’m doing all of this on Windows, but it will certainly work on OSX or Linux. I’m also using <a href="https://www.jetbrains.com/webstorm/">WebStorm</a>,
but any editor can be used, however I recommend one like WebStorm or <a href="https://code.visualstudio.com/">Visual Studio Code</a> where you
can debug in the same IDE that you are editing in but there are <a href="https://nodejs.org/en/docs/inspector/">relatively clear instructions</a> on how to get up and debugging on the NodeJS site.</p>
<h2 id="getting-started">Getting Started</h2>
<h3 id="packagejson">Package.json</h3>
<p>First thing we are going to do is make a <code class="highlighter-rouge">package.json</code> file.</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
</span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"google-actions-rpsls"</span><span class="p">,</span><span class="w">
</span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Rock Paper Scissors Lizard Spock as a Google Action"</span><span class="p">,</span><span class="w">
</span><span class="nt">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.0.1"</span><span class="p">,</span><span class="w">
</span><span class="nt">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"actions-on-google"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^1.7.0"</span><span class="p">,</span><span class="w">
</span><span class="nt">"express"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^4.16.2"</span><span class="p">,</span><span class="w">
</span><span class="nt">"body-parser"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^1.18.2"</span><span class="p">,</span><span class="w">
</span><span class="nt">"ejs"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^2.5.7"</span><span class="p">,</span><span class="w">
</span><span class="nt">"mongoose"</span><span class="p">:</span><span class="w"> </span><span class="s2">"~4.13.6"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nt">"scripts"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"start"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node index.js"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span></code></pre></figure>
<p>First, lets download the dependencies, type:</p>
<p><code class="highlighter-rouge">npm install</code></p>
<p>Since there are no comments in JSON, let’s go through the various dependencies.</p>
<h4 id="actions-on-google">Actions on Google</h4>
<p>Actions-on-google is the library from Google that is going to allow us to quickly communicate with Google Actions without having to manually parse all the input from Google Action, and having to write all the JSON responses. It provides a fairly nice, and <a href="https://developers.google.com/actions/reference/nodejs/AssistantApp">relatively well documented</a> API.</p>
<h4 id="express">Express</h4>
<p>Although actions-on-google appears to be designed for the <a href="https://cloud.google.com/functions/docs/writing/http">Google Cloud Platform Http Functions</a>, according to documention, the Google Cloud Platform have the same properties as the ExpressJS <a href="http://expressjs.com/en/4x/api.html#req">Request</a> and <a href="http://expressjs.com/en/4x/api.html#res">Response</a> objects. Thus instead of installing the Google Cloud Platform simulator, or running on the Google Cloud Platform we can use <a href="http://expressjs.com">ExpressJS</a> instead.</p>
<h4 id="body-parser">Body Parser</h4>
<p>In version 4 of Express, it was componentized. Body parser allow reading of the HTTP Posts and placing their contents in req.body, there are a lot of other
enhancements and niceties (serve-favicon, compression, session handling, etc) that could be added to improve the app but we’ll leave those as exercises for the user to keep the code as simple as possible. Note: The Actions on Google requires that body-parser be added as <a href="https://expressjs.com/en/guide/using-middleware.html">middleware</a> to Express or it will not work.</p>
<h4 id="ejs">Ejs</h4>
<p>We are going to need a login page eventually, and I am using <a href="http://ejs.co/">EJS</a> as the templating engine, we won’t need it until a couple of articles down the line (in the third article in the series), but lets set up the package.json now.</p>
<h4 id="mongoose">Mongoose</h4>
<p>Eventually (once again, the third article of the series) we are going store our user accounts in Mongo, and <a href="http://mongoosejs.com/">Mongoose</a> is nice <a href="https://en.wikipedia.org/wiki/Object-relational_mapping">ORM</a> wrapper around Mongo.</p>
<h3 id="rpslsjson">RPSLS.json</h3>
<p>Since our app is Rock Paper Lizard Spock we are going to use the acronym rpsls all over the place. The first place we are going to do that is for the project.json file for Google Actions (or gactions). Create a file <code class="highlighter-rouge">rpsls.json</code> and populate it with:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="p">{</span><span class="w">
</span><span class="nt">"actions"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nt">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"RPSLS Welcome Intent"</span><span class="p">,</span><span class="w">
</span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"MAIN"</span><span class="p">,</span><span class="w">
</span><span class="nt">"fulfillment"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"conversationName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"main"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nt">"intent"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"actions.intent.MAIN"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nt">"conversations"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"main"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"main"</span><span class="p">,</span><span class="w">
</span><span class="nt">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://CHANGE-ME.ngrok.io"</span><span class="p">,</span><span class="w">
</span><span class="nt">"fulfillmentApiVersion"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span></code></pre></figure>
<p>Note the CHANGE-ME in the conversations, eventually when we get your NGROK connection setup, you will have to change that URL.</p>
<p>In the third article we are going to be adding to this file, to enable authentication, but for now this will do. This file is currently divided into 2 sections, actions and conversations and only describes one action. The actions described in <a href="https://developers.google.com/actions/reference/rest/Shared.Types/ActionPackage">ActionPackage</a> are actions that are available without having started a conversation (we will code other Actions later, but they will not need to be described in the actions file since they are only available once a conversation is started). Each action must have a name (according to the documentation they do not have a an intent or description if the name of the intent is a <a href="https://developers.google.com/actions/reference/rest/intents">built-in intent</a>, for example action.intent.MAIN – but I haven’t tested that), and should have an intent, a fulfillment and a description. The fulfillment must have a field conversationName, and that has to point to a name in the conversations section.</p>
<p>The conversations section has a collection of conversations, which have a name, a url and fulfillmentApiVersion. Make sure the name of the conversation matches the collection name, and the fulfillmentApiVersion is set to 2 (Api 1 was very different, and will not be supported much longer and doesn’t work with actions-on-google npm module).</p>
<h3 id="set-up-a-project">Set Up A Project</h3>
<p>Go to the “Actions on Google Console” by going to <a href="https://console.actions.google.com/">https://console.actions.google.com/</a>.</p>
<p><img src="/img/google-home/step1.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>Click on Add/Import Project, and call it rpsls. Click on Create Project.</p>
<p><img src="/img/google-home/step2.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>Now click on the “Actions SDK” build button:</p>
<p><img src="/img/google-home/step3.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>And you will see that your project has been created.</p>
<p><img src="/img/google-home/step4.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>Now copy the piece of text from the second step and paste it into a command prompt where the active directory is the project root
(the directory where you created the <code class="highlighter-rouge">rpsls.json</code> and <code class="highlighter-rouge">package.json</code>) – <strong>don’t hit return yet</strong>. It should look something like:</p>
<p><code class="highlighter-rouge">gactions update --action_package PACKAGE_NAME --project rpsls-XXXXX</code></p>
<p>Where the XXXXX will be a unique number assigned to you by Google. Replace PACKAGE_NAME with rpsls.json and hit enter:</p>
<p><code class="highlighter-rouge">gactions update --action_package rpsls.json --project rpsls-XXXXX</code></p>
<p>If everything goes right, you will be told (did you forget to <a href="https://developers.google.com/actions/tools/gactions-cli">download</a> and place the gactions CLI in your path, if so do that now.)</p>
<div class="highlighter-rouge"><pre class="highlight"><code>Gactions needs access to your Google account. Please copy & paste the URL below into a web browser and follow the instructions there. Then copy and paste the authorization code from the browser back here.
Visit this URL:
https://accounts.google.com/o/oauth2/auth?access_type=offline&c.....
</code></pre>
</div>
<p>Once you have visited the URL paste the authorization code into the command prompt your app should be ready for testing. Except we don’t have an app.</p>
<h3 id="the-first-app">The First App</h3>
<p>Create a new file index.js (of course you could call it app.js or google-actions.js or rpsls.js) in the root directory of our project.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">ActionsSdkApp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'actions-on-google'</span><span class="p">).</span><span class="nx">ActionsSdkApp</span><span class="p">;</span>
<span class="kr">const</span> <span class="nx">express</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'express'</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">bodyParser</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'body-parser'</span><span class="p">);</span>
<span class="kr">const</span> <span class="nx">expressApp</span> <span class="o">=</span> <span class="nx">express</span><span class="p">();</span>
<span class="nx">expressApp</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">bodyParser</span><span class="p">.</span><span class="nx">urlencoded</span><span class="p">({</span><span class="na">extended</span><span class="p">:</span> <span class="kc">false</span><span class="p">}));</span>
<span class="nx">expressApp</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">bodyParser</span><span class="p">.</span><span class="nx">json</span><span class="p">({</span><span class="na">extended</span><span class="p">:</span> <span class="kc">false</span><span class="p">}));</span>
<span class="kd">function</span> <span class="nx">mainIntentHandler</span><span class="p">(</span><span class="nx">app</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">app</span><span class="p">.</span><span class="nx">ask</span><span class="p">(</span><span class="s1">'Hey Ma, It works!'</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">expressApp</span><span class="p">.</span><span class="nx">use</span><span class="p">((</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Request body: '</span> <span class="o">+</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">request</span><span class="p">.</span><span class="nx">body</span><span class="p">));</span>
<span class="kr">const</span> <span class="nx">actionApp</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ActionsSdkApp</span><span class="p">({</span>
<span class="nx">request</span><span class="p">,</span>
<span class="nx">response</span>
<span class="p">});</span>
<span class="c1">// Map that contains the intents and respective handlers to be used by the</span>
<span class="c1">// actions client library</span>
<span class="kr">const</span> <span class="nx">actionMap</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Map</span><span class="p">();</span>
<span class="nx">actionMap</span><span class="p">.</span><span class="nx">set</span><span class="p">(</span><span class="nx">actionApp</span><span class="p">.</span><span class="nx">StandardIntents</span><span class="p">.</span><span class="nx">MAIN</span><span class="p">,</span> <span class="nx">mainIntentHandler</span><span class="p">);</span>
<span class="nx">actionApp</span><span class="p">.</span><span class="nx">handleRequestAsync</span><span class="p">(</span><span class="nx">actionMap</span><span class="p">).</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Success handling GoogleAction request'</span><span class="p">);</span>
<span class="p">},</span> <span class="p">(</span><span class="nx">reason</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Error: '</span> <span class="o">+</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">reason</span><span class="p">));</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="nx">expressApp</span><span class="p">.</span><span class="nx">listen</span><span class="p">(</span><span class="mi">5050</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Example app listening on port '</span> <span class="o">+</span> <span class="mi">5050</span><span class="p">));</span></code></pre></figure>
<p>OK, the app is really simple at this point. The first bit of code just loads the libraries and sets up Express (if you don’t understand that part
of Node, Mozilla has great <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">tutorial</a> on getting started
with Node). We set the main express <a href="http://expressjs.com/en/api.html#app.use">middleware function</a> to be our actions SDK handler.</p>
<p>We could also just make this a <a href="http://expressjs.com/en/api.html#app.post">post handler</a> like this:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">expressApp</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'/'</span><span class="p">,</span> <span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span></code></pre></figure>
<p>Also if you are new to the whole ES6 thing, the lambda expression
is the same as:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">expressApp</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">'/'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">)</span> <span class="p">{</span></code></pre></figure>
<p>Now lets look at what we are doing in our Middleware function (post handler, whatever you want to call it). The log on the first line
is just so you can see what Google Actions is sending us each time. It will look something like this:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="err">Request</span><span class="w"> </span><span class="err">body:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"user"</span><span class="p">:{</span><span class="w">
</span><span class="nt">"userId"</span><span class="p">:</span><span class="s2">"ABwppHH...cTfKaFJo"</span><span class="p">,</span><span class="w">
</span><span class="nt">"locale"</span><span class="p">:</span><span class="s2">"en-US"</span><span class="p">,</span><span class="w">
</span><span class="nt">"lastSeen"</span><span class="p">:</span><span class="s2">"2018-02-04T17:32:26Z"</span><span class="p">},</span><span class="w">
</span><span class="nt">"conversation"</span><span class="p">:{</span><span class="w">
</span><span class="nt">"conversationId"</span><span class="p">:</span><span class="s2">"1517765608314"</span><span class="p">,</span><span class="w">
</span><span class="nt">"type"</span><span class="p">:</span><span class="s2">"NEW"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nt">"inputs"</span><span class="p">:[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nt">"intent"</span><span class="p">:</span><span class="s2">"actions.intent.MAIN"</span><span class="p">,</span><span class="w">
</span><span class="nt">"rawInputs"</span><span class="p">:[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nt">"inputType"</span><span class="p">:</span><span class="s2">"VOICE"</span><span class="p">,</span><span class="w">
</span><span class="nt">"query"</span><span class="p">:</span><span class="s2">"talk to my test app"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nt">"surface"</span><span class="p">:{</span><span class="w">
</span><span class="nt">"capabilities"</span><span class="p">:[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"actions.capability.AUDIO_OUTPUT"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"actions.capability.MEDIA_RESPONSE_AUDIO"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nt">"isInSandbox"</span><span class="p">:</span><span class="kc">true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></figure>
<p>which you will see shortly when you get your App up and running.</p>
<p>Next we construct our actionsApp, there isn’t any <a href="https://developers.google.com/actions/reference/nodejs/ActionsSdkApp">documentation</a> for what you can send to the constructor,
but from the source code we can see is that the only three things it takes is a request, response, and sessionStarted function. If you aren’t 100% familiar with modern ES6 (ES2015)
syntax, note that</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">actionApp</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ActionsSdkApp</span><span class="p">({</span>
<span class="nx">request</span><span class="p">,</span>
<span class="nx">response</span>
<span class="p">});</span></code></pre></figure>
<p>is just <a href="https://en.wikipedia.org/wiki/Syntactic_sugar">syntactic sugar</a> for</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kr">const</span> <span class="nx">actionApp</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ActionsSdkApp</span><span class="p">({</span>
<span class="na">request</span><span class="p">:</span> <span class="nx">request</span><span class="p">,</span>
<span class="na">response</span><span class="p">:</span> <span class="nx">response</span>
<span class="p">});</span></code></pre></figure>
<p>Next we create Map of actions to event handlers. And finally we handle the request based on the actionMap we created above. Note that there are two ways
to handle the request, we could have also done the more succinct (and from what I have seen more common):</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">actionApp</span><span class="p">.</span><span class="nx">handleRequest</span><span class="p">(</span><span class="nx">actionMap</span><span class="p">);</span></code></pre></figure>
<p>However, then you get no idea of what the error was when it happened, and usually just a not very useful unhandled promise request.</p>
<p>Finally we have our very simple mainIntent handler which just speaks to the user <a href="https://ia801708.us.archive.org/BookReader/BookReaderImages.php?zip=/13/items/Basic_Computer_Language_Its_Easier_Than_You_Think_1978_Radio_Shack/Basic_Computer_Language_Its_Easier_Than_You_Think_1978_Radio_Shack_jp2.zip&file=Basic_Computer_Language_Its_Easier_Than_You_Think_1978_Radio_Shack_jp2/Basic_Computer_Language_Its_Easier_Than_You_Think_1978_Radio_Shack_0009.jp2">“Hey Ma, It Works”</a>. The ask
function takes a lot of possible arguments, and has builders and we will get into the complexities of how it works in the next tutorial, but for now know that you
can send it is simple string and the Google Assistant will read it. OK, now that we have a working script lets get this show on the road.</p>
<h3 id="tunneling-with-ngrokio">Tunneling with NGrok.io</h3>
<p>Since we are keeping things simple, and not requiring a VPS or doing something sketchy with our firewall or setting up <a href="https://en.wikipedia.org/wiki/Dynamic_DNS">Dynamic DNS</a> and since Google Actions wants to be on HTTPS, we are going to use <a href="https://ngrok.com/">ngrok</a> to forward HTTPS from the internet to our app running on port 5050 on our PC. It is pretty self explanatory get setup with ngrok (follow the instructions on their site), and don’t worry, for what we are doing it is completely free. Once ngrok is installed, you have signed up with an account, start up a tunnel by typing:</p>
<p><code class="highlighter-rouge">ngrok http 5050</code></p>
<p>It will look something like:</p>
<pre><code class="language-$xslt">ngrok by @inconshreveable (Ctrl+C to quit)
Session Status online
Account Kris Erickson (Plan: Free)
Version 2.2.8
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://dbbe2688.ngrok.io -> localhost:5050
Forwarding https://dbbe2688.ngrok.io -> localhost:5050
Connections ttl opn rt1 rt5 p50 p90
7 0 0.00 0.00 8.42 20.98
</code></pre>
<p>Copy the https url (in this case https://dbbe2688.ngrok.io) and paste it into your rpsls.json converstation url:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="w"> </span><span class="s2">"conversations"</span><span class="err">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"main"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"main"</span><span class="p">,</span><span class="w">
</span><span class="nt">"url"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://dbbe2688.ngrok.io"</span><span class="p">,</span><span class="w"> </span><span class="err">⇐</span><span class="w"> </span><span class="err">CHANGE</span><span class="w"> </span><span class="err">THIS</span><span class="w"> </span><span class="err">LINE</span><span class="w">
</span><span class="nt">"fulfillmentApiVersion"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span></code></pre></figure>
<p>Now we have to tell Google Actions that we have change our url, so go back to the command line and type (where rpsls-XXXXX is the same project name you received earlier)</p>
<p><code class="highlighter-rouge">gactions update --action_package rpsls.json --project rpsls-XXXXX</code></p>
<p>Wow, finally we are ready for testing!</p>
<h3 id="finally-testing-the-app">Finally, testing the app</h3>
<p>We need to start the app up and running, which is as simple as</p>
<p><code class="highlighter-rouge">node index.js</code></p>
<p>Eventually you might want to debug the app, either in WebStorm, Visual Studio Code or the Node Inspector, but be aware that you only get less than half a minute before the request times out and Google Assistant tells you that “my test app is not responding”.</p>
<p>Hopefully if all went right, the app is up and running.</p>
<p>We want to go to our Google Actions console, <a href="https://console.actions.google.com/">https://console.actions.google.com/</a> and pick our rpsls project. In the Overview section hit “Test Draft”.</p>
<p>We haven’t set up the “App Information” on purpose, so our App will be called “My Test App”. Feel free to set this up, you will need a unique name for the app, as well as graphics, a description, a url for your privacy policy, etc. In the third article in this series we will come back here to set up account linking, but for now we can just hit “Test Draft”.</p>
<p><img src="/img/google-home/step5.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>You should see the simulator in all it’s Glory. In the input section type</p>
<p><code class="highlighter-rouge">"Talk to my test app"</code></p>
<p><img src="/img/google-home/step6.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>If everything worked, you should see and hear “Hey Ma, It Works!”</p>
<p><img src="/img/google-home/step7.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<h3 id="next-steps">Next Steps</h3>
<p>Now that it is working on the simulator, you can test it on your own Google Home. Note: The account you have logged in to your Google Home must be the account you are developing the App under. The same is true for testing on a device, and remember unless you fill out the App Information the name of the App is “My Test App”.</p>
<p>Ok, now that we have an app up and working, we’ll start learning more about the actual ActionsAppSDK in the <a href="/programming/2018/02/08/creating-a-google-home-app-part-2-the-game/">next tutorial</a>
and build out our program to actually play a game. While you are waiting, you might want to start messing around with the program we have created.<br />
Perhaps asking more questions, or instead of using plain text use <a href="https://developers.google.com/actions/reference/ssml">SSML</a> (Speech Synthesis Markup Language ) to make more complex speech.
The methods available to the app object in the GoogleActionsSDK are documented <a href="https://developers.google.com/actions/reference/nodejs/ActionsSdkApp#methods">here</a>. Until next time, have fun!</p>
<p>Oh, and all the code for this project can be found on <a href="https://github.com/kriserickson/google-actions-rpsls">GitHub</a>. This first article is is divided into 2 tags if you want to follow along,
first there is generating the <a href="https://github.com/kriserickson/google-actions-rpsls/tree/PART_1_STEP_1">JSON files</a>, and finally the full code for this article is
<a href="https://github.com/kriserickson/google-actions-rpsls/tree/PART_1_STEP_2">https://github.com/kriserickson/google-actions-rpsls/tree/PART_1_STEP_2</a>.<br />
If you see any bugs, or have suggestions feel free to do so either in the comments below, or as an issue or pull request in the
<a href="https://github.com/kriserickson/google-actions-rpsls">GitHub repo</a>.</p>
<p><a href="http://agingcoder.com/programming/2018/02/04/creating-a-google-home-app/">Creating A Google Home App</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on February 04, 2018.</p>http://agingcoder.com/programming/2017/10/15/flutter-a-quick-look-at-a-new-cross-platform-mobile-app-toolkit2017-10-15T00:00:00-07:002017-10-15T00:00:00-07:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<h4 id="introduction">Introduction</h4>
<p>Well over a year ago, Google (well, at least Google developers built it – it’s said to be made with “contributions by Google and the community”<sup><a href="https://flutter.io/faq/#who-makes-flutter">ref</a></sup>), introduced yet another Mobile Toolkit that allows building apps for both iOS and Android.
Although it is still boldly declared as an alpha release (even after over year and a half), it differs from most
of the cross platform toolkits in some interesting ways so I decided to spend some time with it to see if it was
shaping into a reasonable solution for creation cross platform Apps.</p>
<h4 id="what-exactly-is-flutter">What Exactly is Flutter</h4>
<p>Flutter is a bit of a strange beast – it runs fully natively, but uses Dart as its “lingua franca” (as far as I understand compiling
Dart into optionally Java or Kotlin on Android and into Object-C or Swift on iOS).</p>
<p><img src="/img/flutter/new_project.png" style="border: 1px solid #000; margin: 10px auto 0" /></p>
<p>It doesn’t use native widgets, but uses its own C++ Rendering (written with <a href="https://en.wikipedia.org/wiki/Skia_Graphics_Engine">Skia</a>)
to quickly draw native-like widgets. The authors state that they are doing this to ship higher quality widgets otherwise
“the quality and performance of Flutter apps would be limited by the quality of those widgets”<sup><a href="https://flutter.io/faq/#does-flutter-use-my-systems-oem-widgets">ref</a></sup>.
They claim to be inspired by React’s style, and use code rather than markup to create UI, and not even in the JSX sense but literally
you create UI by creating objects as seen in the definition of a ChatWindow from their tutorial:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"> <span class="n">@override</span>
<span class="n">Widget</span> <span class="nf">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">FadeTransition</span><span class="p">(</span>
<span class="n">opacity</span><span class="p">:</span> <span class="k">new</span> <span class="nf">CurvedAnimation</span><span class="p">(</span>
<span class="n">parent</span><span class="p">:</span> <span class="n">animationController</span><span class="p">,</span> <span class="n">curve</span><span class="p">:</span> <span class="n">Curves</span><span class="p">.</span><span class="n">easeOut</span><span class="p">),</span>
<span class="n">child</span><span class="p">:</span> <span class="k">new</span> <span class="nf">Container</span><span class="p">(</span>
<span class="n">margin</span><span class="p">:</span> <span class="k">const</span> <span class="n">EdgeInsets</span><span class="p">.</span><span class="nf">symmetric</span><span class="p">(</span><span class="n">vertical</span><span class="p">:</span> <span class="m">10.0</span><span class="p">),</span>
<span class="n">child</span><span class="p">:</span> <span class="k">new</span> <span class="nf">Row</span><span class="p">(</span>
<span class="n">crossAxisAlignment</span><span class="p">:</span> <span class="n">CrossAxisAlignment</span><span class="p">.</span><span class="n">start</span><span class="p">,</span>
<span class="n">children</span><span class="p">:</span> <span class="p"><</span><span class="n">Widget</span><span class="p">>[</span>
<span class="k">new</span> <span class="nf">Container</span><span class="p">(</span>
<span class="n">margin</span><span class="p">:</span> <span class="k">const</span> <span class="n">EdgeInsets</span><span class="p">.</span><span class="nf">only</span><span class="p">(</span><span class="n">right</span><span class="p">:</span> <span class="m">16.0</span><span class="p">),</span>
<span class="n">child</span><span class="p">:</span> <span class="k">new</span> <span class="nf">CircleAvatar</span><span class="p">(</span><span class="n">child</span><span class="p">:</span> <span class="k">new</span> <span class="nf">Text</span><span class="p">(</span><span class="n">_name</span><span class="p">[</span><span class="m">0</span><span class="p">])),</span>
<span class="p">),</span>
<span class="k">new</span> <span class="nf">Column</span><span class="p">(</span>
<span class="n">crossAxisAlignment</span><span class="p">:</span> <span class="n">CrossAxisAlignment</span><span class="p">.</span><span class="n">start</span><span class="p">,</span>
<span class="n">children</span><span class="p">:</span> <span class="p"><</span><span class="n">Widget</span><span class="p">>[</span>
<span class="k">new</span> <span class="nf">Text</span><span class="p">(</span><span class="n">_name</span><span class="p">,</span> <span class="n">style</span><span class="p">:</span> <span class="n">Theme</span><span class="p">.</span><span class="nf">of</span><span class="p">(</span><span class="n">context</span><span class="p">).</span><span class="n">textTheme</span><span class="p">.</span><span class="n">subhead</span><span class="p">),</span>
<span class="k">new</span> <span class="nf">Container</span><span class="p">(</span>
<span class="n">margin</span><span class="p">:</span> <span class="k">const</span> <span class="n">EdgeInsets</span><span class="p">.</span><span class="nf">only</span><span class="p">(</span><span class="n">top</span><span class="p">:</span> <span class="m">5.0</span><span class="p">),</span>
<span class="n">child</span><span class="p">:</span> <span class="k">new</span> <span class="nf">Text</span><span class="p">(</span><span class="n">text</span><span class="p">),</span>
<span class="p">),</span>
<span class="p">],</span>
<span class="p">),</span>
<span class="p">],</span>
<span class="p">),</span>
<span class="p">));</span>
<span class="p">}</span></code></pre></figure>
<p>While I am not opposed to this methodology (and in the future if Flutter got huge I am sure some kind of Visual Designer could
be written for it), I find it interesting that they would specifically state that they are targeting “designers and prototypers” for
this Toolkit, and yet chose to make a very non-visual (I know that HTML and XML is not visually representational of what is produced
but it I think it is easier to picture than a Object Definition). That said, the Hot Reload functionality may be a way to quickly
iterate though ideas and bring a native like app to production faster than the slower build process of Android Studio or XCode.</p>
<h4 id="dart-is-the-language">Dart is the Language</h4>
<p>Clearly Dart is not as dead a language as I thought it was. While the <a href="https://webdev.dartlang.org/tools/dartium">Dartium browser is dead</a>,
and Dart as the language to replace Javascript is clearly not the success it was hoped it be (used by Google but by few others, kind
of on the scale of <a href="https://kotlinlang.org/docs/tutorials/javascript/kotlin-to-javascript/kotlin-to-javascript.html">Kotlin for Web</a>,
<a href="http://elm-lang.org/">Elm</a>, <a href="https://github.com/clojure/clojurescript">clojurescript</a>, or <a href="http://www.gwtproject.org/">GWT</a>. Used
by a few developers who love it, but not really setting the Development on fire.</p>
<p>Perhaps Flutter was seen as a way to bring prominence to Dart, maybe other more popular languages didn’t have either the
syntax or the toolchain required for Flutter, but the choice of the <a href="https://flutter.io/faq/#why-did-flutter-choose-to-use-dart">Dart language does kind of stick out</a>.
Not that it’s a bad language, by any stretch of the imagination, it’s a nice modern language that will feel very familiar
to anyone who has used an Object Oriented language developed in the past 10 years (or almost 20 years for C#).
It’s just a big ask to get new developers to learn a language that is not very widely used. It’s not hard to lean,
and anyone with Familiarity with any of the <a href="https://en.wikipedia.org/wiki/ALGOL">Algol</a>
family of languages (C, Java, C#, and to a lesser extent Javascript) will be able to pick it up relatively quickly, but
one might of thought with the recent push to <a href="https://blog.jetbrains.com/kotlin/2017/05/kotlin-on-android-now-official/">Kotlin</a> in
Android, Google might have chosen Kotlin as the language to use – it certainly seems capable in both syntax and <a href="https://github.com/JetBrains/kotlin-native">toolchain</a>
but perhaps they hesitant to use a language that they don’t have as much control of.</p>
<h4 id="intellij-is-the-ide">Intellij is the IDE</h4>
<p>Another interesting thing about Flutter is that the IDE they support (though you can build Flutter apps without any IDE and you
text editor of choice) is <a href="https://www.jetbrains.com/idea">Intellij</a> and not Android Studio (this might be a licensing issue
with how you can use the Intellij Platform and Plugins and such, but it is still interesting). Note: you can use the community
edition, and are not required to buy the Enterprise Edition, so don’t let that dissuade you from using Intellij if you do decide
to do some Flutter development.</p>
<p>The polish that the Intellij Flutter Plugin has (especially since Flutter is still considered Alpha) is very impressive, and I
don’t know if that speaks to how easy it is to extend Intellij or how good the developers of Flutter are at working with Intellij (maybe
a bit of both). Intellisense, Code Inspection, Debugging with hot-reload, is all seamless and higher quality than a lot of the
non-Jetbrains language plugins that I have used. For me, using Jetbrains products every day, the decision to use Intellij rather than
writing their own or piggybacking on the Chrome Dev Tools is huge plus.</p>
<h4 id="documentation">Documentation</h4>
<p>The <a href="https://flutter.io">Flutter site</a> is very polished and provides a great way to get started with Flutter. They have Code-Labs
that walk you through not only installing the SDK, but building some relatively complicated applications (including a <a href="https://codelabs.developers.google.com/codelabs/flutter-firebase/#0">Chat Application
that supports Firebase on the Backend</a>). While the documentation
isn’t perfectly organized, almost everything you need to know is there if you look hard enough (another quibble is that the search
is just a Google Search against flutter.io, but it mostly works). It’s nice to see that they took documentation and the getting started
tutorials so seriously. BTW if you are going to develop for Flutter, there is not only the <a href="https://flutter.io">main information page</a>,
but also the harder to find <a href="https://docs.flutter.io/index.html">Docs Page</a> – yes I know it should be obvious but it took me a while
to find and it is kind of essential for doing actual flutter development.</p>
<h4 id="material-design">Material Design</h4>
<p>Another thing to point out, is that Flutter Apps look great from the get go. They look like they are using native widgets (even though they
aren’t), and you don’t have to much to get a pretty good looking app form the get-go. While you do have to do some mucking around with
the strange <a href="https://flutter.io/widgets/cupertino/">“Cupertino Widgets”</a> to get the iOS look and feel, I think we can all agree that code
that looks like the following is ugh:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="n">child</span><span class="p">:</span> <span class="n">Theme</span><span class="p">.</span><span class="nf">of</span><span class="p">(</span><span class="n">context</span><span class="p">).</span><span class="n">platform</span> <span class="p">==</span> <span class="n">TargetPlatform</span><span class="p">.</span><span class="n">iOS</span>
<span class="p">?</span> <span class="k">new</span> <span class="nf">CupertinoButton</span><span class="p">(</span>
<span class="n">child</span><span class="p">:</span> <span class="k">new</span> <span class="nf">Text</span><span class="p">(</span><span class="s">"Send"</span><span class="p">),</span>
<span class="n">onPressed</span><span class="p">:</span> <span class="n">_isComposing</span>
<span class="p">?</span> <span class="p">()</span> <span class="p">=></span> <span class="nf">_handleSubmitted</span><span class="p">(</span><span class="n">_textController</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
<span class="p">:</span> <span class="k">null</span><span class="p">)</span>
<span class="p">:</span> <span class="k">new</span> <span class="nf">IconButton</span><span class="p">(</span>
<span class="n">icon</span><span class="p">:</span> <span class="k">new</span> <span class="nf">Icon</span><span class="p">(</span><span class="n">Icons</span><span class="p">.</span><span class="n">send</span><span class="p">),</span>
<span class="n">onPressed</span><span class="p">:</span> <span class="n">_isComposing</span>
<span class="p">?</span> <span class="p">()</span> <span class="p">=></span> <span class="nf">_handleSubmitted</span><span class="p">(</span><span class="n">_textController</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
<span class="p">:</span> <span class="k">null</span><span class="p">),</span></code></pre></figure>
<p>It’s no worse than the separate project files you need in <a href="https://facebook.github.io/react-native/">React-Native</a>, similar
if contortions you have to do in <a href="http://docs.nativescript.org/">NativeScript</a>, and CSS fixes required for nice Cross Platform
<a href="http://cordova.apache.org/">Cordova Apps</a>.</p>
<h4 id="api-integration">Api Integration</h4>
<p>As anyone who has built a real app on Cordova, or with any other cross platform toolkit, you know that unless you are
making a very simple <a href="https://en.wikipedia.org/wiki/Line_of_business">Line of Business App</a> you will need to use the
Native API’s at some point – otherwise you might as well just be building a mobile web page. Flutter supports API
calls with <a href="https://flutter.io/using-packages/">packages</a>, and has around <a href="https://pub.dartlang.org/flutter/packages">50 pre-built in packages</a>
that have a lot of functionality of the various Cordova plugins. And there are fairly simple instructions
for creating your own <a href="https://flutter.io/developing-packages/">plugins</a> or calling platform specific
code using <a href="https://flutter.io/platform-channels/">platform channels</a>. I admit that I did not actually write one of these
myself, but I did play with platform-channels on Android (the platform I am most familiar with) and it seems
on the surface to be pretty well thought out. I also looked at the source code from a couple of the more simple
plugins like <a href="https://github.com/Lyokone/flutterlocation">location</a> and it didn’t seem too challenging to write a simple plugin.</p>
<h4 id="overall">Overall</h4>
<p>I was happily surprised by how polished and non-frustrating (unlike, for example ReactNative) it was to get up and started with
Flutter. I haven’t built anything of size or import with it yet (and to be completely honest, I don’t know exactly what set
of circumstances would push me towards building an App in Flutter even when it it was deemed to be 1.0 release), but the
App I built was built in a few hours and with very little gnashing of teeth or pulling out my hair. The IDE experience was
polished and a breeze to use, and errors actually made sense and were easy to debug and fix.</p>
<h4 id="why-i-would-consider-it">Why I would Consider It</h4>
<ul>
<li>Cross Platform - I think that building an App on two platforms with Flutter rather than just one wouldn’t add too much extra dev time.</li>
<li>Hot Reload - Works pretty well, sometimes it gets messed up if you <a href="https://flutter.io/faq/#hot-reload">change your class a little too much</a>, but that is to be expected.</li>
<li>Unit Testing - I didn’t dig too deep into the unit testing, but it is clear that there is a <a href="https://flutter.io/testing/">deep testing infrastructure</a>.</li>
<li>Looks good by default - Clean material design, even if it isn’t native, does look good. Easily supports animation.</li>
<li>Great IDE Integration - The installing SDK to having an App Running and Debugging is faster than anything I have used yet (even native or Cordova).</li>
</ul>
<h4 id="what-would-give-me-pause-to-develop-an-app-in-flutter">What would give me pause to develop an App in Flutter</h4>
<ul>
<li>Still an alpha,</li>
<li>Hiring support, how many developers are familiar with Dart?</li>
<li>Not native widgets (when Apple or Google rolls out a new design language will require rewriting all the C++ code to make it look right).</li>
<li>What if Google or its developers decide to stop supporting it, stuck on a dead platform with no possible re-use of the code.</li>
<li>Not <a href="https://flutter.io/faq/#who-uses-flutter">a lot real world Flutter apps</a> (a bunch of internal Google apps, and the <a href="https://play.google.com/store/apps/details?id=com.hamilton.app">Hamilton Musical</a> app.</li>
<li>A little uncertain of the advantages over the other main non-native widget toolkit – the web and Cordova, where you can get a lot of code-reuse and a ton of developers available are familiar with the paradigm, frameworks and language. Yes Flutter will probably perform a fair bit bitter, but now that <a href="https://github.com/apache/cordova-plugin-wkwebview-engine">WkWebView</a> is more usable now, the only real devices that suffer performance are Android Devices pre-chrome (i.e. Android before 4.4) which are rapidly disappearing (currently less than 10% of the Android market).</li>
</ul>
<p><a href="http://agingcoder.com/programming/2017/10/15/flutter-a-quick-look-at-a-new-cross-platform-mobile-app-toolkit/">Flutter: A quick look at a new Cross Platform Mobile App Toolkit</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on October 15, 2017.</p>http://agingcoder.com/opinion/2017/08/02/what-javascript-framework-should-i-allow-my-developers-to-use2017-08-02T00:00:00-07:002017-08-02T00:00:00-07:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<h3 id="brief">Brief</h3>
<p>This genesis of this article was a conversation attempting to explain the reasons for choosing one Javascript Framework (or really collection of Frameworks) over another to a non Frontend developer. It was remarked that many managers, entrepreneurs, and clients of contractors might find the information useful when thinking about how to develop a new, or in the industry jargon <a href="https://en.wikipedia.org/wiki/Greenfield_project">greenfield</a> project. While this is mostly relevant in choosing a framework for a <a href="https://en.wikipedia.org/wiki/Single-page_application">Single Page Application</a> (or SPA), some of the considerations can be applied to a more traditional page based web application (though some of the heavier Javascript Frameworks do really lend themselves at all well traditional web applications). What is a SPA and when is it the better choice for Web Site (or area of a web site)? A is frequently a website that is used like a more traditional application, some examples of SPAs would be Gmail (or really any modern WebMail), <a href="https://expedia.com">Expedia</a> or any travel or <a href="https://hotels.com">hotel booking application</a>, <a href="https://facebook.com">Facebook</a>, <a href="https://twitter.com">Twitter</a>, etc. Think of sites where the main frame of the website stays relatively constant and only the data within that frame changes. Sites that don’t necessarily lend themselves to being SPA’s are more like Shopping Sites, News Sites, a Blogs, basically any site where you want the content to be indexed by a web search engine. Though there are techniques to make SPA’s indexable and searchable, it is generally a better idea to make that content that dynamically changed by the server rather than using data from server and dynamically updating it on the client.</p>
<h4 id="choosing-a-framework-and-what-is-a-framework-anyway">Choosing a Framework, and what is a Framework Anyway?</h4>
<p>Without understanding the intricacies and minutiae of Front End Development it is tempting to give your developers either a free reign in the choice of technologies they use to develop a product (“I don’t care how you get it done as long as it works”) or being overly proscriptive (“Angular is the framework everyone uses, so we are using Angular”), but a little understanding of what Frameworks are, how they effect the development and maintenance of your project, and yes even a little history of Frameworks will allow you to make a more educated determination of how proscriptive and how lax to be about the choice of a Javascript Framework.</p>
<p>A Framework is a collection of libraries (code whether that be Javascript, CSS, or server side programming language) that enables the programmer to more quickly develop a project. Front-end frameworks have really only been around for the last 10 years or so (<a href="https://en.wikipedia.org/wiki/Dojo_Toolkit">Dojo</a> is generally thought of as the first real Javascript Framework), but have been around in Desktop Client world for a very long time (dating back to the early history of GUI’s).</p>
<h3 id="a-bit-of-history">A Bit of History</h3>
<p>I might as well at this point digress to a bit of history which might better explain what a Framework is. In the beginning of the world wide web, what <a href="https://en.wikipedia.org/wiki/Tim_Berners-Lee">Tim Berners Lee</a> created was basically the ability to create a static page (i.e. unchanging, every time it was viewed it was the same) with links on it, kind of a computerized <a href="https://en.wikipedia.org/wiki/Choose_Your_Own_Adventure">Choose Your Own Adventure</a>. Very quickly after its introduction, however, it was quickly realized that people wanted dynamic content, or content that could change depending upon user input. So web servers changed from programs that displayed static content, to programs that could run scripts, in the early nineties this was CGI (or <a href="https://en.wikipedia.org/wiki/Common_Gateway_Interface">Common Gateway Interface</a>) written in C. This would allow a user to see a different page depending on input from the previous page (for example, when you went to <a href="https://lycos.com">Lycos</a> in 1994, you could enter a search term and find web pages that contained that search word). Eventually most developers switched from writing in C to more productive languages like <a href="https://www.perl.org">Perl</a>, <a href="https://php.net">PHP</a>, <a href="https://www.java.com">Java</a>, and whatnot. These CGI programs running on a web server are what we call server side languages, and for a majority of the lifetime of the web this was were the majority of the work was done that allowed showing of web pages.</p>
<h4 id="javascript-is-born">Javascript is Born</h4>
<p>In the early days of the web, the Browser wars Mosiac, Netscape, Cello, Spyglass were are trying to innovate to be come the top browser. Netscape navigator in version 2.0 added the ability to run scripts on a web page (for brevity and the fact that browser plugins are for the most part relegated to history, we will do our best to ignore Java Applets, ActiveX, and Flash as alternate ways to run client side code) with a programming language that was initially dubbed LiveScript but later changed to JavaScript for purely marketing reasons (it really shares nothing with Java other than curly braces which is a feature of a vast majority of the computer languages in the world). While initially JavaScript was a curiosity that was used for little more than basic form validation (you could instantly tell if the user had forgot to fill in a field for example before having to do a costly trip to the server – remember this was the days of 14.4 modems are very slow internet connections), over time and improvements to JavaScript more and more could be done with the paradigm that was deemed DHTML (or Dynamic HTML). Amazing features like interactive menus, images that changed when you hovered over them (I know these seems quaint now, but at the time these were truly amazing). Around this time CSS, which allowed web designers to make web pages look far more attractive was also introduced (another digression about the horrors of how web pages were styled with tables and images will also be avoided at this point).</p>
<h4 id="ajax-changes-everything">Ajax Changes Everything</h4>
<p>At end of the 90’s, there was an arms race between two browser vendors, Microsoft and Netscape. Microsoft was terrified that if the web browser could truly create programs that could run equally well on any operating system, their dominance with Windows might come to an end. So they did everything they could to create the best web browser, but have it run really only on Windows – and here is where ActiveX plugins from above rears its ugly head – they tried to <a href="https://en.wikipedia.org/wiki/Embrace,_extend_and_extinguish">embrace, extend and extinguish</a> cross platform HTML with ActiveX and have developers create plugins for their pages that only ran on Internet Explorer on Windows. One of the Hot technologies at the turn of the century was XML, XML was a way to transmit data and one of the prepackaged ActiveX plugins that Microsoft packaged in internet explorer was a way for Javascript to send XML (and people later discovered non-XML) data to and from the server <strong>without</strong> requiring the page to reload. Netscape copied this one feature in their own way, but in a way that clever JavaScript developers could get the same script to run on both Netscape and Internet Explorer, and eventually this transmission of Data without a page reload was dubbed <a href="https://en.wikipedia.org/wiki/Ajax_(programming)">Ajax</a>. It was Ajax that allowed for things like Google Auto-ccomplete, where when you type in the Google Search box it makes suggestions of what you might be searching for based on what you are typing. Rest and JSON services (which are the server portion of Ajax) became a way for a small amount of data to be quickly transferred from Server to Client (and vice-versa) without having to undergo the jarring reload of page. And as this paradigm become more common, web browsers became much better at updating small portions of a web page, rather than reloading an entire page.</p>
<h4 id="jquery-changes-everything">JQuery changes everything</h4>
<p>Years of developers fighting with the various web browsers differences, and dealing with what many developers thought of at the time was a terrible language (JavaScript), was helped by three major factors. First, in 2006 the JQuery library was introduced to help paper over the differences between the web browsers – JQuery was truly a game changing library that massively improved productivity and allowed the creation of Apps with a level of interactivity never previously seen. Second, there became some <a href="http://shop.oreilly.com/product/9780596517748.do">norms around the way to use JavaScript</a>, inline code, global variables, and the like became verboten and developers learned how to use the best features of JavaScript without falling to victim to its weaknesses. And third, a renaissance in Browsers came about with Safari, Firefox, and Chrome. Internet Explorer which had held a 90%+ market share eventually dwindled down to about 20% (when combined with Edge) today. In said renessaince most browsers (the exception being Safari), switched to being EverGreen – in which they were updated on a 6 week schedule, rather than the multi-year schedule that IE had been stuck on. Due to the rapid speed of upgrade developers could actually rely on newly implemented features being available, whereas for years before you had to develop your application without those features because they were not available in Internet Explorer 6.</p>
<h4 id="angular-and-the-rise-of-frameworks">Angular and the Rise of Frameworks</h4>
<p>While some might argue that JQuery is a Framework, it is really mostly a library. It does a lot of things, but it does not provide a proscribed way of separating the what the user sees from the data that proscribes what the user should see. In the world of jargon and Computer Science Patterns this is called M-V-Something (<a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC</a>, <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel">MVVM</a>, <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter">MVP</a>, and a half dozen other variants), which is not important but what is important is that a Framework provides a good way to organize the code (think of this as the model) and separate it from the markup (think of this a the view). Even with JQuery where the code was taken out of the HTML page placed in Script Module, the elements on the page were referenced by strings which were fragile. For example, your page contains a field for entering Zip Codes, and the Jquery references the zip code. But then it is decided that you want to be more international and change it Zip/Postal code, and suddenly the reference stops working. While this is obviously an oversimplification it was issues like these, and the growing size of Javascript code bases that required a formalized setup for the organization of code and use of classical patterns on the client side. Angular was one of the first of these Frameworks to become truly popular, and it solved a lot of the problems of having a large client side codebase.</p>
<h3 id="back-to-the-present">Back to the Present</h3>
<p>OK, so now that we know what Javascript Framework is, how they came about, and why you need them; the question becomes why is it important to choose wisely. If your project works out successfully, you will be maintaining it for years – the project that is feature complete, unless it is a truly simple application that you probably don’t need a framework for, is a project that that has no engaged users. If you picked your framework wrong (it stops being maintained, or is for some reason no longer viable), you may have to either take up maintenance of the framework or switch to a new framework. While you <strong>can</strong> swap out one framework for another, as the frameworks grow more complex it becomes considerably more effort. For example, upgrading from Angular 1.X to Angular 2 (which confusingly is now called Angular 4 or maybe Angular 14 by the time you read this) is no easy feat. The suggested migration path is to include both the Angular 1 framework as well as the Angular 2 framework on the page and have them communicate to each other through a migration translator, slowing taking out Angular 1 code as you change the app. This creates a situation where your developers have to be intricately familiar with both Angular 1 and Angular 2, as well as your clients have to load two frameworks on a page which can affect loading speeds.</p>
<h4 id="what-to-consider-when-choosing-a-framework">What to consider when choosing a framework.</h4>
<p>The obvious first thing when considering a framework is longevity, will the framework be maintained in the future. While popularity is a good sign of longevity, it may not guarantee it (the above example of Angular - probably the most popular framework - switch to a completely incompatible version 2 is a lesson that should be heeded). You might wonder why it is important to maintain a Framework, after all if it works now, won’t it work forever? Unfortunately, while it continue to work forever, it may use features of the browser that are deprecated or changed over time. It will not take advantage of features that are added to browsers, and eventually you will end up with an application that looks dated and xdoesn’t hold up against its contemporaries.</p>
<p>The next thing to consider when looking at a framework is popularity itself, if a framework is popular it will be much easier to hire talent to work on it, as well as for the talent that is working to get assistance when issues come up. There is a saying in computer science that all abstractions are leaky, and what that generally means is that a Framework (which is an abstraction) frequently runs into issues that the designers of the Framework didn’t think about. If you don’t run into one of these issues, consider yourself very lucky, but I find that almost all frameworks run into issues of some kind. If you are one of only a few people working with a Framework you will not be able to easily find solutions these problems as you might be the one of the few people having this particular problem, however if a lot of people have the same problem because there are a ton of people using the framework it is more likely there will be blog posts, <a href="https://stackoverflow.com">StackOverflow</a> questions, or some answers or at least clues to answers on the internet.</p>
<p>One thing that I also like to consider, is the size and complexity of the Framework. If a framework is small and relatively simple it becomes a lot easier to debug when things don’t work the way you expect, whereas a giant framework that requires a PHD in computer science to understand how it works becomes more of a Black Box where you have to just trust that it behaves the way it is documented.</p>
<p>How long a framework has been around plays both to strengths and weaknesses. Angular 1 has been around for a long time (and even though it has been superseded by Angular 2) it is still probably the most popular framework with the most developers using it, the most StackOverflow questions, etc. However, it uses a technique to determine if the page has to be updated called dirty-checking, which causes a lot overhead and has been superseded in modern frameworks by a technique that is orders of magnitude faster generally called Virtual-DOM. This may or may not be noticeable by your users, depending upon what your are doing, but it is a consideration you might want to weigh when determining your framework.</p>
<h3 id="javascript-frameworks-as-of-2017">Javascript Frameworks as of 2017</h3>
<p>While there are always new Frameworks coming along, but here are a list of what I consider viable Frameworks, their advantages and potential disadvantages. I am going to use the number of StackOverFlow questions as a general guide for how popular a language is (there are other metrics, like number Google Results, <a href="https://help.github.com/articles/about-stars/">Github Stars</a>, and it also points to past popularity rather than current popularity.</p>
<h5 id="angular-1-or-angularjshttpsangularjsorg"><a href="https://angularjs.org">Angular 1 (or AngularJS)</a></h5>
<p><strong>Advantages:</strong> Probably still the most popular framework, and certainly the most popular historically (240,000 SO questions). Lots of developers are familiar with it, and there are hundreds of plugins and libraries that work with it.</p>
<p><strong>Disadvantages:</strong> While still maintained, no new features are coming to the framework. Slower for updating than more modern frameworks. High level of complexity and what now seems like a large learning curve to go from casual familiarity to expert level comfort with the framework. Angular is also a fairly large framework and unless you go through a complex asset pipeline build process (which is part of the reason to use Angular 1 is to avoid complex build processes) it will not be shrunk down with features like <a href="https://webpack.js.org/guides/tree-shaking/">Tree Shaking</a>.</p>
<h5 id="angular-24-or-angulariohttpsangulario"><a href="https://angular.io">Angular 2/4 (or Angular.io)</a></h5>
<p><strong>Advantages:</strong> Doing complete rewrite allowed the developers to take everything that they learned developing Angular 1 and improve on it in many ways. Faster, more modern, and has less hacks than previous language. Built from the ground up to be easily interoperable with IDE’s ()Integrated Development Environments). Improved performance.</p>
<p><strong>Disadvantages:</strong> The complete rewrite produced a painful upgrade situation where, and soured a great many original Angular Developers. Angular 1 skills and knowledge do not translate with Angular 2. Requires a lot of of outside tooling to get up and running (<a href="https://nodejs.org">Nodejs</a>, <a href="https://webpack.github.io/, [TypeScript](https://www.typescriptlang.org/)">WebPack</a> as well as a lot of boilerplate, and is quite a large framework in size.</p>
<h5 id="aureliahttpaureliaio"><a href="http://aurelia.io/">Aurelia</a></h5>
<p><strong>Advantages:</strong> Modern framework that takes a lot of the ideas from Angular and <a href="http://durandaljs.com/">Durandal</a>. For those familiar with <a href="https://en.wikipedia.org/wiki/Extensible_Application_Markup_Language">XAML</a>, Microsoft’s Client Side GUI language, MVVM’s patterns may feel more familiar.</p>
<p><strong>Disadvantages</strong> Relatively small following, and the documentation is not as extensive as the frameworks. The build system leaves a bit to be desired, but hopefully will be improved over time. Basically the project of a singular developer (however one with quite a pedigree), without a major company backing the project might lead to some trepidation.</p>
<h5 id="emberhttpswwwemberjscom"><a href="https://www.emberjs.com/">Ember</a></h5>
<p><strong>Advantages:</strong> While it has been around for a while it managed to improve performance from version 1 - 2 without the complete rewrite headache that plagued Angular. It is well documented, and has passionate followers, especially those in the Ruby community.</p>
<p><strong>Disadvantages:</strong> May seem cumbersome to those outside the Ruby community. It is a fairly large framework, with a lot of concepts to grasp (for example, the router is introduced as one of the core concepts in the getting started guide). Has never caught on in the way that React and Angular has.</p>
<h5 id="reacthttpsfacebookgithubioreact"><a href="https://facebook.github.io/react/">React</a></h5>
<p><strong>Advantages:</strong> It’s meteoric rise in a short period of time, and it’s backing by Facebook due to its simplicity and lightening speed has made it probably the #2 framework in the past 2 years. Its very well documented, easy to use, and due to the fact that it is not an all encompassing framework makes it easy to add to existing projects. Since it doesn’t attempt to do as much as other frameworks it is considerably easier to learn.</p>
<p><strong>Disadvantages:</strong> Not as much of a framework as the other listed here, more just like a view renderer. Most developers combine it <a href="https://facebook.github.io/flux/">Flux</a>/<a href="http://redux.js.org/">Redux</a> to flesh out the functionality but the lack of a router/DI and other features included in the other frameworks may mean relying on less reliable libraries to include those features. <a href="https://react-etc.net/entry/your-license-to-use-react-js-can-be-revoked-if-you-compete-with-facebook">Some concern over the license</a> has spooked some developers lately.</p>
<h5 id="vuehttpsvuejsorg"><a href="https://vuejs.org/">Vue</a></h5>
<p><strong>Advantages:</strong> As with other modern frameworks it is fast as it uses a virtual-dom. It’s small codebase makes it relatively easy to debug and understand. It’s rapidly increasing popularity means that it probably isn’t disappearing anytime soon, although it has mostly been developed by one developer. Very similar to angular1 syntax, it is one of the easier to learn frameworks.</p>
<p><strong>Disadvantages</strong> Missing some of proscription of Angular 2 or Ember (less opinionated as they say) developers can be left on their own to build inelegant solutions quite easily in Vue. The fact that is basically built by one developer might be a reason for concern, however with over 100 contributors and a MIT license it could easily be forked and continued if the primary contributor ever went away or became uninterested.</p>
<h4 id="how-to-choose">How to Choose</h4>
<p>Obviously you should listen to your developers or contractor but at least now you should have enough information to ask some smart questions.</p>
<p><strong>Why would do you recommend this framework?</strong> - If the answer is its the only framework I’ve looked at you may want to ask some more probing questions or look elsewhere.</p>
<p><strong>Does it match our backend well</strong> - For example, if your backend is designed to output <a href="http://graphql.org/learn/">GraphQL</a> rather than <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST</a>, React may be a more natural fit.</p>
<p><strong>Do are developers have experience with the framework</strong> - Not the best reason for choosing a framework, I, for example would recommend building a site in <a href="http://backbonejs.org/">BackBone</a> or <a href="https://www.sencha.com/products/extjs/#overview">ExtJS</a> at this date in history, but rather than having your developers spend a week or two learning they could be coding. Just remember over the life of a successful product more time will be spent on maintenance than on the original development.</p>
<p><strong>Are developers excited about working with the framework</strong> - Once again, not the best reason to choose a product, I know of far too many places that have a mismatch of “Technology du Jour” foisted upon them be developers eager to work on the new hotness that they greatly regret being burdened with.</p>
<p>Hopefully now you fell a little more prepared to discuss with stakeholders the relevant issues of what frontend framework to choose, wean developing your Single Page Application.</p>
<p><a href="http://agingcoder.com/opinion/2017/08/02/what-javascript-framework-should-i-allow-my-developers-to-use/">What Javascript Framework should I allow my developers to develop a SPA App, and what is a SPA App</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on August 02, 2017.</p>http://agingcoder.com/mobile/2016/09/25/the-state-of-html-mobile-frameworks-in-20162016-09-25T00:00:00-07:002016-09-25T00:00:00-07:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<h4 id="the-state-of-mobile-frameworks-2016">The State of Mobile Frameworks 2016</h4>
<p>It’s been over 2 years since I posted <a href="/programming/2014/04/22/the-state-of-html-mobile-frameworks-in-2014/">The State of Mobile Frameworks</a> and a fair amount has changed in that time, so now is probably a good time to dive back into that thorny topic. Some frameworks have withered and died, some have gained functionality and popularity, and some have effectively stayed still. It is important to note, that mostly I am focused on Hybrid app frameworks, frameworks that are designed to run solely on mobile/tablet. If the framework’s main focus isn’t building mobile first applications that can be run on PhoneGap then I am not covering it here, or am looking at it in a mobile first context. This is clearly not the way to build a modern website, where a responsive solution is always best, but it is the way to build an app that can have more functionality than a website can by utilizing features on the device that isn’t currently accessible to websites (either for security reasons or just because the feature hasn’t rolled out to browsers yet).</p>
<h4 id="its-dead-jim">It’s Dead Jim</h4>
<p>Of the frameworks I looked at, 10 of the 12 have effectively disappeared.</p>
<ul>
<li><a href="http://jqtjs.com/">jQT</a> was dead in 2014, but was included since it was still appearing a lot in searches at the time. Put the final nail in that coffin now.</li>
<li><a href="http://jquerymobile.com/">jQueryMobile</a>’s last release was 2 and a half years ago, it was mostly dead then. No one should consider using it now.</li>
<li><a href="http://getbootstrap.com">Bootstrap</a> threatened to be a valuable for mobile development, and maybe with <a href="http://v4-alpha.getbootstrap.com/">Bootstrap 4</a>, if it is ever released, it might be but for now it is still for use on websites only. If you want to use Bootstrap, combine it with something like <a href="http://mobileangularui.com/">Mobile Angular-UI</a>, and then you have something approaching a modern solution, however I don’t think it best of breed solution.</li>
<li><a href="http://www.sencha.com/products/touch/">Sencha-Touch’s</a> license, or its htmlless design model may have killed it, but although it still appears to exist as product it is not an option I would consider. Also the source code doesn’t appear to be a GitHub which is always a bad sign.</li>
<li><a href="http://phonejs.devexpress.com/">PhoneJS</a> has been rebranded <a href="http://js.devexpress.com/MobileDevelopment/">DevExtreme Mobile</a> seems to be at about the same point it was 2 years ago, also not on GitHub, also requires a commercial license.</li>
<li><a href="https://github.com/davebalmer/jo">Jo</a>, the old site joapp.com is dead and there hasn’t been a commit in 3 years. Put a fork in it.</li>
<li><a href="https://github.com/rbiggs/chocolatechip-ui">ChoclateChip-UI</a>, the chocolatechip-ui.com website doesn’t appear to reference the project any more, and there hasn’t been a commit in a while on GitHub.</li>
<li><a href="http://app-framework-software.intel.com/">AppFramework</a>, points to the GitHub site, but I think Intel doesn’t have the energy to focus on this type of project any more.</li>
<li><a href="http://topcoattouch.com">TopcoatTouch</a> was kind of dead when I wrote about this two and a half years and almost nothing has been done it since. The TopCoat CSS framework has long since been abondoned by Adobe, so I can’t recommend using it.</li>
<li><a href="http://www.telerik.com/kendo-ui">KendoUI Mobile</a> has kind of disappeared from the Kendo-UI framework in favour of responsive design (probably the right way to go). That means, though, that the <a href="http://demos.telerik.com/kendo-ui/mobilethemebuilder">Mobile Theme Builder</a> and cross platform look and feel is also effectively gone. Kendo-UI might be good for developing responsive web sites now, but I don’t think I would use it for building a Hybrid app.</li>
</ul>
<p>I didn’t mention <a href="https://github.com/Famous/framework">Famous</a> in the previous article, but after a brief moment looking like they were going to be an interesting alternative to the Angular Based Mobile frameworks and a ton of VC investment, they <a href="https://techcrunch.com/2015/11/06/nopen-source/">flamed out and had to pivot</a>.</p>
<h4 id="is-less-diversity-and-choice-maybe-a-good-thing">Is Less Diversity and Choice Maybe a Good Thing?</h4>
<p>That leaves <a href="http://ionicframework.com/">Ionic</a>, and <a href="https://onsen.io/">Onsen-UI</a>. There have been a few others
(like <a href="http://goratchet.com/">Rachet</a> which is more of a prototyping framework), but the only real new framework
I would consider would be <a href="https://framework7.io/">Framework 7</a>. This dearth of choice almost brings us to back to 2013
when there were only a couple of options, before the explosion of frameworks of 2014.<br />
It certainly means that you will have less frameworks that you will be required to evaluate before making a decision,
but it also means that if you are unhappy about one of the big three’s framework design choices or appearance you have
less options for change.</p>
<p>There are certainly going to be new Frameworks in the future (<a href="http://touchstonejs.io/">Touchstone.js</a>,
a React based mobile framework may sometime be released but it hasn’t had any commits in the past 5 months) and there
are now a plethora of NativeJavascript frameworks (<a href="https://www.nativescript.org/">NativeScript</a>, <a href="https://facebook.github.io/react-native/">React Native</a>.
<a href="http://www.appcelerator.com/mobile-app-development-products/">Titanium</a> and <a href="https://tabrisjs.com/">Tabris</a> to name
a few) that are other viable options for mobile application building.</p>
<p>However, this article is concerned with the Big Three frameworks that are left in Hybrid-Mobile Javascript space. All of
these frameworks come with the standards that we would expect: the standard mobile widgets, CSS that looks modern rather
than dated, styling to match either the iOS or Material design (Android) platforms, and the ability to quickly
create a modern looking mobile application with Cordova.</p>
<p>So let’s start the 500lb Gorilla, Ionic.</p>
<h4 id="ionic">Ionic</h4>
<p>If most people ask me what Framework to use I pretty much always say <a href="http://ionicframework.com/">Ionic</a>. The only
real question in the past few months has been whether or not to try to live on the bleeding edge and use <a href="http://ionic.io/2">Ionic
2</a> or stay with the tried and true. <a href="http://ionicframework.com/">Ionic 1</a> is an open source
framework built on top of <a href="https://angularjs.org/">Angular</a>, providing a bunch of directives, css and a well
through out, proven way of creating mobile apps. You can see my previous <a href="/programming/2014/10/11/mobile-frameworks-ionic/">look at Ionic</a>,
which pretty much holds true for the past couple of years. The <a href="http://ionic.io/">Ionic</a> company, besides working on
Ionic, has developed an eco-sphere of paid add ons that add a fair of bit of value to the Ionic Framework. A <a href="http://ionic.io/cloud">cloud
service</a> which provides push notification, authentication services, a build server, and their
version of live update. They also sell a <a href="http://ionic.io/products/creator">drag and drop designer</a>, that would
allow a designer to prototype an app and then allow a developer to bring the prototype to life without having to
start from scratch (a year ago, the last time I used it, it produced a pretty good framework to work with, a
little messy but I am sure that has improved over time).</p>
<h5 id="ionic-1-or-ionic-2">Ionic 1 or Ionic 2?</h5>
<p>And now they are at beta 11 of Ionic 2, which uses <a href="https://angular.io">Angular 2</a> as the basis of Ionic 2. This means
that it will be basically a rewrite to upgrade your application from Ionic to Ionic 2. You might be able to keep some
of the html templates, the controllers (controllers no longer exist in Angular 2) will have to be completely
rewritten, and it is recommended (though not essential) that you switch from plain ol’ Javascript to <a href="https://www.typescriptlang.org/">TypeScript</a>. However if
you are starting a new project, you really should be thinking about starting in Ionic 2. Now that
Angular 2 final has been released, I think we can expect Ionic 2 to leave beta shortly. Although Angular 2 is almost
a completely different framework from Angular 1, it shares only a little of the philosophy and even less of the
syntax, it is a much more modern framework that has taken a lot insight from weaknesses of Angular 1 and from new
frameworks like <a href="https://facebook.github.io/react/">React</a>. Since Angular 2 is more efficient at rendering HTML, it
is much more suited for the mobile environment. It was wise of Ionic to switch to Angular 2, as staying with
Angular 1 would have left them on a dead-end framework.</p>
<p>While I have not built a real world Ionic 2 application yet, and I might hesitate to work with Ionic 2 if I had to
deliver an app in the next couple of weeks, I think that one should definitely consider moving to Ionic 2 if you
are building an App that you wish to deliver in months rather than weeks and are expecting to support for years.
However if you need to build an app in the next few days that you will be doing little maintenance on, I would stick with Ionic 1.<br />
Over the next months and years, I belive that usage of Ionic 1 will drop off and Ionic 1 apps will start seeming dated.<br />
The hope is, now that Angular 2 has been released, the developers of Angular will stop changing the API and we can
have a framework that will last years without the constant breaking changes that we have endured over the past year.<br />
Angular 1 had a very good run and has been the dominant framework for several years (maybe React has taken over with
developer excitement, Angular is still the top dog in <a href="https://www.similartech.com/compare/angular-js-vs-react-js">real world usage</a>),
and with the force behind Angular 2 it is hard to believe that it will eventually not become one of the most important
Javascript Frameworks.</p>
<h5 id="the-ionic-framework">The Ionic Framework</h5>
<p>What you get with the Ionic 2 Framework (not talking about the upsells from the company) is a pretty robust framework,
that smartly uses Angular 2 and its strengths. They have hidden a lot of the complexity of getting the Angular 2
framework up and running, the files are laid out in a sensible manner and they have leveraged the standard web construction
toolchain like NPM, Gulp and Sass so that anyone who has worked with these tools will not be confused with what is going
on behind the scenes, and because of this the live-reload and debugging in with sourcemaps just work when you type
<code>ionic server</code>. It’s clear that the Ionic team has taken what they learned from Ionic 1 and poured that
knowledge into Ionic 2, since you are going to pretty much have to start fresh anyway (since Angular 2 is so different
from Angular 1) you might as well remove any of the cruft from the previous framework.</p>
<h5 id="the-cli">The CLI</h5>
<p>You get a CLI that allows project creation. The project creation,
like most of the CLI, requires you to set all of your options on the command line. You have to know the name of
the template you want to use (or specify a github repo, or a codepen url) and specify that on the command line.<br />
You have to remember specify –v2 if you want an Ionic 2 project rather than an Ionic project, otherwise it is
ctrl-c and delete and start again (if you caught it when it was building rather than after you spent the 5 minutes
to bring up the new project and then discover that it is a Ionic 1 app). You have to specify –sass i8f you want
Sass rather than CSS. This will be familiar to you if you have have run the Cordova CLI a lot, but other CLIs (and
even Yeoman) are more prompt based.</p>
<p>The CLI also wraps almost all of the cordova features (build, run, add platform, add plugin), and if you want the
advantages from the <a href="http://ionicframework.com/docs/v2/native/">Ionic Native</a> plugin wrappers, I believe you have
to use <code>ionic plugin add</code> rather than <code>cordova plugin add</code> (though I haven’t actually tried
using straight <code>cordova plugin add</code> with Ionic Native), for more information about how to use Ionic Native and how
it is different from <a href="http://ngcordova.com/">ng-cordova</a> see <a href="http://www.joshmorony.com/">John Morony’s</a>
<a href="http://www.joshmorony.com/using-cordova-plugins-in-ionic-2-with-ionic-native/">article on Ionic Native</a>.</p>
<p>The CLI also gives you easy access to the various platform services that subscribing to the Ionic Cloud gives you, as
well as a couple of features that probably should be part of the standard Cordova CLI (like creating all your resource
files from a single icon and a single splash screen).</p>
<h5 id="other-ionic-products">Other Ionic Products</h5>
<p>Ionic provides a couple of Products. Ionic Lab, which is a GUI frontend to the CLI. It is clearly an early product,
but since the “About IonicLab” menu option does nothing, and there are no version numbers on the Downloads or on the
<a href="http://lab.ionic.io/">info page</a>, the only place were version info could be found was on their <a href="https://github.com/driftyco/ionic-lab-issues">GitHub page</a>
to track issues for the project and that claims that we are at “1.0.0-alpha.14”, but that was last updated
in July 2015 so who knows. The app only works with Ionic 1 projects (it can only create Ionic 1 projects, and though
it appears to load Ionic 2 projects you have created from the command line, you can’t install a platform so it
is stuck with just serving the app). They might be waiting for Ionic 2 to be released to do a complete update,
or maybe Ionic has abandoned this product failing to find some way to monetize it.</p>
<p><a href="https://creator.ionic.io">Ionic Creator</a> also is limited to Ionic 1 apps, but is a pretty good drag and drop mobile app creator. While this
doesn’t hold much interest for me, it might be very useful if you want to have a designer design the pages and then
have coders come through and make the app functional. I’ve only played around with it enough to knock out a simple
mock up, and you need the professional version to hook up any directives and do any code editing so I may not have
enough information to properly judge how useful the tool would be.</p>
<h5 id="final-thoughts-on-ionic-2">Final Thoughts on Ionic 2</h5>
<p>While I haven’t worked with Ionic 2 enough to know where the sharp edges are, I would be comfortable in using it in
an application that was going to be completed in 6 months or so. Now that Angular 2 is finally released, and the breaking
changes will finally stop (until Angular 2.1 at least), I think the release version of Ionic 2 should be just around
the corner. Once it is released my assumption is that it will quickly become the dominant mobile application framework,
overtaking Ionic 1 in a year or so, so you might as well learn it.</p>
<h4 id="onsen-ui">OnSen UI</h4>
<p><a href="https://onsen.io/">OnSen UI</a> is another framework I have looked into <a href="/programming/2014/10/16/mobile-frameworks-onsenui/">in the past</a>,
and was pleasantly with. A fair bit has changed with what was written in the article, and they have just
recently released <a href="https://onsen.io/blog/onsen-ui-2-is-here/">OnSen UI 2</a>. Like Ionic 2, it is pretty much
a complete rewrite and is no longer based on Angular. They have switched to using the <a href="https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements">Custom Elements API</a>,
as the way that their <on-sen> components are rendered, and because of this they allow using a Vanilla JS,
Angular 1, Angular 2, React, <a href="https://vuejs.org/">Vue</a> or even <a href="https://www.meteor.com/">Meteor</a> as the binding
framework. They also provide two separate and automatically rendered skins, Material (for Android) and iOS.</p>
<p>Like Ionic, their value add (monetization strategy) is adding features on backend (push notifications, live
app updating) as well as a build server, and a even Cloud IDE. Unlike Ionic this is under a different name called
<a href="https://monaca.io/">Monaca</a>, which does not require use of OnSen, but clearly favours it.</p>
<h5 id="cli">CLI</h5>
<p>When reviewing a earlier version of OnSen, it required using <a href="http://yeoman.io/">Yeoman</a> to Scaffold the
application. The latest version uses the <a href="https://docs.monaca.io/en/manual/development/monaca_cli/">Monaca CLI</a> which
is a slick CLI that even shows previews of the templates when selecting them. It’s not as full featured as the
Ionic CLI, but the features it does include seem a lot more user friendly than the Ionic version, in that not everything
has to be done as an argument to the command. When you type <monaca create="" myproject=""> you are asked to choose what
type of project you want to make.</monaca></p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="gp">$> </span>monaca create onsen-demo
? Choose a category: <span class="o">(</span>Use arrow keys: ↑ ↓ →<span class="o">)</span>
<span class="gp">> </span>Onsen UI
Onsen UI and Angular 1
Onsen UI and Angular 2
Onsen UI and React
Ionic
No Framework
Sample Apps</code></pre></figure>
<p>You are next asked to choose a template (a nice feature is that pressing P on the currently selected template will bring
up a preview of the app in your browser):</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">? Select a template - Press P to see a preview <span class="o">(</span>Use arrow keys: ↑ ↓ ← →<span class="o">)</span>
<span class="gp">> </span>Onsen UI V2 Angular 2 Tabbar
Onsen UI V2 Angular 2 Splitter
Onsen UI V2 Angular 2 Navigation
Onsen UI V2 Angular 2 Minimum</code></pre></figure>
<p>These are kind of nice features that would be relatively easy to add to all CLI’s which make it much easier to get up
and started.</p>
<h5 id="the-framework">The Framework</h5>
<p>Please note that I didn’t spend much time with OnSen outside of the Angular 2 environment (as that is where my current
interests lie), other than playing with their pretty excellent <a href="https://onsen.io/tutorial/">interactive tutorial</a>,
which unfortunately does not support Angular2 yet (apparently coming soon). So pretty much everything I have to
complain about is more about how they supported the Angular2 framework.</p>
<p>I started my little toy app (get a list of github users from the <a href="https://api.github.com/users">GitHub api</a> with the
minimum Angular 2 configuration. The project structure seemed a little more messy to me than the Ionic 2 project structure,
and I thought that there was an excessive amount of bootstraping required that ionic (maybe to its detriment in the future)
hides in their own ionicBootstrap function. The OnSen bootstrap also omits including “CommonModule”, so none of the ng* directives
will work until you add that. They are transpiling all the typescript, so not having an option to use SASS seems like
a bit of a lost opportunity. The way they are transpiling (besides being very slow, it can take up to a minute to
transpile a trivial app) makes it impossible do any debugging in the preview mode (all you get is a minified single
app.js that is injected into the page through evals (using webpack), and though
there appears to be base64 encoded sourceMaps included they clearly aren’t pointing to files that the browser
can access because even when you throw in the <code>debugger;</code> into your code the only place it breaks is
in one of chromes VM files (see diagram below, with apologies to all those on mobile devices)
<span>
<img src="/img/state-of-frameworks-2016/debugging-onsen.jpg" style="border: 1px solid #000; margin: 10px auto 0" />
<span style="display: block; text-align: center; font-weight: bold">Debugging an Onsen Application</span>
</span></p>
<p>There may be some way to get around this, but I haven’t discovered it. My guess is that since they are supporting a
lot of different binding frameworks, they do not have the time to spend on getting the debugging working perfectly for
everyone of these platforms.</p>
<p>I then tried using the Monaca online debugger to run the same project, but with the online site it currently only brings
the minified compiled files into their debugger. I assume this is because the online site does not yet have the ability to
support webpack and the other transpiling infrastructure required to convert typescript files to standard javascript
but it makes the online editor basically useless for Angular 2. It was also useless because (unlike in preview mode),
I couldn’t get the transpiled version of the app running in the cloud to include “CommonModule”, and therefor got the
following error when trying to run the application in the cloud (and on my device):</p>
<p><code style="color:red"> Error: Template parse errors: Property binding ngforOf not used by any directive on an embedded template.
Make sure that the property name is spelled correctly and all directives are listed in the "directives" section.</code></p>
<p>Since the build cycle of this was so slow, after trying 3 or 4 ways to register the “CommonModules” I gave up.</p>
<h5 id="final-thoughts-on-onsen2">Final Thoughts on OnSen2</h5>
<p>Although OnSen2 claims to be an actual release, rather than the RC that Ionic is, I would say that their Angular2 support
is very beta and early days. Not being able to debug in Chrome Dev tools is a deal breaker for me (I am pretty sure
that this will get fixed, but launching with it this broken seems like a mistake), but if you want
a framework to work with in Angular 1 or regular Javascript, or maybe even React (although it shares the same slow
transpiling process, they have got debugging with map files working) you might want to look into OnSen.</p>
<h4 id="framework-7">Framework 7</h4>
<p>Framework 7 is a different beast than Ionic and OnSen, in that it feels much more like a classic Mobile framework like Jquery Mobile
or one of the many frameworks from 4 or 5 years ago. First off all it is delivered with bower (OK, you don’t have to install it with Bower, you can
<a href="https://github.com/nolimits4web/Framework7/">download(sic) it from Github as well</a>), which is enough of a blast from the past, but also
it is very html based rather than programmatically based (the getting started html page is 75 lines and 3500 characters where the ionic 2 starting
page is 7 lines and 150 character). Although it can be used with both <a href="https://github.com/valnub/Framework7-Pure-Angular-Template">Angular 1</a>,
or even <a href="http://embed.plnkr.co/KTzAqu0OQrP3ZmHpuGy8/">Angular 2</a> it was designed to really just use regular Javascript.
It has its own DOM manipulation framework (<a href="https://framework7.io/docs/dom.html">Dom7</a> which really feels like a drop-in for jQuery), and it’s own Templating library
(<a href="http://idangero.us/template7/">Template7</a> which feels like HandleBars), so if you are used to HandleBars and JQuery you
will feel very comfortable here. Also there is a distinct lack of TypeScript, ES6, JSX or even SASS, so there is no
transpiling and no complex packers, loaders, or anything else that greatly complicates the setup of the other libraries.</p>
<h5 id="integration-with-cordova">Integration with Cordova</h5>
<p>While you certainly can use Framework 7 with Cordova, there is no built in tooling to support it. You are either going
to have to write your own gulp/grunt or custom script to take all of your html, javascript, and css and place it in
the www directory of a cordova project, or you can clone the <a href="https://github.com/nolimits4web/Framework7/">Framework 7 repo</a>
on Github and work with their gulp script. Cloning the repo does add a lot of stuff you will want to clean up though, and
the gulp script works off of their kitchen sink demo (they provide a Material and an iOS styled Kitchen Sink demo but
following this <a href="https://framework7.io/tutorials/maintain-both-ios-and-material-themes-in-single-app.html">tutorial</a> you
can have both look and feels in the same app), so I would recommend editing their Gulp script for your own use. This
is all fine, but it seems a little clunky in this day and age to have to do so much prep just to get hello world working. They
might want to look at creating a <a href="http://yeoman.io/">Yeoman</a> script for scaffolding the building process.</p>
<p>The lack of integration with Cordova isn’t a big problem, it does add one more bit of code that you have to write or update (a Gulpfile)
and another thing that you have to do every time you want to build your application. Run the gulp or grunt script, then run cordova build (there
is a gulp plugin to do builds for <a href="https://github.com/SamVerschueren/gulp-cordova-build-ios">ios</a> and <a href="https://github.com/SamVerschueren/gulp-cordova-build-android">android</a>
which I haven’t used, but my experience with <a href="https://github.com/csantanapr/grunt-cordovacli">grunt plugins for cordova</a> has been mixed at best).</p>
<h5 id="the-framework-1">The Framework</h5>
<p>As I explained above, the framework is pretty simple. It is basically a bunch of CSS, with a few javascript helpers. You
have to initialize the main app, and every view you want to register. If there is a component that requires initialization,
you have to manually initialize it as well. For example if you want to have a picker, you initialize it in javascript:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">triviaPicker</span> <span class="o">=</span> <span class="nx">app</span><span class="p">.</span><span class="nx">picker</span><span class="p">({</span>
<span class="na">input</span><span class="p">:</span> <span class="s1">'#trivaAnswer'</span> <span class="c1">// Here is where you select the element you want to activate on</span>
<span class="na">cols</span><span class="p">:</span> <span class="p">[</span>
<span class="p">{</span>
<span class="na">values</span><span class="p">:</span> <span class="p">[</span><span class="s1">'Android'</span><span class="p">,</span> <span class="s1">'iOS'</span><span class="p">,</span> <span class="s1">'Blackberry'</span><span class="p">,</span> <span class="s1">'Windows Phone'</span><span class="p">,</span> <span class="s1">'PalmOS'</span><span class="p">]</span>
<span class="p">}</span>
<span class="p">]</span>
<span class="p">});</span> </code></pre></figure>
<p>then when you want to activate it, you simply call open:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'click'</span><span class="p">,</span> <span class="s1">'#triviaButton'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">triviaPicker</span><span class="p">.</span><span class="nx">open</span><span class="p">();</span>
<span class="p">});</span></code></pre></figure>
<p>The main problem, or at least the main challenge, is that there is no structure give for you on how to organize your pages. The
JavaScript that they provide for you in both in the starter app, and their kitchen sink app is giant file of
JavaScript in the global namespace (ugh). Even if you look at a fairly nicely designed <a href="https://github.com/GuillaumeBiton/HackerNews7">sample app</a>,
it is still one file, with just comments to break up the pages. If you are going to use Framework7 I highly recommend
you break your pages out into separate files, and look at a better place to store the templates than in the index.html
of the main file (perhaps load them from file when the app is loading).</p>
<h5 id="final-thoughts-on-framework-7">Final Thoughts on Framework 7</h5>
<p>To be honest, Framework7 reminds me a lot of the <a href="https://github.com/kriserickson/topcoat-touch">TopcoatTouch</a> framework
(a framework that I cannot recommend using) that I wrote several years ago, except that it is a lot more polished and
has much better documentation, and has an up-to-date CSS implementation. I think it needs some proper large sample
applications that show a decent way of separating concerns rather than shoving everything into a couple of files.<br />
I think it could also use a decent Yeoman generator to simplify the scaffolding process, and pre-build a gulp or grunt
script that could concat and minify all the Javascript and CSS. However if you are more comfortable with the older jQuery
style of development, rather than the more modern Angular and React style you could do worse than choosing
Framework7 (you could choose <a href="https://jquerymobile.com/">jQuery Mobile</a>!)</p>
<h4 id="the-verdict">The Verdict</h4>
<p>As always, it comes down to what you are going to be most productive with, what your end goal is, how long you expect
to be maintaining the application, and if you want to spend a lot of time learning instead of developing. I can
say without question the easiest of these frameworks to get a sample app together was Framework7. It is very easy to
build app with, especially if you have been using jQuery and Handlebars for the past years (wow, hard to believe that
jQuery came out over 10 years ago). The second easiest was Ionic2, it is certainly a bit trickier than Ionic 1 especially
if you are not 100% comfortable with Angular2 (check out this great article on
<a href="https://hackernoon.com/why-learning-angular-2-was-excruciating-d50dc28acc8a#.aupibdhf9">the pains of learning Angular 2</a>)
but the future is pretty clearly not with Angular 1 and Ionic (unless the worlds treats Angular 2 like
<a href="http://blog.thezerobit.com/2014/05/25/python-3-is-killing-python.html">Python 3</a>). While I really wanted to like OnSen,
and loved a lot of the features it has (like its swappable binding frameworks), I think that they should have labeled the
current Angular2 implementation beta. It was the hardest of the frameworks to work in, the slowest, and the buggiest (I
consider it a bug if the app works in dev mode, but not when you switch to production). Like I said, you might like
it if you want to build an app in Angular 1 or React but I really can’t recommend it at this point for Angular 2.</p>
<p>You can find the sample apps I messed around with while writing this article on <a href="https://github.com/kriserickson/framework-2016">GitHub</a>,
they aren’t much but they might give you something to play with if you are going to explore these frameworks.</p>
<p><a href="http://agingcoder.com/mobile/2016/09/25/the-state-of-html-mobile-frameworks-in-2016/">The State of HTML Mobile Frameworks in 2016</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on September 25, 2016.</p>http://agingcoder.com/programming/2016/09/03/should-i-use-cordovaphonegap-or-go-native2016-09-03T00:00:00-07:002016-09-03T00:00:00-07:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<h3 id="cordova-or-native">Cordova or Native?</h3>
<p>The number one question I get asked, both by as a consultant and in the emails I get from readers of this blog is</p>
<p>“Should I use Phonegap or write a native app”</p>
<p><em>Side note: when asked this question people almost always refer to Cordova as Phonegap, however I usually refer to it as
Cordova as that is the name of the Open Source project, rather than PhoneGap which is the Adobe branded distribution
of Cordova</em></p>
<p>And my answer is always: it depends. For us pragmatists it is a similar question to “What is the best programming language?”
And it depends on even more than the type of App you are planning on creating, it depends on where you want that app
to go, what features the app requires, what development talent you have available to you, what platforms you want to target,
and the list goes on.</p>
<p>When I am asked if you can build it in Cordova, the answer 90% of the time is yes, you can build it Cordova.<br />
However, sometimes, building certain types of Apps in Cordova would require so much work will be spent on
plugins and optimizing the HTML that it may not necessarily the most optimal choice of a platform.</p>
<p>Initially I had sketched out a flowchart that gave some guidance as to whether or not you should
develop a native app or a hybrid/Cordova app, however it oversimplified things and did not properly factor a great many
things into the decision.</p>
<p>My new answer is more like the classic decision tool where you write down the plus and minuses of each aspect of a decision,
except I recommend you create two columns (Native and Cordova )and give points to either Native or Cordova depending upon the
answers to following questions. You may decide to change the weights I have awarded to each question, and I may have
missed several factors that may affect the decision, please feel free to make suggestions in the comments below.</p>
<h4 id="number-of-platforms-to-support">Number of Platforms to Support</h4>
<p>If you only have one platform to support (for example you are building an app for a company that will supply the
devices that the App is deployed on to their users), award 5 points for Native. If you plan to launch iOS only for
the near term (but eventually will launch on Android or another platform) are 2 points for Native. If you have to
launch on 2 platforms award 2 points for Cordova, if you have to launch on 3 platforms award 5 points to cordova,
if you have to launch on 4+ platforms award 7 points to Cordova (and cry a lot, since you are going to be in an
unenviable situation).</p>
<h4 id="making-a-game">Making A Game</h4>
<p>If you are making a game that has any level of sophisticated graphical requirements, award 5 points to Native. You can
do graphics, and you can certainly make puzzle style games in HTML and Javascript, but if you want to squeeze everything
out of a mobile device you really want to get as close to the metal as possible and that generally means coding in C.
This may be changing as the old Android Browser is becoming a thing off the past, the old Android browser was removed in
Android KitKat 4.4.3 and replaced with the Chrome view, however it wasn’t until Lollipop (5.0) that it became an evergreen
(i.e. always updating) browser.</p>
<h4 id="native-feel">Native Feel</h4>
<p>There are two philosophies when designing mobile apps, one is to make the App look like they completely belong on the
platform. Follow the platform design guidelines as closely as possible so that users familiar with ecosystem will
feel as comfortable as possible in the application. Use the conventions of the platform to easily expose functionality,
hoping the user will intuit how to use your app because they know the platform. Gmail is a good example of this,
where Gmail on iOS looks like an iOS app and Gmail on Android looks like an Android App:</p>
<div style="text-align: center; margin-bottom: 20px">
<span style="vertical-align:top; display: inline-block">
<div>
<img src="/img/cordova-or-native/gmail-ios.png" style="border: 1px solid #000; margin: 0 10px 10px 0" />
</div>
<span class="figure">Gmail on iOS</span>
</span>
<span style="vertical-align:top; display: inline-block">
<div>
<img src="/img/cordova-or-native/gmail-android.png" style="border: 1px solid #000; margin: 0 10px 10px 0" />
</div>
<span class="figure">Gmail on Android</span>
</span>
</div>
<p>If you app falls into this category, where you want to follow the platforms design guidelines and functionality as
much as possible, award 5 points to Native.</p>
<p>Then there is the design philosophy that our App is our own. We should have a common look and feel across platforms
for our App, and that our brand and our app is more important than the platform we are running on. Instagram is a good
example of this:</p>
<div style="text-align: center; margin-bottom: 20px">
<span style="vertical-align:top; display: inline-block">
<div>
<img src="/img/cordova-or-native/instagram-ios.png" style="border: 1px solid #000; margin: 0 10px 10px 0" />
</div>
<span class="figure">Instagram on iOS</span>
</span>
<span style="vertical-align:top; display: inline-block">
<div>
<img src="/img/cordova-or-native/instagram-android.png" style="border: 1px solid #000; margin: 0 10px 10px 0" />
</div>
<span class="figure">Instagram on Android</span>
</span>
</div>
<p>If you want your app to not mimic the features of the underlying OS, and have its own look and feel, award 3 points to Cordova.</p>
<h4 id="development-experience">Development Experience</h4>
<p>The experience your developers have will heavily factor into your decision. If you are sub-contracting you might need
to adjust this scale to somehow factor in the cost of sub-contractors (native iOS and Android developers unfairly or not
usually charge more than Cordova developers). I have no simple scale for this question, except maybe I would say add
2 points to Cordova for every developer who is an experienced web developer on your team, add 5 points to Cordova for
experienced Cordova developer and add 5 points to Native for experienced iOS or Android developer and subtract 5 points
from Native if you are missing a developer for that platform (i.e. if you have an experienced iOS developer, but no
experienced Android developer and you are shipping to both platforms then add 0 points since it is a wash). Feel free to
adjust the number of points awarded to each side based on the level of experience on the various platforms.</p>
<h4 id="speed-to-delivery">Speed to Delivery</h4>
<p>Everyone needs everything fast, but from experience I believe that you can create Cordova app faster than a native App,
in the end the maintenance will probably similar (and long term developers know that most of the lifecycle of a
long-term successful app is maintenance) but it will be faster to develop a Cordova app (even if it is only for one platform,
double true if it is for multiple platforms) than Native Apps. Add 3 points to Cordova if speed of initial delivery is
more important than the upkeep up the application.</p>
<h4 id="os-features-required">OS Features Required</h4>
<p>As a reader of this blog, you probably know what Cordova does best, and you probably know all of the built in features that
it has available to it (battery, camera, content-sync, dialogs, file, in-app-browser, media, vibration, network). These plugins are
all pretty battle tested and reliable (<a href="/phonegap/2016/03/20/phonegap-be-wary-of-plugins/">though not 100% reliable</a>),
however as soon as you need other functionality you may want to start removing points from Cordova. If there is a popular
plugin available you may be in luck, but look at the plugin very carefully. Here are some things to look for:</p>
<p>1) Is it frequently updated, though this is not 100% guaranteed to be show stopper, the plugin may be perfect and
just “work”, it is rare that mobile plugin will not have to be updated. Apple deprecates functions all the time (just
look at the “official” plugins when you compile in XCode or Android studio and see how many deprecation warnings there are),
and if a plugin has been abandoned you may be taking on more work than you bargained for. Frequently plugins are written
for apps and then when the app gets abandoned, so does the plugin. It may work perfectly, or may just need some fixes, or
may take weeks of your time getting it to work.</p>
<p>2) Is there are lot of users of the plugin. Look at the stars and forks on GitHub, the number of contributors, but also
look at the number of downloads it gets on the <a href="https://www.npmjs.com/">npm registry</a>. For example the cordova sanctioned
plugins will get 4000-5000 downloads a day, whereas a popular plugin like the facebook plugin will get 500 downloads a day.</p>
<p>3) Is there any real showstopping issues for the plugin. Spend the time doing due diligence and look at the github
issues for the plugin, does the the plugin author/owner seem to respond to issues. Is he taking pull requests?</p>
<p>4) Is there decent documentation and preferably an example app for the plugin (check to make sure the example isn’t
the default cordova app, this has happened to me a couple of times when I thought there was a sample app and it just
turned out to be Hello Cordova).</p>
<p>5) See if you can make your own quick and dirty sample app that does what you need it to do, and run it on all the platforms
you are going to run on. Obviously for something like <a href="https://github.com/j3k0/cordova-plugin-purchase">In App Purchase</a>
that might be a little too much work for preliminary research (though I can attest to how well that particular
plugin works, it’s awesome), but if it is something like a barcode scanner, make sure it functions like you expect.</p>
<p>6) Check StackOverflow and see if there are lingering issues with said Plugin.</p>
<p>7) Ask on the <a href="http://slack.cordova.io/">Slack Channels</a> if anyone has used the plugin, and what their experiences were.</p>
<p>If everything checks out, and you either don’t need plugins, or the plugins work great give Cordova +5 points, otherwise
+5 to Native. If you discover that it is something that you simply currently can’t do on Cordova, then really short-circuit
this whole process and go direct to Native. If you are going to be using plugins that may require some care and attention,
or you are going to be writing your own plugins, make sure you have access to developers who are comfortable in all the
platforms you will be developing for. Debugging plugins requires a fairly good understanding of the underlying platform.</p>
<h4 id="backend-requirements">Backend Requirements</h4>
<p>Lots of mobile apps have to communicate backend servers to be functional. Most modern services support various <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST</a>
protocols but sometimes you have to communicate with servers in older binary (like <a href="https://en.wikipedia.org/wiki/ISO_8583">ISO 8583</a>),
or XML protocols (like <a href="https://en.wikipedia.org/wiki/SOAP">Soap</a> or <a href="http://xml.coverpages.org/ebx.html">EBX</a>). Maybe
you have to work with a particularly complicated remote API where there are libraries in Java and C (or something that
iOS can work with) but not in Javascript. If that is the case, then award 5 points to native. If it either a JSON Rest API
or posting to a website and having to parse the results in HTML, Javascript potentially with the help of jQuery or another
Javascript library is going to be easier, award 3 points to Cordova.</p>
<h4 id="update-requirements">Update Requirements</h4>
<p>A lot of hay was made about ReactNative’s ability to update without going the App Store, and with multiple [plugins]
(https://github.com/nordnet/cordova-hot-code-push), including one from <a href="https://github.com/Microsoft/cordova-plugin-code-push">Microsoft</a>, or
with the <a href="http://docs.phonegap.com/phonegap-build/tools/hydration/">hydration</a> feature from PhoneGap build you can do the
same thing with Cordova. In the past, the week to two week updates times of the iOS App Store made this a truly compelling
feature, however with Apple’s new <a href="http://www.bloomberg.com/news/articles/2016-05-12/apple-shortens-app-review-times-in-push-to-boost-service-sales">push to shorten approval times</a>
to just one day that becomes a less appealing feature of Cordova. However, if you want really want to do <a href="https://www.agilealliance.org/glossary/continuous-deployment/">Continuous Deployment</a>,
or you are concerned that Apple will not be maintaining there speedy approval times in the future, chalk a couple of extra points up for the Cordova side.</p>
<h3 id="the-final-result">The Final Result</h3>
<p>Even if you end up ignoring the final tally (you may find after doing all the research required to come up with a number
for Cordova or Native that you have already decided on one direction or another) thinking about and researching each
of these methods will give you a feel on the way to go. Be pragmatic and patient, realize where the advantages of the various
methodologies lie and in the end, choose wisely. You will, if you have a successful product, have to live
with your decision for quite a while.</p>
<p><a href="http://agingcoder.com/programming/2016/09/03/should-i-use-cordovaphonegap-or-go-native/">Should I use Cordova / Phonegap or go Native?</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on September 03, 2016.</p>http://agingcoder.com/typescript/2016/06/09/typescript-all-the-things2016-06-09T00:00:00-07:002016-06-09T00:00:00-07:00Kris Ericksonhttp://agingcoder.comkristian.erickson@gmail.com
<p>Finally after three and a half years, I’m all in on <a href="https://www.typescriptlang.org/">TypeScript</a>. What really
pushed me over the line was creating a fairly large SPA (single page app) in Angular and TypeScript. Once you
got around the tooling (getting a good <a href="http://gulpjs.com/">gulp</a> script 9 months ago was half the struggle
but there are excellent samples and even pretty good generators now), and spent the couple of hours it takes
to get comfortable with typescript and tsd (which is now superseded by <a href="https://github.com/typings/typings">typings</a>)
and I was sold. Of course it makes less sense on a small project with few dependencies and just a few odd scripts,
but for any project of any size that I was to greenfield I would definitely start with TypeScript. And then,
after yet another bug at work where two parameters where flipped in a call, and another where two object
properties (photoCount and photosCount) where used interchangeably throughout the code causing subtle bugs that
took days to track down, I decided that I was going to bite the bullet and port our JavaScript codebase to TypeScript.
I had just recently heard a great <a href="https://devchat.tv/js-jabber/209-jsj-typescript-with-anders-hejlsberg">interview</a>
with <a href="https://twitter.com/ahejlsberg">Anders</a> about how you can literally just take all your .js files and rename them
.ts and get some of the advantages of TypeScript, so I set off to do so.</p>
<p><strong>Warning! This article is a bit of a deep dive, with lots of source code. Please let me know in the comments
if this is useful or whether it is best to talk in generalities without getting into the weeds.</strong></p>
<h4 id="tip-1----chrome-dev-tools-and-source-maps">Tip 1 – Chrome Dev Tools and Source Maps</h4>
<p>First, part of the reason I have become so attached to TypeScript is that since late 2014 (Chrome 39) <a href="https://developers.google.com/web/tools/chrome-devtools/debug/readability/source-maps?hl=en#source-maps-in-devtools-sources-panel">SourceMaps have been
enabled by default</a>
and work really well with TypeScript, there is basically only 1 trick to learn about debugging in Chrome Dev Tools (or Firefox)
in SourceMaps and that is that sometimes ‘this’ is ‘_this’. Which is to say, if you look at TypeScript output you will
discover that whenever you use an arrow function, it creates a reference to this in _this. See the example silly class below:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">class</span> <span class="nc">TimedLogger</span> <span class="p">{</span>
<span class="nf">constructor</span><span class="p">(</span><span class="k">private</span> <span class="n">msg</span><span class="p">:</span><span class="kt">string</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">public</span> <span class="nf">output</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">setTimeout</span><span class="p">(()</span> <span class="p">=></span> <span class="p">{</span>
<span class="n">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">msg</span><span class="p">);</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>and that converts to :</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="kt">var</span> <span class="n">TimedLogger</span> <span class="p">=</span> <span class="p">(</span><span class="nf">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="n">function</span> <span class="nf">TimedLogger</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">msg</span> <span class="p">=</span> <span class="n">msg</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">TimedLogger</span><span class="p">.</span><span class="n">prototype</span><span class="p">.</span><span class="n">output</span> <span class="p">=</span> <span class="nf">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="kt">var</span> <span class="n">_this</span> <span class="p">=</span> <span class="k">this</span><span class="p">;</span>
<span class="nf">setTimeout</span><span class="p">(</span><span class="nf">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="n">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="n">_this</span><span class="p">.</span><span class="n">msg</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">};</span>
<span class="k">return</span> <span class="n">TimedLogger</span><span class="p">;</span>
<span class="p">}());</span></code></pre></figure>
<p>So, when you are debugging in the Chrome Dev tools, and you set a breakpoint in the <strong>output</strong> function it will look like you can
type:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="gp">> </span>this.msg
undefined</code></pre></figure>
<p>which fails since this is actually bound to the window (global) scope, but if you type</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="gp">> </span>_this.msg
<span class="s2">"Hey Ma! It Works!"</span></code></pre></figure>
<p>you are all good. This was learned quickly when we developed the Spa app, but it still trips me up every now
and then (especially when dealing with the dreaded scoping of this in classes – more on that later) and it frequently
trips up our junior devs so I thought I would mention it at the top.</p>
<h4 id="tip-2----set-up-a-good-tsconfigjson-file">Tip 2 – Set up a good tsconfig.json File</h4>
<p>The first time I built a moderately large TypeScript application I omitted creating a tsconfig.json and did everything
with command line options called from a gulp file. Do not make this mistake, while it is nice to have the control
over what typescript files get compiled through watchers and the other advantages of grunt, you are going to want to
tsconfig.json to be able to set various options of how typescript compiles your files. While the default way to
specify all the files you are converting is to create a list of files</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="s2">"files"</span><span class="err">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"core.ts"</span><span class="p">,</span><span class="w">
</span><span class="s2">"sys.ts"</span><span class="p">,</span><span class="w">
</span><span class="s2">"types.ts"</span><span class="p">,</span><span class="w">
</span><span class="s2">"scanner.ts"</span><span class="p">,</span><span class="w">
</span><span class="s2">"parser.ts"</span><span class="p">,</span><span class="w">
</span><span class="s2">"utilities.ts"</span><span class="p">,</span><span class="w">
</span><span class="s2">"binder.ts"</span><span class="w">
</span><span class="p">]</span></code></pre></figure>
<p>this is not good for us lazy programmers with hundreds of files to compile, especially if they are a bunch of old
javascript files that are being renamed and moved into more appropriate folders as we going through the process of
turning all our old JavaScript into TypeScript. The better way is to use filesGlob, a la:</p>
<figure class="highlight"><pre><code class="language-json" data-lang="json"><span class="s2">"filesGlob"</span><span class="err">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"typings/tsd.d.ts"</span><span class="p">,</span><span class="w">
</span><span class="s2">"ts/**/*.ts"</span><span class="w">
</span><span class="p">]</span></code></pre></figure>
<p>however, here is the rub with fileGlob (which is hopefully fixed by the time you read this), it is clearly caching the
list files because when you add a new file to the ts directory it won’t compile it. My current solution is to change
the second glob (“ts/<em>*/</em>.ts”) to point directly to the file and run typescript again (or have <a href="https://www.jetbrains.com/webstorm/">WebStorm</a>
run typescript on a watcher, see more about WebStorm and TypeScript later in this post). Then change the filesGlob back
to “ts/<em>*/</em>.ts” and it will have smartened up.</p>
<p>There are very useful options available to you in the tsconfig.json file:</p>
<p><strong>noEmitOnError</strong> - Don’t output the js file until there are no errors in the ts file.<br />
<strong>noFallthroughCasesInSwitch</strong> - Don’t allow fall throughs on case switches.<br />
<strong>target</strong> - The language to target, for now you want es5 but es6 should be soon.</p>
<p>A full list of compiler options can be found <a href="https://www.typescriptlang.org/docs/handbook/compiler-options.html">here</a>.</p>
<h4 id="tip-3---use-typings-definitely-typed">Tip 3 - Use Typings (definitely typed)</h4>
<p>To get proper IntelliSense or any kind of code completion and static type checking in your editor (whether it is WebStorm,
<a href="https://code.visualstudio.com/">Visual Studio Code</a>, or <a href="https://github.com/leafgarland/typescript-vim">Vim with TypeScript support</a>)
you need to install with Typings all the libraries you are using (unless of course you are really creating VanillaJS TypeScript and
if you are <a href="https://cdn.meme.am/instances/500x/56204219.jpg">kudos</a>). It gives you more than code completion, of course,
it also tells the TypeScript compiler how you should be using various APIs and you will get warnings if you are using
them wrong.</p>
<p>If you aren’t going to go all in, and are only going to convert a few of your JavaScript files you are going to want to
add your own my-typings.d.ts (or something much better named) where you can document the libraries you aren’t going to
convert, and say, any JQuery plugins you might have written. Included below, to get you started, is the definitions
for a hypothetical block/unblock jquery extension that allowed you disable access to some UI elements. It would could be used
like:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">$</span><span class="p">(</span><span class="s1">'#okButton'</span><span class="p">).</span><span class="nx">block</span><span class="p">({</span><span class="na">toolTip</span><span class="p">:</span> <span class="s1">'Please agree to terms and conditions'</span><span class="p">}});</span>
<span class="c1">// Or optionally</span>
<span class="nx">$</span><span class="p">.</span><span class="nx">unblock</span><span class="p">(</span><span class="s1">'#okButton'</span><span class="p">);</span></code></pre></figure>
<p>The following should be pretty self-explanatory and this isn’t an article on writing definitely typed syntax but quickly
going over it, the DefaultStatic allows selection through a selector string and takes an optional object for the options.
The DefaultJquery requires calling from a JQuery object and also takes an optional Object. Of course you can make these
much more detailed (you can specify the potential options, you could make the DefaultStatic take an element or a
JQuery object, but the intention is pretty clear). Also, note this does require having already installed the jquery.d.ts
file through typings.</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">MyJquery</span> <span class="p">{</span>
<span class="k">interface</span> <span class="n">DefaultStatic</span> <span class="p">{</span>
<span class="p">(</span><span class="n">selector</span><span class="p">:</span><span class="kt">string</span><span class="p">,</span> <span class="n">options</span><span class="p">?:</span><span class="n">Object</span><span class="p">):</span><span class="n">JQuery</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">interface</span> <span class="n">DefaultJQuery</span> <span class="p">{</span>
<span class="p">(</span><span class="n">options</span><span class="p">?:</span><span class="n">Object</span><span class="p">):</span><span class="n">JQuery</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">interface</span> <span class="n">JQueryStatic</span> <span class="p">{</span>
<span class="n">block</span><span class="p">:</span><span class="n">MyJquery</span><span class="p">.</span><span class="n">DefaultStatic</span><span class="p">;</span>
<span class="n">unblock</span><span class="p">:</span><span class="n">MyJquery</span><span class="p">.</span><span class="n">DefaultStatic</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">interface</span> <span class="n">JQuery</span> <span class="p">{</span>
<span class="n">block</span><span class="p">:</span><span class="n">MyJquery</span><span class="p">.</span><span class="n">DefaultJquery</span><span class="p">;</span>
<span class="n">unblock</span><span class="p">:</span><span class="n">MyJquery</span><span class="p">.</span><span class="n">DefaultJquery</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<h4 id="tip-4---use-tslint">Tip 4 - Use tslint</h4>
<p>To get even further benefits from TypeScript (and some help that might track down some of the errors you might
encounter if you are going to convert all of your old Javascript to Classes and the like) you may want to use
<a href="https://palantir.github.io/tslint/">TSLint</a>. Spend some time looking at the <a href="http://palantir.github.io/tslint/rules/">rules</a>,
and the example <a href="https://github.com/palantir/tslint/blob/master/docs/sample.tslint.json">tslint.json</a> file,
but I would recommend heavily altering the sample tslint.json file (it is <strong>very</strong> strict and has some
weird checks for things I think TypeScript removes the need for (trailing-comma, use-strict, etc)). While you
may want to get everything working first, and then run tslint, be aware that it is going to hurt your feelings about
your code if you aren’t ready for it, especially if you use said sample config file. Also you should note that if
you do use tslint, and you are obsessive about getting rid of all of the errors like me, converting to typescript
will take a <strong>lot</strong> longer. Giving types to every variable, even if typescript can infer it’s type may be overkill,
but it will give a lot more information to the static analysis engine in typescript (see Tip 5 below).</p>
<h4 id="tip-5---use-an-editor-with-support-for-typescript">Tip 5 - Use an Editor with support for TypeScript</h4>
<p>I love <a href="https://www.jetbrains.com/webstorm/">WebStorm</a> and all the JetBrains IDE variants, and I would highly recommend
it for developing in TypeScript (you won’t even need to write a Gulp/Grunt script it will do almost all of that work for
you out of the box) but there are a lot of editors (see Tip 3) that have plugins that work with the TypeScript compiler
to make you more productive, and help you automatically detect errors. The built in <a href="https://www.jetbrains.com/help/webstorm/2016.1/typescript-support.html">support for TypeScript</a>
and tslint makes me think that it is a no brainer, but if you prefer VS Code (where it works out of the box), or
Sublime <a href="https://github.com/Microsoft/TypeScript-Sublime-Plugin">the plugin is here</a>, Vim <a href="https://github.com/Quramy/tsuquyomi">plugin</a>,
or even ughh, Eclipse <a href="https://github.com/angelozerr/typescript.java">plugin</a> make sure that you are getting all the help that the
<a href="https://github.com/Microsoft/TypeScript/blob/master/bin/tsserver">TypeScript Language Server</a> can give you.</p>
<p>TypeScript was specifically written to enable tooling improvements and you should take advantage of the fact that
computers are very good at <a href="https://en.wikipedia.org/wiki/Static_program_analysis">static analysis</a>
if they have a language that is “strict” enough to support it. TypeScript gives JavaScript the information that
static analysis requires to do its job (of course if you don’t supply types or make every type ‘any’ then there is
less static analysis that can be done by the compiler and the utility of converting to TypeScript is greatly reduced.)</p>
<h4 id="tip-6---namespaces-remove-the-need-for-iifes">Tip 6 - Namespaces remove the need for iife’s</h4>
<p>The first thing I do when I convert a js file is turn the iife (immediately invoked function expression, or a <a href="https://twitter.com/jeresig">Resig</a>
as I believe <a href="https://twitter.com/shanselman">Scott Hanselman</a> used to call them) that surrounds all the code in the file
into a namespace. So</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Code here</span>
<span class="p">})();</span></code></pre></figure>
<p>becomes</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">testproject</span> <span class="p">{</span>
<span class="c1">// Code here
</span><span class="p">}</span></code></pre></figure>
<p>Note: If you look at the output in JS you will note that every dot becomes a new iife with variables attached so don’t use
Java style namespaces. For example:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">com.ericksoft.recipefolder</span> <span class="p">{</span>
<span class="n">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="err">'</span><span class="n">Hello</span> <span class="n">Nurse</span><span class="p">!</span><span class="err">'</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>becomes:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">com</span><span class="p">;</span>
<span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">com</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">ericksoft</span><span class="p">;</span>
<span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">ericksoft</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">recipefolder</span><span class="p">;</span>
<span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">recipefolder</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'Hello Nurse!'</span><span class="p">);</span>
<span class="p">})(</span><span class="nx">recipefolder</span> <span class="o">=</span> <span class="nx">ericksoft</span><span class="p">.</span><span class="nx">recipefolder</span> <span class="o">||</span> <span class="p">(</span><span class="nx">ericksoft</span><span class="p">.</span><span class="nx">recipefolder</span> <span class="o">=</span> <span class="p">{}));</span>
<span class="p">})(</span><span class="nx">ericksoft</span> <span class="o">=</span> <span class="nx">com</span><span class="p">.</span><span class="nx">ericksoft</span> <span class="o">||</span> <span class="p">(</span><span class="nx">com</span><span class="p">.</span><span class="nx">ericksoft</span> <span class="o">=</span> <span class="p">{}));</span>
<span class="p">})(</span><span class="nx">com</span> <span class="o">||</span> <span class="p">(</span><span class="nx">com</span> <span class="o">=</span> <span class="p">{}));</span></code></pre></figure>
<h4 id="tip-7---you-dont-have-to-turn-everything-into-classes">Tip 7 - You don’t have to turn everything into classes</h4>
<p>The first couple of files I converted I decided that I would convert from our old style classes (we for the most part have
our classes built upon functions and have eschewed the speed of prototypical functions for the advantages of private
functions and variables – I don’t say that this is the best way to do classes (and it is not the way that TypeScript
impliments them under the hood), but it has worked for us in the past. So we had a class like this:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">Employee</span><span class="p">(</span><span class="nx">initialName</span><span class="p">,</span> <span class="nx">id</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">name</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">greetCount</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="nx">setName</span><span class="p">(</span><span class="nx">initialName</span><span class="p">);</span>
<span class="kd">function</span> <span class="nx">setName</span><span class="p">(</span><span class="nx">val</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Name validation goes here</span>
<span class="nx">name</span> <span class="o">=</span> <span class="nx">val</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">greet</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="s1">'Hello '</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">name</span> <span class="o">+</span> <span class="s1">', your employee id is '</span> <span class="o">+</span> <span class="nx">id</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">})();</span></code></pre></figure>
<p>and after renaming it, adding the namespace and bunch of flipping it became this:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">test</span> <span class="p">{</span>
<span class="k">class</span> <span class="nc">Employee</span> <span class="p">{</span>
<span class="k">private</span> <span class="n">id</span><span class="p">:</span><span class="kt">string</span><span class="p">;</span>
<span class="k">private</span> <span class="n">greetCount</span><span class="p">:</span><span class="n">number</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="k">public</span> <span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">;</span>
<span class="nf">constructor</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">,</span> <span class="n">id</span><span class="p">:</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">id</span> <span class="p">=</span> <span class="n">id</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nf">setName</span><span class="p">(</span><span class="n">name</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="nf">greet</span><span class="p">():</span><span class="k">void</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">greetCount</span><span class="p">++;</span>
<span class="k">return</span> <span class="err">'</span><span class="n">Hello</span> <span class="err">'</span> <span class="p">+</span> <span class="k">this</span><span class="p">.</span><span class="n">name</span> <span class="p">+</span> <span class="err">'</span><span class="p">,</span> <span class="n">your</span> <span class="n">employee</span> <span class="n">id</span> <span class="k">is</span> <span class="err">'</span> <span class="p">+</span> <span class="k">this</span><span class="p">.</span><span class="n">id</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">setName</span><span class="p">(</span><span class="n">val</span><span class="p">:</span><span class="kt">string</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="c1">// Name validation goes here
</span> <span class="k">this</span><span class="p">.</span><span class="n">name</span> <span class="p">=</span> <span class="n">val</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>Which isn’t a lot of work for small files, but it is a lot of Regex Replace /this.(\w+) = function/public $1/ and
Regex Replace /function (\w+)(/private $1(/ and Regex Replace /var (\w+);/private $1;/ and then manaully adding a lot
of this references.</p>
<h4 id="tip-8---jquery-can-be-a-nemesis">Tip 8 - JQuery can be a nemesis</h4>
<p>Then I started running into problems, because there are a lot of JQuery calls in our
code, and if something calls that anonymous function without correctly setting the scope of this then you are headed
into a heap of trouble. For example, suppose we have some Controller that is using jQuery:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">EmployeeController</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$</span><span class="p">(</span><span class="s1">'#nameField'</span><span class="p">).</span><span class="nx">val</span><span class="p">(</span><span class="nx">name</span><span class="p">).</span><span class="nx">on</span><span class="p">(</span><span class="s1">'blur'</span><span class="p">,</span> <span class="nx">nameFieldChanged</span><span class="p">);</span>
<span class="kd">function</span> <span class="nx">nameFieldChanged</span><span class="p">(</span><span class="nx">e</span><span class="err">:</span><span class="nx">JQueryInputEventObject</span><span class="p">)</span><span class="err">:</span><span class="k">void</span> <span class="p">{</span>
<span class="nx">name</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">val</span><span class="p">();</span>
<span class="nx">outputName</span><span class="p">();</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">outputName</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">name</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">tmp</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">EmployeeController</span><span class="p">(</span><span class="s1">'John Doe'</span><span class="p">);</span>
<span class="p">})();</span></code></pre></figure>
<p>We naively convert it to TypeScript</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">test</span> <span class="p">{</span>
<span class="k">class</span> <span class="nc">EmployeeController</span> <span class="p">{</span>
<span class="nf">constructor</span><span class="p">(</span><span class="k">private</span> <span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
<span class="err">$</span><span class="p">(</span><span class="err">'#</span><span class="n">nameField</span><span class="err">'</span><span class="p">).</span><span class="nf">val</span><span class="p">(</span><span class="n">name</span><span class="p">).</span><span class="k">on</span><span class="p">(</span><span class="err">'</span><span class="n">blur</span><span class="err">'</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="n">nameFieldChanged</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">nameFieldChanged</span><span class="p">(</span><span class="n">e</span><span class="p">:</span><span class="n">JQueryInputEventObject</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">name</span> <span class="p">=</span> <span class="err">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nf">val</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nf">outputName</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">outputName</span><span class="p">()</span> <span class="p">{</span>
<span class="n">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">name</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">var</span> <span class="n">tmp</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">EmployeeController</span><span class="p">(</span><span class="err">'</span><span class="n">John</span> <span class="n">Doe</span><span class="err">'</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>But if we look in the console, we see that we get an error (you can see the converted Javascript and what happens at
this <a href="https://jsfiddle.net/ksoncan34/nbwn4Lka/">fiddle</a>).</p>
<p><code style="color:red; background: #e5e5e5; border: 1px solid #999; padding: 5px; width: 100%; display: inline-block">Uncaught TypeError: this.outputName is not a function</code></p>
<p>So what is going on? Those who look for a minute will quickly realize (and I added a hint, using the jQuery shorthand $(this).val())
that the scope of this is scoped to the jQuery element that was blurred, and not scoped to our EmployeeController class at all.
So not only can we not call the prive outputName function, but we are actually setting name on the nameField input element
and not changing the name of our user at all. How to fix this? Well, for one, <strong>never</strong> bind jQuery callbacks to private or
public class members (quick note, there is really no difference in typescript between private and public class members and
the only thing stopping you from calling private members of TypeScript classes is the compiler will complain). Also, you
are going to ween yourself off of the this scope in jQuery – it’s confusing and leads to more unintentional bugs than you
probably realize. And you are going to have to use an arrow function. Let’s look at our fixed class:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">test</span> <span class="p">{</span>
<span class="k">class</span> <span class="nc">EmployeeController</span> <span class="p">{</span>
<span class="nf">constructor</span><span class="p">(</span><span class="k">private</span> <span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
<span class="err">$</span><span class="p">(</span><span class="err">'#</span><span class="n">nameField</span><span class="err">'</span><span class="p">).</span><span class="nf">val</span><span class="p">(</span><span class="n">name</span><span class="p">).</span><span class="k">on</span><span class="p">(</span><span class="err">'</span><span class="n">blur</span><span class="err">'</span><span class="p">,</span> <span class="p">(</span><span class="n">e</span><span class="p">:</span><span class="n">JQueryInputEventObject</span><span class="p">):</span><span class="k">void</span> <span class="p">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nf">nameFieldChanged</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">nameFieldChanged</span><span class="p">(</span><span class="n">e</span><span class="p">:</span><span class="n">JQueryInputEventObject</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">name</span> <span class="p">=</span> <span class="err">$</span><span class="p">(</span><span class="n">e</span><span class="p">.</span><span class="n">currentTarget</span><span class="p">).</span><span class="nf">val</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nf">outputName</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">outputName</span><span class="p">()</span> <span class="p">{</span>
<span class="n">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">name</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kt">var</span> <span class="n">tmp</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">EmployeeController</span><span class="p">(</span><span class="err">'</span><span class="n">John</span> <span class="n">Doe</span><span class="err">'</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>
<p>Now everything works. See the updated <a href="https://jsfiddle.net/ksoncan34/fwexam4h/">fiddle</a>. Now you might not think that
this will be a problem to find, but if you are using a lot of these you will get burned missing a few (at least I did).</p>
<h4 id="tip-9---callbacks-are-also-problematic-use-promises-instead">Tip 9 - Callbacks are also problematic, use Promises instead</h4>
<p>Similar to the jQuery issues, is the issue of callbacks. If you are used to passing in callbacks to a function to handle
asynchronous functions you might also have issues with TypeScript classes. For example:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">test</span> <span class="p">{</span>
<span class="k">class</span> <span class="nc">UserController</span> <span class="p">{</span>
<span class="k">public</span> <span class="nf">constructor</span><span class="p">(</span><span class="k">private</span> <span class="n">toastService</span><span class="p">:</span><span class="n">ToastService</span><span class="p">,</span> <span class="k">private</span> <span class="n">jsonService</span><span class="p">:</span><span class="n">JsonService</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">public</span> <span class="nf">changeName</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nf">updateNameAsync</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="nf">function</span> <span class="p">(</span><span class="n">success</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nf">toastService</span><span class="p">(</span><span class="err">'</span><span class="n">Name</span> <span class="n">updated</span><span class="err">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">updateNameAsync</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">,</span> <span class="n">callback</span><span class="p">:(</span><span class="n">success</span><span class="p">:</span><span class="n">boolean</span><span class="p">)</span> <span class="p">=></span> <span class="k">void</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="c1">// Name validation amd other stuff happens here...
</span> <span class="k">this</span><span class="p">.</span><span class="n">jsonService</span><span class="p">.</span><span class="nf">updateName</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="nf">function</span> <span class="p">(</span><span class="n">success</span><span class="p">:</span><span class="n">boolean</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="nf">callback</span><span class="p">(</span><span class="n">success</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>would throw an error when the service was updated, since toastService doesn’t exist in the global scope. You have to
remember to perform all your class callbacks with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call">.call</a>
or <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply">.apply</a>. So to properly
use a callback in a TypeScript class you must use arrow functions and either call or apply:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">test</span> <span class="p">{</span>
<span class="k">class</span> <span class="nc">UserController</span> <span class="p">{</span>
<span class="k">public</span> <span class="nf">constructor</span><span class="p">(</span><span class="k">private</span> <span class="n">toastService</span><span class="p">:</span><span class="n">ToastService</span><span class="p">,</span> <span class="k">private</span> <span class="n">jsonService</span><span class="p">:</span><span class="n">JsonService</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">public</span> <span class="nf">changeName</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nf">updateNameAsync</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="p">(</span><span class="n">success</span><span class="p">)</span> <span class="p">=></span> <span class="p">{</span> <span class="c1">// Note the fat-arrow
</span> <span class="k">this</span><span class="p">.</span><span class="nf">toastService</span><span class="p">(</span><span class="err">'</span><span class="n">Name</span> <span class="n">updated</span><span class="err">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">updateNameAsync</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">,</span> <span class="n">callback</span><span class="p">:(</span><span class="n">success</span><span class="p">:</span><span class="n">boolean</span><span class="p">)</span> <span class="p">=></span> <span class="k">void</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="c1">// Name validation amd other stuff happens here...
</span> <span class="k">this</span><span class="p">.</span><span class="n">jsonService</span><span class="p">.</span><span class="nf">updateName</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="p">(</span><span class="n">success</span><span class="p">:</span><span class="n">boolean</span><span class="p">):</span><span class="k">void</span> <span class="p">=></span> <span class="p">{</span> <span class="c1">// Note the fat-arrow
</span> <span class="n">callback</span><span class="p">.</span><span class="nf">apply</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="p">[</span><span class="n">success</span><span class="p">]);</span>
<span class="c1">// Optionally callback.call(this, success);
</span> <span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>However, a much better way to go is to use promises rather than callbacks, preferably the <a href="http://documentup.com/kriskowal/q/">Q</a> library or
the $q service in Angular, or if <a href="http://stackoverflow.com/questions/23744612/problems-inherent-to-jquery-deferred-jquery-1-x-2-x">worse comes to worse</a>
using <a href="https://api.jquery.com/jquery.deferred/">jQuery.Deferred</a> (though if you are using <a href="http://blog.jquery.com/2016/06/09/jquery-3-0-final-released/">jQuery 3</a>,
its Deferred is <a href="https://github.com/promises-aplus/promises-tests">Promises/A+ compliant</a>).</p>
<p>The above code becomes much nicer (especially with <a href="https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Generics.md">Generics in TypeScript</a>)
using promises:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">namespace</span> <span class="nn">test</span> <span class="p">{</span>
<span class="k">class</span> <span class="nc">UserController</span> <span class="p">{</span>
<span class="k">public</span> <span class="nf">constructor</span><span class="p">(</span><span class="k">private</span> <span class="n">toastService</span><span class="p">:</span><span class="n">ToastService</span><span class="p">,</span> <span class="k">private</span> <span class="n">jsonService</span><span class="p">:</span><span class="n">JsonService</span><span class="p">)</span> <span class="p">{}</span>
<span class="k">public</span> <span class="nf">changeName</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">):</span><span class="k">void</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nf">updateNameAsync</span><span class="p">(</span><span class="n">name</span><span class="p">).</span><span class="nf">then</span><span class="p">((</span><span class="n">success</span><span class="p">)</span> <span class="p">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nf">toastService</span><span class="p">(</span><span class="err">'</span><span class="n">Name</span> <span class="n">updated</span><span class="err">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">private</span> <span class="nf">updateNameAsync</span><span class="p">(</span><span class="n">name</span><span class="p">:</span><span class="kt">string</span><span class="p">):</span><span class="n">Q</span><span class="p">.</span><span class="n">Promise</span><span class="p"><</span><span class="n">boolean</span><span class="p">></span> <span class="p">{</span>
<span class="kt">var</span> <span class="n">defer</span> <span class="p">=</span> <span class="n">Q</span><span class="p">.</span><span class="n">defer</span><span class="p"><</span><span class="n">boolean</span><span class="p">>();</span>
<span class="c1">// Name validation amd other stuff happens here...
</span> <span class="k">this</span><span class="p">.</span><span class="n">jsonService</span><span class="p">.</span><span class="nf">updateName</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="p">(</span><span class="n">success</span><span class="p">:</span><span class="n">boolean</span><span class="p">):</span><span class="k">void</span> <span class="p">=></span> <span class="p">{</span> <span class="c1">// Note the fat-arrow
</span> <span class="n">defer</span><span class="p">.</span><span class="nf">resolve</span><span class="p">(</span><span class="n">success</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">return</span> <span class="n">defer</span><span class="p">.</span><span class="n">promise</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<h4 id="tip-10---go-with-the-spirit-of-typescript-instead-of-trying-to-trick-it">Tip 10 - Go with the spirit of TypeScript instead of trying to trick it</h4>
<p>One of the advantages of JavaScript and other untyped languages is that you can easily mutate variables, a string can
be come a number, or even an object. To get this working without the TypeScript compiler yelling at you, you will need
to make these types any. Resist that impulse and think about refactoring your code. While it is not the worst thing
in the world to have these types (in fact I used to do a lot of tricks to make optional args work this way), it will
make it much harder for the TypeScript compiler to help you validate your work. If you work against the spirit of TypeScript
(which is that all variables should have a type) then you create areas in your code where you cannot rely on the TypeScript
compiler to check your work. This is kind of like having large areas of code without UnitTests, it probably works, but you
are never really sure.</p>
<p>For example, suppose we had the following JavaScript function that took two optional arguments:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="cm">/**
* @param url {String}
* @param x {Number}
* @param y {Number}
* @param [size] {Number}
* @param [options] {Object}
*/</span>
<span class="kd">function</span> <span class="nx">drawSquare</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">size</span><span class="p">,</span> <span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">size</span> <span class="o">===</span> <span class="s1">'object'</span> <span class="o">||</span> <span class="o">!</span><span class="nx">size</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">size</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="nx">options</span> <span class="o">=</span> <span class="nx">size</span> <span class="o">||</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="c1">// ... Draw the square</span>
<span class="p">}</span></code></pre></figure>
<p>and we converted it TypeScript</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="n">function</span> <span class="nf">drawSquare</span><span class="p">(</span><span class="n">url</span><span class="p">:</span><span class="kt">string</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span><span class="n">number</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span><span class="n">number</span><span class="p">,</span> <span class="n">size</span><span class="p">?:</span><span class="n">number</span><span class="p">,</span> <span class="n">options</span><span class="p">?:</span><span class="n">Object</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="n">size</span> <span class="p">===</span> <span class="err">'</span><span class="kt">object</span><span class="err">'</span> <span class="p">||</span> <span class="p">!</span><span class="n">size</span><span class="p">)</span> <span class="p">{</span>
<span class="n">size</span> <span class="p">=</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
<span class="n">options</span> <span class="p">=</span> <span class="n">size</span> <span class="p">||</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="c1">// ... Draw the square
</span><span class="p">}</span></code></pre></figure>
<p>you are going to get a warning that a number can’t be converted to an object. So either you for the type:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"> <span class="n">options</span> <span class="p">=</span> <span class="p">(</span><span class="n">size</span> <span class="k">as</span> <span class="n">any</span><span class="p">)</span> <span class="p">||</span> <span class="p">{};</span> <span class="p">//</span> <span class="p"><!---</span></code></pre></figure>
<p>**Note: use [as any rather than (<any>size) to allow for future use of JSX](https://github.com/Microsoft/TypeScript/issues/296).**</any></p>
<p>But a better way is to go with the spirit of TypeScript and not mutate types, so instead of changing the type of arguments,
create variables in the function and assign them the proper value.</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="n">function</span> <span class="nf">drawSquare</span><span class="p">(</span><span class="n">url</span><span class="p">:</span><span class="kt">string</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span><span class="n">number</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span><span class="n">number</span><span class="p">,</span> <span class="n">inSize</span><span class="p">?:</span><span class="n">number</span><span class="p">,</span> <span class="n">inOptions</span><span class="p">?:</span><span class="n">Object</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">var</span> <span class="n">size</span><span class="p">:</span><span class="n">number</span><span class="p">;</span>
<span class="kt">var</span> <span class="n">options</span><span class="p">:</span><span class="n">Object</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="n">inSize</span> <span class="p">===</span> <span class="err">'</span><span class="kt">object</span><span class="err">'</span> <span class="p">||</span> <span class="p">!</span><span class="n">inSize</span><span class="p">)</span> <span class="p">{</span>
<span class="n">size</span> <span class="p">=</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
<span class="n">options</span> <span class="p">=</span> <span class="n">size</span> <span class="p">||</span> <span class="p">{};</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">size</span> <span class="p">=</span> <span class="n">inSize</span><span class="p">;</span>
<span class="n">options</span> <span class="p">=</span> <span class="n">inOptions</span> <span class="p">||</span> <span class="p">{};</span>
<span class="p">}</span>
<span class="c1">// ... Draw the square
</span><span class="p">}</span></code></pre></figure>
<p>Obviously this is a simplistic example, but I think it helps to realize that in Javascript we are no longer trying to
compress as much code into as few lines as possible, and the addition of a couple lines (and a couple variables) will
provide more readable code that doesn’t force the mutation of objects. In our code we had some pretty crazy functions
that had a large number of optional variables and huge blocks of code that checked what parameters were passed in and
which optional parameters were omitted. We made the decision to make none of the arguments optional, and found that there
were several places where the function was being called were the handling that adjusted parameters was only working
by fluke (the values of the parameters were actually invalid but the invalid value triggered a default behavior that was
the desired value). Another way to go (rather than optional parameters) is to use a parameters object that is strongly typed.
For the above example:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">interface</span> <span class="n">IDrawSquareParameters</span> <span class="p">{</span>
<span class="n">url</span><span class="p">:</span><span class="kt">string</span><span class="p">;</span>
<span class="n">x</span><span class="p">:</span><span class="n">number</span><span class="p">;</span>
<span class="n">y</span><span class="p">:</span><span class="n">number</span><span class="p">;</span>
<span class="n">size</span><span class="p">?:</span><span class="n">number</span><span class="p">;</span>
<span class="n">options</span><span class="p">?:</span> <span class="p">{</span>
<span class="n">border</span><span class="p">?:</span> <span class="n">boolean</span><span class="p">;</span>
<span class="n">borderColor</span><span class="p">?:</span> <span class="kt">string</span><span class="p">;</span>
<span class="n">borderWidth</span><span class="p">?:</span><span class="n">number</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">function</span> <span class="nf">drawSquare</span><span class="p">(</span><span class="n">parameters</span><span class="p">:</span><span class="n">IDrawSquareParameters</span><span class="p">)</span> <span class="p">{</span>
<span class="n">parameters</span><span class="p">.</span><span class="n">size</span> <span class="p">=</span> <span class="n">parameters</span><span class="p">.</span><span class="n">size</span> <span class="p">||</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
<span class="n">parameters</span><span class="p">.</span><span class="n">options</span> <span class="p">=</span> <span class="n">parameters</span><span class="p">.</span><span class="n">options</span> <span class="p">||</span> <span class="p">{};</span>
<span class="p">}</span></code></pre></figure>
<p>The nice thing about using a parameters interface, is that the values are checked by the compiler, but the defination of
the interface takes no space in the compiled code.</p>
<h4 id="tip-11---enjoy">Tip 11 - Enjoy</h4>
<p>I know reading over this document it appears that switching from JavaScript to TypeScript seems like a lot of work,
and to be fair to get all the benefits from TypeScript it is a fair bit of work, but you don’t have to do all the work
at once and once you have TypeScript up and running you will be living and working in a better world. You can confidently
refactor and find code usages that were more string matching in the old JavaScript world. You know what methods are available
for every class (and you have proper JavaScript classes before ES6 arrives in all browsers). You know exactly what arguments
are required for each function and what is returned just through intellisense rather than having to the code for the
function you are calling. You can use namespaces, classes, inheritance, decorators, rocket functions, and a lot of the great features of ES6/7 without
waiting for browsers to support all these features. You also get the advantages of <a href="http://definitelytyped.org/">DefinatelyTyped</a>
for older libraries. And since TypeScript is rapidly evolving you will see future Javascript features like async/await, JSX, generators,
mixins, non-nullable types, variadic types, and tons of other <a href="https://github.com/Microsoft/TypeScript/wiki/Roadmap">features</a> coming
in the near future. I highly recommend switching to TypeScript, you will be glad you did!</p>
<p><a href="http://agingcoder.com/typescript/2016/06/09/typescript-all-the-things/">Typescript All The Things! Lessons learned moving a large codebase to typescript</a> was originally published by Kris Erickson at <a href="http://agingcoder.com">Aging Coder</a> on June 09, 2016.</p>