<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Julien Enselme personal blog - Aurelia</title><link href="https://www.jujens.eu/" rel="alternate"></link><link href="https://www.jujens.eu/feeds/aurelia.atom.xml" rel="self"></link><id>https://www.jujens.eu/</id><updated>2022-09-11T00:00:00+02:00</updated><entry><title>Aurelia Store and Immer</title><link href="https://www.jujens.eu/posts/en/2020/Aug/09/aurelia-store-immerjs/" rel="alternate"></link><published>2020-08-09T00:00:00+02:00</published><updated>2020-08-09T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2020-08-09:/posts/en/2020/Aug/09/aurelia-store-immerjs/</id><summary type="html">&lt;p&gt;I spoke last year of Aurelia store in &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2019/Aug/24/aurelia-store/"&gt;this article&lt;/a&gt;
At the time, it wasn't possible to combine Aurelia Store and &lt;a class="reference external" href="https://immerjs.github.io/immer/docs/introduction"&gt;Immer&lt;/a&gt;.
The root cause of the issue was fixed in the latest version of Immer (7.0), so we can now combine both.&lt;/p&gt;
&lt;p&gt;Let's study how to do this …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I spoke last year of Aurelia store in &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2019/Aug/24/aurelia-store/"&gt;this article&lt;/a&gt;
At the time, it wasn't possible to combine Aurelia Store and &lt;a class="reference external" href="https://immerjs.github.io/immer/docs/introduction"&gt;Immer&lt;/a&gt;.
The root cause of the issue was fixed in the latest version of Immer (7.0), so we can now combine both.&lt;/p&gt;
&lt;p&gt;Let's study how to do this:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Install immer with &lt;tt class="docutils literal"&gt;npm install &lt;span class="pre"&gt;--save&lt;/span&gt; &lt;span class="pre"&gt;immer&amp;#64;^7.0.7&lt;/span&gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;yarn add &lt;span class="pre"&gt;immer&amp;#64;^7.0.7&lt;/span&gt;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Because we need Aurelia to update the state when it's used in component to add some observers, we must disable immer's &lt;a class="reference external" href="https://immerjs.github.io/immer/docs/freezing"&gt;auto-freezing&lt;/a&gt;. You can do this in &lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;app.js&lt;/tt&gt; with:&lt;/p&gt;
&lt;pre class="code JavaScript literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setAutoFreeze&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'immer'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// This is required to allow Aurelia to add its observer on objects in the state.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;setAutoFreeze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Update you actions to use &lt;tt class="docutils literal"&gt;produce&lt;/tt&gt;. This will allow you to modify the state directly without mutating it. This way, the history features of the store will be preserved. For a simple example:&lt;/p&gt;
&lt;pre class="code JavaScript literal-block"&gt;
&lt;span class="c1"&gt;// Before.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;selectBackend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;selectedBackend&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SelectableBackend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedBackend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;selectedBackend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c1"&gt;// After&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;selectBackend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;produce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;selectedBackend&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;SelectableBackend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedBackend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;selectedBackend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Thanks to immer, you don't have to copy the state explicitly, immer will create a new state based on your modification automatically.
The &lt;tt class="docutils literal"&gt;produce&lt;/tt&gt; function takes a function that will receive the draft of the state you can modify as first argument and all other arguments passed to you action as extra arguments.
So the function you need to pass to &lt;tt class="docutils literal"&gt;produce&lt;/tt&gt; takes the same arguments as a standard action.
You still need to be aware and pay attention to some details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;All the objects in the store are changed so immer can track the changes without modifying the original objects. This means that this won't work:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;deleteArticle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;produce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c1"&gt;// Will return -1 because article is not in the articles array, it's proxied version is.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;indexOfArticleToDelete&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;articles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Only the state will be a &amp;quot;draft&amp;quot; (to use immer terminology) and can be modified directly. If you were to modify other argument, you would mutate them. And if they are in the store, you would update them the store as well. So this won't work as expected and will mutate the object:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;markAsRead&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;produce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isRead&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For both these issues, you need to first find the proper object in the draft state and apply the modification to it.
You can view an example &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/-/blob/5a962d4fdd0c06ac809a9689c401d63de99a09a9/src/store/rss-backends-actions.ts#L119"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you want more detail, you can check &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/-/commit/5a962d4fdd0c06ac809a9689c401d63de99a09a9"&gt;this commit&lt;/a&gt; of one of my project when I updated my actions to use immer.&lt;/p&gt;
&lt;p&gt;As noted by &lt;a class="reference external" href="https://github.com/zewa666"&gt;Vildan Softic&lt;/a&gt; (the creator of Aurelia Store), you can organize how you update the state differently to update the state correctly while keeping things clear without requiring immer.
You can see an example &lt;a class="reference external" href="https://github.com/zewa666/enforted/blob/master/src/store/actions/commands.ts#L62"&gt;here&lt;/a&gt;.
Many thanks to him for pointing that out and giving me some pointer to correctly test Aurelia Store with Immer.&lt;/p&gt;
&lt;p&gt;So while I think it's really nice to be able to use Immer, whether to use it or not highly depends on your programming style and on whether you can afford adding yet another library to your project (immer is about 15kB minified which is tiny, but it adds up with the rest).&lt;/p&gt;
</content><category term="Aurelia"></category><category term="JavaScript"></category><category term="TypeScript"></category><category term="Aurelia"></category><category term="RxJS"></category></entry><entry><title>Switching from Aurelia CLI to Webpack</title><link href="https://www.jujens.eu/posts/en/2020/Jul/04/aurelia-wepack/" rel="alternate"></link><published>2020-07-04T00:00:00+02:00</published><updated>2020-07-04T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2020-07-04:/posts/en/2020/Jul/04/aurelia-wepack/</id><summary type="html">&lt;p&gt;Recently I decided to change the build system I use on my main Aurelia project. I changed in &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2016/Apr/24/switch-to-webpack/"&gt;April 2016 from JSPM to Webpack&lt;/a&gt; (JSPM was really not awesome at the time, I can't say for now), again in &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2016/Aug/17/switch-to-aurelia-cli/"&gt;August 2016 from Webpack to the CLI&lt;/a&gt; and now I am …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Recently I decided to change the build system I use on my main Aurelia project. I changed in &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2016/Apr/24/switch-to-webpack/"&gt;April 2016 from JSPM to Webpack&lt;/a&gt; (JSPM was really not awesome at the time, I can't say for now), again in &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2016/Aug/17/switch-to-aurelia-cli/"&gt;August 2016 from Webpack to the CLI&lt;/a&gt; and now I am switching back to webpack. At the time, I moved away from webpack because of a big bundle of 1.3 MB that was always loaded. I didn't manage to split it correctly using Webpack while Aurelia CLI gave me the flexibility I needed to load less JS by default. So why did I decided to switch again?&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;To get smaller bundles: since Webpack is not used just by Aurelia it supports many optimization to reduce the size of your bundles (like tree shaking to remove unused code).&lt;/li&gt;
&lt;li&gt;To simplify assets management: Webpack can do it directly. I used to rely on &lt;a class="reference external" href="https://www.npmjs.com/package/gulp-rev"&gt;gulp-rev&lt;/a&gt; to correctly version them. But it required manual work to get it working. Now I can just rely on Webpack.&lt;/li&gt;
&lt;li&gt;Currently, Server Side Rendering with Aurelia is only supported by Webpack. This could be a big plus for my site where I have public facing pages that would gain from being correctly indexed by Google.&lt;/li&gt;
&lt;li&gt;Hot Module Reload to speed development (alone this wouldn't have been enough for a switch, but it's nice).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since my last try, Webpack and Aurelia's support for Webpack both significantly improved so I guessed this was worth a try to help me reduce my bundles (that the main reason I tried to be honest, the other ones are more bonuses).&lt;/p&gt;
&lt;p&gt;Here's how I made the switch. I:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Copied required dependencies from a new project created with the CLI (it was of course configured to use webpack, which is now the default option for a bundler). That included of course &lt;tt class="docutils literal"&gt;webpack&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;webpack-dev-server&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Changed &lt;tt class="docutils literal"&gt;.css&lt;/tt&gt; into &lt;tt class="docutils literal"&gt;.scss&lt;/tt&gt; in HTML files so styles are correctly loaded by webpack on my &lt;tt class="docutils literal"&gt;require&lt;/tt&gt; lines.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Used &lt;tt class="docutils literal"&gt;PLATFORM.moduleName&lt;/tt&gt; where necessary. This is required to bridge Aurelia with Webpack. See &lt;a class="reference external" href="https://aurelia.io/docs/build-systems/webpack/a-basic-example#introduction"&gt;the documentation&lt;/a&gt;. This mainly implied to:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Add &lt;tt class="docutils literal"&gt;PLATFORM.moduleName&lt;/tt&gt; to link moduleId to their module my route configurations.&lt;/li&gt;
&lt;li&gt;Add &lt;tt class="docutils literal"&gt;PLATFORM.moduleName&lt;/tt&gt; in plugin definition.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Updated my storage service so it uses a prefix for my keys. This was required because webpack use &lt;tt class="docutils literal"&gt;localStorage&lt;/tt&gt; to store things and I looped on all the keys which resulted in errors in my code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Updated aurelia.json to match one provided by the webpack skeleton.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Added a webpack configuration. It's based on the one from the skeleton. I had to change (to match what I do in my project):&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Where I get sources from &lt;tt class="docutils literal"&gt;src&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;app&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;The name of the static folder from &lt;tt class="docutils literal"&gt;public&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;assets&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Where I send the static files when the app is built.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Import all my images and assets directly in JS files so Webpack would correctly copy and version them. This way, in JS I can use the imported name to point to the assets and let Webpack handle the versioning. I had a bunch of gulp tasks to do this before and it was a bit cumbersome. See &lt;a class="reference external" href="https://webpack.js.org/guides/asset-management/"&gt;this page&lt;/a&gt; and &lt;a class="reference external" href="https://gitlab.com/arenaoftitans/arena-of-titans/-/commit/b06fca566e8257b780c11135e2f691e81ab629c8"&gt;this commit&lt;/a&gt; for more details on how to manage assets with webpack. To help me generate the import lines and the mapping objects I often needed, I relied on this small Python script:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="code Python literal-block"&gt;
&lt;span class="n"&gt;files_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&amp;quot;&amp;quot;COPY HERE THE RESULT OF ``ls -d PATH``&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;import_name_to_file_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;elt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;elt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="n"&gt;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="n"&gt;components&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="n"&gt;import_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="n"&gt;import_name_to_file_name&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;import_name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'import &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;import_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; from &amp;quot;../../&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;elt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'{'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;import_name_to_file_name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;'&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;,'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'}'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Added &lt;tt class="docutils literal"&gt;index.ejs&lt;/tt&gt; as a base index file for webpack and adapt it to suit my needs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Updated my jest configuration so I&amp;nbsp;could run my tests again. I even went a bit further and stopped using Aurelia CLI to use Jest CLI directly. This allowed me to have access to all the options provided by Jest more easily. To do this, aside from updating the configuration to support Webpack, I&amp;nbsp;had to make sure the environment variable &lt;tt class="docutils literal"&gt;BABEL_TARGET&lt;/tt&gt; was correctly set to &lt;tt class="docutils literal"&gt;node&lt;/tt&gt;. Without it, Babel doesn't target the proper runtime and it doesn't work. Since I already used &lt;a class="reference external" href="https://www.npmjs.com/package/nps"&gt;nps&lt;/a&gt; to launch some scripts, this wasn't an issue. I defined my tasks to run tests like this:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;crossEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;BABEL_TARGET=node jest&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;coverage&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;crossEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;BABEL_TARGET=node jest --coverage&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;crossEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;BABEL_TARGET=node jest --watch&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;See &lt;a class="reference external" href="https://gitlab.com/arenaoftitans/arena-of-titans/-/commit/359423faff3030ddc92d870652f8ec0af82ea9c0"&gt;this commit&lt;/a&gt; for more details.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Used a webpack plugin to help me generate my ServiceWorker to preload all bundles and assets: &lt;a class="reference external" href="https://www.npmjs.com/package/serviceworker-webpack-plugin"&gt;serviceworker-webpack-plugin&lt;/a&gt;. The details are &lt;a class="reference external" href="https://gitlab.com/arenaoftitans/arena-of-titans/-/commit/abe3c290505bd46607e2d3ff5eebf87ce076b439"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I had one big issue during the switch: &lt;tt class="docutils literal"&gt;compose&lt;/tt&gt;. I use this to dynamically select a view model to display in an element like this: &lt;tt class="docutils literal"&gt;&amp;lt;compose &lt;span class="pre"&gt;view-model=&amp;quot;./${type}&amp;quot;&lt;/span&gt; &lt;span class="pre"&gt;model.one-way=&amp;quot;popupModel&amp;quot;&amp;gt;&amp;lt;/compose&amp;gt;&lt;/span&gt;&lt;/tt&gt; (where &lt;tt class="docutils literal"&gt;type&lt;/tt&gt; is passed to my component and corresponds to JS and HTML files to load on the view (for instance if type is &lt;tt class="docutils literal"&gt;transition&lt;/tt&gt; Aurelia will load and inject the content of &lt;tt class="docutils literal"&gt;transition.js&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;transition.html&lt;/tt&gt;). Sadly, Webpack doesn't know what to do with this out of the box. Luckily, I found &lt;a class="reference external" href="https://discourse.aurelia.io/t/dynamically-loading-compose-viewmodels-using-webpack/173"&gt;a thread on the forum&lt;/a&gt; that gave the solution: declare all you need to use with &lt;tt class="docutils literal"&gt;PLATFORM.moduleName&lt;/tt&gt; in the component definition. See &lt;a class="reference external" href="https://gitlab.com/arenaoftitans/arena-of-titans/-/commit/6d223145cfd83bffb15939b1b574d17846a949fc"&gt;this commit&lt;/a&gt; for more details (or the thread on the forum).&lt;/p&gt;
&lt;p&gt;I'd also like to point out that the configuration generated by the CLI can optimize for two major use cases:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Usage over HTTP 1.1: you get fewer but bigger bundles.&lt;/li&gt;
&lt;li&gt;Usage over HTTP 2: you get a lot of small bundles (around 50 in my case).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since my site is only compatible with modern browser and supports HTTP 2, I decided to optimize for that. One important point to know is that by default, all bundles will be loaded by the &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt; even those you won't need yet. To prevent this (and put some pages in dedicated bundles), you can specify a chunk name to &lt;tt class="docutils literal"&gt;PLATFORM.moduleName&lt;/tt&gt; like this: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;PLATFORM.moduleName('myRouteModule',&lt;/span&gt; 'chunkName')&lt;/tt&gt;. This way, all that's required for &lt;tt class="docutils literal"&gt;myRouteModule&lt;/tt&gt; to run will only be loaded when the route becomes first active. That's a good thing to know to avoid loading to much JS from the start if your app is big. See &lt;a class="reference external" href="https://github.com/aurelia/webpack-plugin/wiki/Managing-dependencies#code-splitting"&gt;this page&lt;/a&gt; to learn more about this.&lt;/p&gt;
&lt;p&gt;Results:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Bundle sizes before webpack:&lt;ul&gt;
&lt;li&gt;Total first party JS loaded on the whole site: 1.54 MB&lt;/li&gt;
&lt;li&gt;Total first party JS on the home page: 1.02 MB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Bundle sizes with webpack:&lt;ul&gt;
&lt;li&gt;Total first party JS loaded on the whole site: 1.26 MB&lt;/li&gt;
&lt;li&gt;Total first party JS on the home page: 618 kB&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So that's a gain of about a 400 kB. Not bad.&lt;/p&gt;
&lt;p&gt;To conclude, I am satisfied with the switch: I reached my goal and can take advantages of webpack which is nice! I'd also like to point out that the transition went way smother that I expected: I got it working in a couple of hours and then had to spend some time with various issues (like assets management or the issue with &lt;tt class="docutils literal"&gt;compose&lt;/tt&gt;) and to tailor the build to my needs which are not standard. All in all, I think I made the switch in less than 30 hours. Not bad. If your needs are less complex than mine, it should be way less than this. I guess both Webpack and Aurelia matured a lot since the last time I made a build system switch. That's a very good thing!&lt;/p&gt;
</content><category term="Aurelia"></category><category term="JavaScript"></category><category term="Aurelia"></category><category term="Webpack"></category></entry><entry><title>Writing a PWA with Aurelia</title><link href="https://www.jujens.eu/posts/en/2019/Sep/01/aurelia-pwa/" rel="alternate"></link><published>2019-09-01T00:00:00+02:00</published><updated>2022-09-11T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2019-09-01:/posts/en/2019/Sep/01/aurelia-pwa/</id><summary type="html">&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#pwa-basics" id="toc-entry-1"&gt;PWA basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#the-app" id="toc-entry-2"&gt;The App&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#manifest-json" id="toc-entry-3"&gt;manifest.json&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#serviceworker" id="toc-entry-4"&gt;ServiceWorker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#conclusion" id="toc-entry-5"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#resources" id="toc-entry-6"&gt;Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#history" id="toc-entry-7"&gt;History&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;I feel like Progressive Web Application (PWA for short) are a more and more popular way to build mobile apps.
That's understandable since you can use the same technology you use to build a SPA to build one!
So …&lt;/p&gt;</summary><content type="html">&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#pwa-basics" id="toc-entry-1"&gt;PWA basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#the-app" id="toc-entry-2"&gt;The App&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#manifest-json" id="toc-entry-3"&gt;manifest.json&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#serviceworker" id="toc-entry-4"&gt;ServiceWorker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#conclusion" id="toc-entry-5"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#resources" id="toc-entry-6"&gt;Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#history" id="toc-entry-7"&gt;History&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;I feel like Progressive Web Application (PWA for short) are a more and more popular way to build mobile apps.
That's understandable since you can use the same technology you use to build a SPA to build one!
So you stay in known grounds, can use your usual web browser to develop and test the app.
And, unlike hybrid apps, you also enhance your standard SPA!
In this article, I'll explain how I build a PWA named &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss"&gt;aurss&lt;/a&gt; with the &lt;a class="reference external" href="https://aurelia.io/"&gt;Aurelia framework&lt;/a&gt;.
I'll start by explaining what PWAs are and some specific tech you must use to make them work. Then I'll explain how to build one with Aurelia.&lt;/p&gt;
&lt;div class="section" id="pwa-basics"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;PWA basics&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Simply put, PWAs are &amp;quot;installed&amp;quot; web pages: when you browse on a website that is a PWA, you will be prompted to install it.
If you accept, you will see the icon of the APP on your home screen (like any other app) and if you press this icon, the PWA will launch in full screen mode just like a native app.
But you didn't go to the app store to install it.&lt;/p&gt;
&lt;p&gt;As a developer, building a PWA means:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Your users don't have to install it: they can use the app directly on the website without installing anything and you'll still be able to provide them advanced features like caching.&lt;/li&gt;
&lt;li&gt;You will have more control on when you can update it: no need to submit it to the app store and wait for validation. Just push files to you server. The update will occur the next time your users reopen the app.&lt;/li&gt;
&lt;li&gt;You are not bound by the rules of any app stores.&lt;/li&gt;
&lt;li&gt;The app will work on any device with a recent browser.
Chrome has the best support for PWAs right now, Firefox is quite good but Safari support can be an issue (even if it's getting better, see &lt;a class="reference external" href="https://medium.com/&amp;#64;firt/whats-new-on-ios-12-2-for-progressive-web-apps-75c348f8e945"&gt;this article for instance&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;The app can be progressively enhanced (eg by adding better offline support or better caching).&lt;/li&gt;
&lt;li&gt;You show notifications the user with the &lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/API/notification"&gt;Notification API&lt;/a&gt; and  the &lt;a class="reference external" href="https://developer.mozilla.org/fr/docs/Web/API/Push_API"&gt;Push API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;You can access &lt;em&gt;some&lt;/em&gt; native features (like the camera or the microphone).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are the basic components needed to make a PWA:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;manifest.json&lt;/tt&gt;: it's a JSON file describing how the app must be displayed and installed. You'll use it to specify the name, icon, orientation and some display properties of your app. See &lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/Manifest"&gt;here&lt;/a&gt; for more details. &lt;em&gt;It is required to make your app a PWA.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;ServiceWorker&lt;/tt&gt; (SW for short): it behaves like a proxy server between your app and the network. It will keep working even when the tab is closed. So it will allow you to:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Have caching and offline support.&lt;/li&gt;
&lt;li&gt;Sync things in the background.&lt;/li&gt;
&lt;li&gt;Subscribe to notifications of the Push API and display them to users.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please note that:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;It only works over HTTPS (except for localhost).&lt;/li&gt;
&lt;li&gt;It cannot access the DOM.&lt;/li&gt;
&lt;li&gt;It may apply to all or only part of the pages.&lt;/li&gt;
&lt;li&gt;It must be registered to have an effect.&lt;/li&gt;
&lt;li&gt;It will only get updated if its content changed and after the tab is closed and reopen (unless you use the dev tools and force it to reload on change).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See &lt;a class="reference external" href="https://developer.mozilla.org/fr/docs/Web/API/Service_Worker_API"&gt;here&lt;/a&gt; for more. &lt;em&gt;It is required to make your app a PWA.&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;indexDB&lt;/tt&gt;: where you put data to exchange with the SW (SW cannot access &lt;tt class="docutils literal"&gt;localStorage&lt;/tt&gt;). See &lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API"&gt;here&lt;/a&gt; for more information.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Many other APIs I'll just list here so you know what you can do:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/API/notification"&gt;Notification API&lt;/a&gt; to display notification to the user.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://developer.mozilla.org/fr/docs/Web/API/Push_API"&gt;Push API&lt;/a&gt; to receive messages from a server.&lt;/li&gt;
&lt;li&gt;Many more: geolocation, camera access, …&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="the-app"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-2"&gt;The App&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that we've seen what the basic components are, let's see how to do this with Aurelia.&lt;/p&gt;
&lt;p&gt;In a nutshell, you want to build a normal app while paying attention to some details, relating with the SW: it will rely on &lt;tt class="docutils literal"&gt;indexDB&lt;/tt&gt; for storage, on the fetch API for network (luckily that's what the Aurelia HTTP client is using), all its APIs are promise based and &lt;em&gt;it will be reinstalled only when it changes&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I assume you are familiar with Aurelia so I won't explain Aurelia specific things (since they don't exist, I just build a standard app using the framework) and focus on PWA stuff. Anything I explain here is applicable with a basic app created by the CLI (if possible use webpack since this article will be easier to follow if you do).&lt;/p&gt;
&lt;p&gt;So to make your app a PWA, you will have to:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Add the &lt;tt class="docutils literal"&gt;manifest.json&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Add the service worker.&lt;/li&gt;
&lt;li&gt;Check with &lt;a class="reference external" href="https://developers.google.com/web/tools/lighthouse/"&gt;lighthouse&lt;/a&gt; that the app is installable.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can then add other capabilities if needed.&lt;/p&gt;
&lt;div class="section" id="manifest-json"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-3"&gt;manifest.json&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once you've build the app, you can add the &lt;tt class="docutils literal"&gt;manifest.json&lt;/tt&gt; file. To do so add in your &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;index.ejs&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;manifest&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/manifest.json&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Then in the &lt;tt class="docutils literal"&gt;static/&lt;/tt&gt; folder (if you are not using webpack as configured by &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-cli&lt;/span&gt;&lt;/tt&gt;, you may need to adapt this, the goal is to have this file in the root of the build files next to the &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt; file), you can create the &lt;tt class="docutils literal"&gt;manifest.json&lt;/tt&gt; file. Below is an example, you can adapt it to your needs. It also has more properties, see &lt;a class="reference external" href="https://developer.mozilla.org/en-US/docs/Web/Manifest"&gt;the documentation&lt;/a&gt; to view all its possibilities.&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurss – RSS reader&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;short_name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurss&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;start_url&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scope&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;display&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;standalone&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;background_color&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#FFF&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;theme_color&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;#493174&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;description&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;RSS reader for TTRSS&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;dir&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ltr&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;lang&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;en-US&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;orientation&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;portrait-primary&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;icons&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;src&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/icons/aurss.96x96.png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;image/png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sizes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;96x96&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;src&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/icons/aurss.512x512.png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;image/png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;sizes&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;512x512&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="serviceworker"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-4"&gt;ServiceWorker&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Then, include the service worker:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Create a file named &lt;tt class="docutils literal"&gt;sw.js&lt;/tt&gt; in your &lt;tt class="docutils literal"&gt;src&lt;/tt&gt; folder. Leave it empty for now.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Register the worker in the &lt;tt class="docutils literal"&gt;app.js&lt;/tt&gt; with:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'serviceWorker'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Service worker is registered'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;registrationError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s1"&gt;'Service worker failed to register'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;registrationError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Service worker is not available in this browser.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Make sure it will be copied when you build the application next to the &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt;. For instance, with webpack, you can use &lt;a class="reference external" href="https://www.npmjs.com/package/serviceworker-webpack-plugin"&gt;this plugin&lt;/a&gt; (which will also be handy later for caching so I recommend that you use it or something similar).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Even you are using TypeScript, I suggest you rely on plain JavaScript for the SW file. It will be much easier to write and you won't have to worry about transpilation.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonition tip"&gt;
&lt;p class="first admonition-title"&gt;Tip&lt;/p&gt;
&lt;p class="last"&gt;If you are using Chrome, the &lt;em&gt;Application&lt;/em&gt; tab of the dev tools is really useful. It will show you information about the manifest file, the service worker and the used storage.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonition tip"&gt;
&lt;p class="first admonition-title"&gt;Tip&lt;/p&gt;
&lt;p class="last"&gt;ES6 imports won't work with SW, you must use something like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;importScripts('/src/js/idb.js');&lt;/span&gt;&lt;/tt&gt; to import things.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Now the SW is bootstrapped, we can start to use it to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Cache the shell of the app (ie the files it needs to work) and provide offline support. This is done once the SW is installed, so when the &lt;tt class="docutils literal"&gt;installed&lt;/tt&gt; event has been fired (&lt;tt class="docutils literal"&gt;self&lt;/tt&gt; represent the SW instance):&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="c1"&gt;// Name of our static cache.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;staticCachePrefix&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'static'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// Update this each time you app shell changes.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// This way, the SW will have changed and get reinstalled&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// allowing for the new files to be cached.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;VERSION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'1.0.0'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;staticCacheName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;staticCachePrefix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;VERSION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'install'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Installing SW version:'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;VERSION&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c1"&gt;// waitUntil is there to make the sure SW is neither paused nor stopped&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c1"&gt;// while we expect it to do some work until the promise we passed to it completes.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c1"&gt;// See: https://stackoverflow.com/a/37906330&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;staticCacheName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Caching app shell'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="c1"&gt;// serviceWorkerOption comes from serviceworker-webpack-plugin.&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="c1"&gt;// Adapt if needed.&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serviceWorkerOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can then use in your &lt;tt class="docutils literal"&gt;app.js&lt;/tt&gt; file, in the &lt;tt class="docutils literal"&gt;constructor&lt;/tt&gt;, something like the code below to display notification to the user about online/offline status:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onOffline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOffline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onOnline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOnline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onLine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOffline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isOnline&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'offline'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onOffline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'online'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onOnline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Catch network requests, for instance to implement dynamic caching.&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fetch'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c1"&gt;// Let the browser do its default thing&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c1"&gt;// for non-GET requests.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;When offline, you can store data in &lt;tt class="docutils literal"&gt;indexDB&lt;/tt&gt; to send it as soon as the browser gets online again. To do this, you must listen to the &lt;tt class="docutils literal"&gt;sync&lt;/tt&gt; event in the SW. You can take inspiration from &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/blob/master/src/sw.js#L50"&gt;this code&lt;/a&gt; for the SW part and from &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/blob/master/src/utils/sync.ts"&gt;these utility functions&lt;/a&gt; (see a usage &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/blob/master/src/services/backends/ttrss.ts#L275"&gt;here&lt;/a&gt;) to achieve this.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;There are many caching strategies: with network fallback, cache only, network only, network with cache fallback, cache then network. I won't detail them here. Search on the internet if needed.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Don't forget to clean the cache from time to time. This can be done with:&lt;/p&gt;
&lt;pre class="code javascript last literal-block"&gt;
&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'activate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cleaning old cache shell'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;keylist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;keylist&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;staticCacheName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;staticCachePrefix&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="ow"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Finally, you should test you app with lighthouse to check it actually is a PWA.
The tool will also check for performance which is a good thing.
In the Chrome dev tools, go to the &lt;em&gt;Audit&lt;/em&gt; tab, verify that &lt;em&gt;Progressive Web App&lt;/em&gt; is checked under the audit section and run the audit.
The report should tell you that you have indeed created a PWA (and if not, what you are missing).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-5"&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To sum up:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;PWAs are not tied to any framework. You can make a PWA without one or add PWA capabilities to a SPA written with any framework.&lt;/li&gt;
&lt;li&gt;They are quite fast to develop and allow for good code re-use because you are not making an app distinct from you main web site.&lt;/li&gt;
&lt;li&gt;You don't have to learn many new technologies, most of the things you will do will be standard we development.&lt;/li&gt;
&lt;li&gt;You can progressively enhance an app with PWA capabilities.&lt;/li&gt;
&lt;li&gt;Pay attention to what they support and what they don't before starting and to iOS support.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="resources"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-6"&gt;Resources&lt;/a&gt;&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;My test application: &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss"&gt;aurss&lt;/a&gt; It's MIT licensed so you can re-use it. Don't hesitate to ask me questions in the comment or in Gitlab.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://www.udemy.com/course/progressive-web-app-pwa-the-complete-guide/"&gt;A course I took (I highly recommand it)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="history"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-7"&gt;History&lt;/a&gt;&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;2022-09-11: update link to &lt;tt class="docutils literal"&gt;manifest.json&lt;/tt&gt; in MDN to point to the documentation about PWA and not about Web Extensions. Thank &lt;a class="reference external" href="#isso-305"&gt;Colin O&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="Aurelia"></category><category term="JavaScript"></category><category term="TypeScript"></category><category term="Aurelia"></category><category term="Mobile"></category><category term="PWA"></category></entry><entry><title>Some thoughts on Aurelia Store</title><link href="https://www.jujens.eu/posts/en/2019/Aug/24/aurelia-store/" rel="alternate"></link><published>2019-08-24T00:00:00+02:00</published><updated>2019-08-24T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2019-08-24:/posts/en/2019/Aug/24/aurelia-store/</id><summary type="html">&lt;p&gt;On my spare time, I am developing a &lt;a class="reference external" href="https://gitlab.com/arenaoftitans/arena-of-titans"&gt;board game&lt;/a&gt; that uses &lt;a class="reference external" href="https://aurelia.io"&gt;the Aurelia framework&lt;/a&gt;.
I currently manage the state in a service without any dedicated state management solutions (like RxJS, Aurelia store, Redux, …).
And I feel I am reaching to the limit of what I can do with it …&lt;/p&gt;</summary><content type="html">&lt;p&gt;On my spare time, I am developing a &lt;a class="reference external" href="https://gitlab.com/arenaoftitans/arena-of-titans"&gt;board game&lt;/a&gt; that uses &lt;a class="reference external" href="https://aurelia.io"&gt;the Aurelia framework&lt;/a&gt;.
I currently manage the state in a service without any dedicated state management solutions (like RxJS, Aurelia store, Redux, …).
And I feel I am reaching to the limit of what I can do with it and feel the need for a dedicated solution. So I decided to give &lt;a class="reference external" href="https://github.com/aurelia/store/"&gt;aurelia-store&lt;/a&gt; the official plugin for state management for the framework.
I did so in at test project: &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss"&gt;aurss&lt;/a&gt; a small app to list rss feeds.&lt;/p&gt;
&lt;p&gt;First, let me explain a bit why we would need a store: in Aurelia, you can use services (ie instances of a object) that are injected into your components.
You can use them for actions (eg interaction with an external API) and to store data.
If you app is big, you will end up with many services. You'll have to keep them all in sync (maybe with observers or the event aggregator) and make sure your component are correctly updated.&lt;/p&gt;
&lt;p&gt;It turns out this approach can be quite tricky and that's why solutions like Redux (from the React world) or VueX for Vue.js and of course Aurelia store have emerged: to help you manage the state in a single place so you don't have to do the hard work of keeping everything in sync.
Instead of having multiple services, you have one store, you rely on functions to change its state and the store &amp;quot;notifies&amp;quot; your components that something changed and that they must be updated.
It also comes with some nice features like easy persistence and recovery from local storage and time travel.&lt;/p&gt;
&lt;p&gt;Aurelia store more specifically is a plugin for Aurelia to help you use this pattern with the Aurelia framework.
It's based on &lt;a class="reference external" href="http://reactivex.io/rxjs/"&gt;RxJS&lt;/a&gt; a popular library for event programming.
The idea of RxJS is register actions to do when a given event occurs as specified by the observer pattern, ie do something like this:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fromEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'click'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'clicked!'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sendEventToAnalytics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I'll let you see the doc of RxJS to learn more about that.&lt;/p&gt;
&lt;p&gt;What's very interesting about Aurelia store is that while based on RxJS (and its full power), it remains easy to use at first: you don't need to know RxJS to start and don't need much knowledge to do even quite advanced things.
Please read &lt;a class="reference external" href="https://aurelia.io/docs/plugins/store#introduction"&gt;the introduction to Aurelia store&lt;/a&gt; to have a more detailed explanation about why the plugin exists and its relationship with RxJS.&lt;/p&gt;
&lt;p&gt;Let's see briefly how to use it (I assume you are already familiar with Aurelia, if not, the official documentation will give you more details on how to integrate it in your app):&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;In your &lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt; file, you must enable the plugin and define an initial state:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-store'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;players&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Inject the &lt;tt class="docutils literal"&gt;Store&lt;/tt&gt; in your components:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-framework'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-store'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;&amp;#64;&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;…&lt;/span&gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Listen to events with:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="c1"&gt;// `this.state` will receive a new value each time the state is updated.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// This way, the component will re-render.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Dispatch actions to update the state:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addPlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c1"&gt;// Don't mutate the state directly, create a new one. See the doc for why.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{..&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;players&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;players&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;players&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registerAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;addPlayer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addPlayer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Player &amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;10&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="admonition tip"&gt;
&lt;p class="first admonition-title"&gt;Tip&lt;/p&gt;
&lt;p class="last"&gt;Don't forget to dispose of subscription with &lt;tt class="docutils literal"&gt;this.subscription.unsubscribe&lt;/tt&gt;. See &lt;a class="reference external" href="https://aurelia.io/docs/plugins/store#subscribing-to-the-stream-of-states"&gt;the doc&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I must say the experience of using Aurelia store is very pleasant: the plugin is easy to integrate, it works well, it's not hard to understand, it seems to play nice with RxJS if you need some advance features (for instance to register to updates to part of the state or to get only the first change).&lt;/p&gt;
&lt;p&gt;If I compare this to Redux which is the other state management solution I used, I think that using directly functions when we dispatch actions is a very good idea:
instead of having actions and reducers like in Redux, you only have one concept to achieve the same result.
It's also way easier to get started and is easier to understand (each time I have to touch a Redux code base, I have to re-understand how it works and what the link between actions and reducers is).&lt;/p&gt;
&lt;p&gt;The fact that the plugin hides the complexity of RxJS is also a really good thing. By comparison with Angular (from the tutorial I made at least) where RxJS is fully exposed, it really simplifies the development and startup.&lt;/p&gt;
&lt;p&gt;So I am really pleased with the plugin, what I can do with it and the overall developer experience it provides. There are some small gotchas though:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Don't forget to register your actions. You may forget at first. But the store will provide an helpful error message if you forget so it's not the end of the world.&lt;/li&gt;
&lt;li&gt;Pay attention to async actions: unlike other solutions, the Aurelia store allows you to do something like this: &lt;tt class="docutils literal"&gt;await this.store.dispatch(costlyApiCall)&lt;/tt&gt;.
If you do this, your app will freeze until the API call is done which is not what you want (and may not be noticeable locally if your API is on your machine).
So for expensive async operations, split the actions in two: one to do the call and one to update the store when the call is done.
To view an example of this, look at &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/blob/master/src/store/rss-backends-actions.ts#L30"&gt;fetchArticles&lt;/a&gt; and &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/blob/master/src/store/rss-backends-actions.ts#L40"&gt;receivedArticles&lt;/a&gt; from my test app.
But if you know your async actions will resolve fast (querying &lt;a class="reference external" href="https://developer.mozilla.org/fr/docs/Web/API/API_IndexedDB"&gt;IndexDB&lt;/a&gt; for instance), this shouldn't be an issue.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://github.com/immerjs/immer"&gt;Immer.js&lt;/a&gt; (useful to ease the writing of state mutation) doesn't work with the plugin out of the box yet (see &lt;a class="reference external" href="https://discourse.aurelia.io/t/aurelia-store-immerjs-and-objects/2601"&gt;this discussion&lt;/a&gt; which also gives a possible solution and &lt;a class="reference external" href="https://github.com/immerjs/immer/issues/392"&gt;this issue&lt;/a&gt;).
Maybe alternative solutions work (like &lt;a class="reference external" href="https://github.com/immutable-js/immutable-js"&gt;immutable.js&lt;/a&gt;) but I haven't tried them.
Update of 2020-08-09: it is now possible, see &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2020/Aug/09/aurelia-store-immerjs/"&gt;this update&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Further readings:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://aurelia.io/docs/plugins/store#getting-started"&gt;The documentation of the plugin&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;For a small project using the store, you can view my test project &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss"&gt;aurss&lt;/a&gt;.
It's a small but usable RSS feed reader which I may improve in the future.
It expects to connect to a &lt;a class="reference external" href="https://tt-rss.org/"&gt;TTRSS&lt;/a&gt; backend.
You can use it  &lt;a class="reference external" href="https://aurss.jujens.eu/#/login"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Several articles by Dwayne Charrington an Aurelia Team member:&lt;ul&gt;
&lt;li&gt;&lt;a class="reference external" href="https://ilikekillnerds.com/2019/08/when-to-use-state-management-in-front-end-applications/"&gt;When To Use State Management In Front-end Applications?&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://ilikekillnerds.com/2019/07/working-with-an-api-in-aurelia-store/"&gt;Working With An API In Aurelia Store&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;His book &lt;a class="reference external" href="https://leanpub.com/mastering-aurelia-store"&gt;Mastering Aurelia Store&lt;/a&gt; (still in progress when I write this).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content><category term="Aurelia"></category><category term="JavaScript"></category><category term="TypeScript"></category><category term="Aurelia"></category><category term="RxJS"></category></entry><entry><title>Issues while writing tests for a component using i18n</title><link href="https://www.jujens.eu/posts/en/2019/Aug/04/i18n-tests-issues/" rel="alternate"></link><published>2019-08-04T00:00:00+02:00</published><updated>2019-08-04T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2019-08-04:/posts/en/2019/Aug/04/i18n-tests-issues/</id><summary type="html">&lt;p&gt;Some time ago, I wanted to add some tests (behavior and render) on an Aurelia component that uses the &lt;a class="reference external" href="https://github.com/aurelia/i18n"&gt;i18n plugin&lt;/a&gt; and more precisely the &lt;tt class="docutils literal"&gt;df&lt;/tt&gt; attribute in the view to display a localized date.&lt;/p&gt;
&lt;p&gt;Since it was a while back (I wanted to write it earlier but couldn't), my …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Some time ago, I wanted to add some tests (behavior and render) on an Aurelia component that uses the &lt;a class="reference external" href="https://github.com/aurelia/i18n"&gt;i18n plugin&lt;/a&gt; and more precisely the &lt;tt class="docutils literal"&gt;df&lt;/tt&gt; attribute in the view to display a localized date.&lt;/p&gt;
&lt;p&gt;Since it was a while back (I wanted to write it earlier but couldn't), my memory may not completely accurate. I hope it can help you nonetheless. If you have questions, please post a comment.&lt;/p&gt;
&lt;p&gt;My component is pretty basic: it takes some bindable as inputs, define a time format to format the date correctly, has 3 methods to add custom behavior and that's it. You can view the model &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/blob/4ae68e3d1c55e441df72804620df7fa06d1e35c7/src/resources/elements/aurss-article.ts"&gt;here&lt;/a&gt; and the view &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss/blob/4ae68e3d1c55e441df72804620df7fa06d1e35c7/src/resources/elements/aurss-article.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Also note I am using TypeScript, Webpack and Jest. If you use different tooling, you may need to adapt the example a bit.&lt;/p&gt;
&lt;p&gt;In my test, I setup my component like this:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;StageComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PLATFORM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'../../src/resources/elements/aurss-article'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`
    &amp;lt;aurss-article
        value.bind=&amp;quot;article&amp;quot;
        mark-article-as-read.bind=&amp;quot;markArticleAsRead&amp;quot;
        mark-article-as-unread.bind=&amp;quot;markArticleAsUnread&amp;quot;
        open-article.bind=&amp;quot;openArticle&amp;quot;
    &amp;gt;&amp;lt;/aurss-article&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;boundTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// bootstrap comes from aurelia-bootstrapper&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I got this error when running the tests: &lt;tt class="docutils literal"&gt;Error: No ValueConverter named &amp;quot;df&amp;quot; was found!&lt;/tt&gt;. I then tried to register the component with:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;StageComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withResources&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;PLATFORM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'../../src/resources/elements/aurss-article'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;PLATFORM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-i18n'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;But all I got was: &lt;tt class="docutils literal"&gt;Error: Error invoking RelativeTime. Check the inner error for details.&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;After reading tests in the plugin repo and &lt;a class="reference external" href="https://kabaehr.de/blog/aurelia-advanced-i18n/"&gt;this article&lt;/a&gt; (which solves a similar problem but the proposed solution didn't work for me), here is how I nailed it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;I created an &lt;tt class="docutils literal"&gt;test/helpers.ts&lt;/tt&gt; file with the code below. Its purpose is to bootstrap the i18n plugin (it resembles what you do in &lt;tt class="docutils literal"&gt;main.ts&lt;/tt&gt;):&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Aurelia&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;PLATFORM&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-framework'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-i18n'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;prepareI18nComponent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Aurelia&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;standardConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PLATFORM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-i18n'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aliases&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'i18n'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;

                &lt;/span&gt;&lt;span class="c1"&gt;// register backend plugin&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Backend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nx"&gt;en&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="nx"&gt;translation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;skipTranslationOnMissingKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;

                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aliases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nx"&gt;loadPath&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./locales/{{lng}}/{{ns}}.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;defaultNS&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'translation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;fallbackLng&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;interpolation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{{'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'}}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;In the &lt;tt class="docutils literal"&gt;beforeEach&lt;/tt&gt; function, I now setup the component like this:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;viewModel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;createArticle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;markArticleAsRead&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;markArticleAsUnread&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;openArticle&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;StageComponent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;withResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PLATFORM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'../../src/resources/elements/aurss-article'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`
        &amp;lt;aurss-article
            value.bind=&amp;quot;article&amp;quot;
            mark-article-as-read.bind=&amp;quot;markArticleAsRead&amp;quot;
            mark-article-as-unread.bind=&amp;quot;markArticleAsUnread&amp;quot;
            open-article.bind=&amp;quot;openArticle&amp;quot;
        &amp;gt;&amp;lt;/aurss-article&amp;gt;
        `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;boundTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nx"&gt;prepareI18nComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Bootstrap i18n&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;I also configured jest to load a file name &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;test/jest-pretest.ts&lt;/span&gt;&lt;/tt&gt; before it executes the tests suite by adding, under the &lt;tt class="docutils literal"&gt;jest&lt;/tt&gt; section of my &lt;tt class="docutils literal"&gt;package.json&lt;/tt&gt; file:&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="nt"&gt;&amp;quot;setupFiles&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;rootDir&amp;gt;/test/jest-pretest.ts&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;jest-localstorage-mock&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This file contains:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-loader-nodejs'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;globalize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-pal-nodejs'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-polyfills'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntlPolyfill&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'intl'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nx"&gt;Options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relativeToDir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'unit'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;globalize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;navigator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NumberFormat&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntlPolyfill&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntlPolyfill&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nb"&gt;Intl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nb"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NumberFormat&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntlPolyfill&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nb"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;IntlPolyfill&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;It is meant to be sure the &lt;tt class="docutils literal"&gt;Intl&lt;/tt&gt; API used by the plugin is correctly defined during the tests.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's it! You can also take a look at the full project code in gitlab if you need more details: &lt;a class="reference external" href="https://gitlab.com/Jenselme/aurss"&gt;https://gitlab.com/Jenselme/aurss&lt;/a&gt;&lt;/p&gt;
</content><category term="Aurelia"></category><category term="JavaScript"></category><category term="TypeScript"></category><category term="Aurelia"></category><category term="i18N"></category></entry><entry><title>Extract translations from your Aurelia app</title><link href="https://www.jujens.eu/posts/en/2019/Aug/03/extract-translations/" rel="alternate"></link><published>2019-08-03T00:00:00+02:00</published><updated>2019-08-03T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2019-08-03:/posts/en/2019/Aug/03/extract-translations/</id><summary type="html">&lt;p&gt;If you use Aurelia with the &lt;a class="reference external" href="https://github.com/aurelia/i18n"&gt;i18n plugin&lt;/a&gt;, you will have to maintain JSON files that will map a translation key to an actual translation. Manually editing the file to add/remove keys can be tedious.&lt;/p&gt;
&lt;p&gt;Luckily, keys can be extracted automatically thanks to &lt;a class="reference external" href="https://github.com/i18next/i18next-scanner"&gt;i18next-scanner&lt;/a&gt;.  It can extract translation keys …&lt;/p&gt;</summary><content type="html">&lt;p&gt;If you use Aurelia with the &lt;a class="reference external" href="https://github.com/aurelia/i18n"&gt;i18n plugin&lt;/a&gt;, you will have to maintain JSON files that will map a translation key to an actual translation. Manually editing the file to add/remove keys can be tedious.&lt;/p&gt;
&lt;p&gt;Luckily, keys can be extracted automatically thanks to &lt;a class="reference external" href="https://github.com/i18next/i18next-scanner"&gt;i18next-scanner&lt;/a&gt;.  It can extract translation keys  from JavaScript, TypeScript and HTML files.&lt;/p&gt;
&lt;p&gt;To do this:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Install &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;i18next-scanner&lt;/span&gt;&lt;/tt&gt; with &lt;tt class="docutils literal"&gt;npm install &lt;span class="pre"&gt;--save-dev&lt;/span&gt; &lt;span class="pre"&gt;i18next-scanner&lt;/span&gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;yarn add &lt;span class="pre"&gt;-D&lt;/span&gt; &lt;span class="pre"&gt;i18next-scanner&lt;/span&gt;&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Add the code below in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;i18next-scanner.config.js&lt;/span&gt;&lt;/tt&gt; file at the root of your project.&lt;/li&gt;
&lt;li&gt;Run &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;./node_modules/.bin/i18next-scanner&lt;/span&gt; &lt;span class="pre"&gt;src/**/*.{js,ts,html}&lt;/span&gt;&lt;/tt&gt; to extract the translations. They will be under &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;src/locales/{{lng}}/translation.json&lt;/span&gt;&lt;/tt&gt;. Keys to be translated will have the value &lt;tt class="docutils literal"&gt;__STRING_NOT_TRANSLATED__&lt;/tt&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The extraction supports the following patterns:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;In code files: it will extract keys directly from &lt;tt class="docutils literal"&gt;i18next&lt;/tt&gt; (usage like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;i18next.t('my-key')&lt;/span&gt;&lt;/tt&gt;) and from the injected &lt;tt class="docutils literal"&gt;I18N&lt;/tt&gt; service (ie if you use it like this: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;this.i18n.tr('my-key')&lt;/span&gt;&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;In HMTL files:&lt;ul&gt;
&lt;li&gt;With the following attributes: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;data-i18n&lt;/span&gt;&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;data-t&lt;/span&gt;&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;t&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;i18n&lt;/tt&gt; (usage like this &lt;tt class="docutils literal"&gt;&amp;lt;p &lt;span class="pre"&gt;t='my-key'&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;/tt&gt;).&lt;/li&gt;
&lt;li&gt;With the following behaviors: &lt;tt class="docutils literal"&gt;t&lt;/tt&gt; (usage &lt;tt class="docutils literal"&gt;${ 'myKey' | t }&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;${ 'myKey' &amp;amp; t }&lt;/tt&gt; – the whitespace is not important).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If this doesn't exactly work for your use cases, please adapt the code below and ask a question in the comments if needed.&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fs'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;typescript&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;typescript&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s1"&gt;'src/**/*.{ts}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;func&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'i18next.t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'i18n.t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'this.i18n.tr'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'.js'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;lngs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'fr'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s1"&gt;'translation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;defaultLng&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;defaultNs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'translation'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'__STRING_NOT_TRANSLATED__'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;loadPath&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'src/locales/{{lng}}/{{ns}}.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;savePath&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'src/locales/{{lng}}/{{ns}}.json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;jsonIndent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;lineEnding&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'\n'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;29 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;nsSeparator&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// namespace separator&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;keySeparator&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// key separator&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;31 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;interpolation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{{'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;33 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nx"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'}}'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;35 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;removeUnusedKeys&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;36 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;37 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;38 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;customTransform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;39 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;40 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;41 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;42 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'.ts'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;43 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;outputText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;typescript&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transpileModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;44 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;compilerOptions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;45 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'es2018'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;46 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;47 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;48 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;49 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;50 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseFuncFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'i18next.t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'i18next.tr'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'this.i18n.tr'&lt;/span&gt;&lt;span class="p"&gt;]});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;51 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'.html'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;52 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;53 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parseAttrFromString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'data-i18n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'data-t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'i18n'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;54 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;55 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="c1"&gt;// We extra behaviours `${ 'myKey' | t }` and `${ 'myKey' &amp;amp; t }` from the file.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;56 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;extractBehaviours&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sr"&gt;/\${ *'([a-zA-Z0-9]+)' *[&amp;amp;|] *t *}/g&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;57 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strContent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;58 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;59 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;60 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;extractBehaviours&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;61 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;62 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;63 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;64 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;65 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;66 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;67 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;68 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;69 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;70 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can also view the code in gitlab: &lt;a class="reference external" href="https://gitlab.com/snippets/1881948"&gt;https://gitlab.com/snippets/1881948&lt;/a&gt;&lt;/p&gt;
&lt;p class="warning"&gt;
Because of the &lt;code&gt;removeUnusedKeys: true&lt;/code&gt; options, keys that are not found will be removed from the JSON files. If you don't want this behavior, switch this option to &lt;code&gt;false&lt;/code&gt;.
&lt;/p&gt;&lt;p class="note"&gt;
It is compatible with Javascript and Typescript. If you don't use Typescript, remove the line &lt;code&gt;const typescript = require("typescript");&lt;/code&gt; to avoid issues at import.
&lt;/p&gt;</content><category term="Aurelia"></category><category term="JavaScript"></category><category term="TypeScript"></category><category term="Aurelia"></category><category term="i18N"></category></entry><entry><title>Integrate Rollbar with Aurelia</title><link href="https://www.jujens.eu/posts/en/2017/Aug/19/rollbar-aurelia/" rel="alternate"></link><published>2017-08-19T00:00:00+02:00</published><updated>2017-08-19T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2017-08-19:/posts/en/2017/Aug/19/rollbar-aurelia/</id><summary type="html">&lt;p&gt;I recently integrated &lt;a class="reference external" href="https://rollbar.com/"&gt;Rollbar&lt;/a&gt; (a service to &lt;cite&gt;Detect, diagnose and debug errors with ease&lt;/cite&gt;) into an Aurelia application. I explain here how I did it. I start by explaining how logging works with Aurelia and then how I integrated Rollbar into this.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#logging-with-aurelia" id="toc-entry-1"&gt;Logging with Aurelia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#integration-with-rollbar" id="toc-entry-2"&gt;Integration with Rollbar&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#using-rollbar-within-aurelia" id="toc-entry-3"&gt;Using …&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;I recently integrated &lt;a class="reference external" href="https://rollbar.com/"&gt;Rollbar&lt;/a&gt; (a service to &lt;cite&gt;Detect, diagnose and debug errors with ease&lt;/cite&gt;) into an Aurelia application. I explain here how I did it. I start by explaining how logging works with Aurelia and then how I integrated Rollbar into this.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#logging-with-aurelia" id="toc-entry-1"&gt;Logging with Aurelia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#integration-with-rollbar" id="toc-entry-2"&gt;Integration with Rollbar&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#using-rollbar-within-aurelia" id="toc-entry-3"&gt;Using Rollbar within Aurelia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#loading-rollbar" id="toc-entry-4"&gt;Loading Rollbar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="logging-with-aurelia"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;Logging with Aurelia&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The main module to log things with Aurelia is simply named &lt;a class="reference external" href="https://github.com/aurelia/logging"&gt;aurelia-logging&lt;/a&gt;. It is responsible for:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Setting the log level you want with &lt;tt class="docutils literal"&gt;setLevel&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Create/get loggers with &lt;tt class="docutils literal"&gt;getLogger&lt;/tt&gt;. When you get a logger with this function, logs messages will be dispatched to all registered log appenders (more on that in a minute) while respecting the level of log you set.&lt;/li&gt;
&lt;li&gt;Defining the &lt;tt class="docutils literal"&gt;Appender&lt;/tt&gt; interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-logging&lt;/span&gt;&lt;/tt&gt; is a generic module to deal with logging but doesn't log anything by itself. It is defined this way so it can work in various environment (eg the browser and Node) and send logs where you want (let it be the browser console or Rollbar).&lt;/p&gt;
&lt;p&gt;To actually do some logging, we need to add at least one Appender with &lt;tt class="docutils literal"&gt;addAppender&lt;/tt&gt;. For instance, you can use the &lt;a class="reference external" href="https://github.com/aurelia/logging-console"&gt;aurelia-logging-console&lt;/a&gt; module to append log to the browser console. Or you can use &lt;a class="reference external" href="https://github.com/Jenselme/au-rollbar"&gt;au-rollbar&lt;/a&gt; to log the messages into Rollbar.&lt;/p&gt;
&lt;p&gt;Let's illustrate this with an example. Let's say I configure the log level to log only warnings and above with &lt;tt class="docutils literal"&gt;Logger.setLevel(Logger.logLevel.warn);&lt;/tt&gt; and that you added the &lt;tt class="docutils literal"&gt;ConsoleAppender&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;RollbarAppender&lt;/tt&gt; to the list of appenders with &lt;tt class="docutils literal"&gt;Logger.addAppender(new &lt;span class="pre"&gt;ConsoleAppender())&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;Logger.addAppender(new &lt;span class="pre"&gt;RollbarAppender());&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;You then create your logger with &lt;tt class="docutils literal"&gt;let myLogger = &lt;span class="pre"&gt;Logger.getLogger('my-module')&lt;/span&gt;&lt;/tt&gt;. You then use the logger, eg:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;myLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'A warning'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;myLogger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'A debug message'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;A warning&lt;/tt&gt; message will be dispatched to the console and rollbar while &lt;tt class="docutils literal"&gt;A debug message&lt;/tt&gt; won't be logged anywhere.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="integration-with-rollbar"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-2"&gt;Integration with Rollbar&lt;/a&gt;&lt;/h2&gt;
&lt;div class="section" id="using-rollbar-within-aurelia"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-3"&gt;Using Rollbar within Aurelia&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In order to integrate Rollbar into Aurelia, I created a &lt;tt class="docutils literal"&gt;RollbarAppender&lt;/tt&gt; that implements the &lt;tt class="docutils literal"&gt;Appender&lt;/tt&gt; interface (see the interface definition &lt;a class="reference external" href="https://github.com/aurelia/logging/blob/master/src/index.js#L85"&gt;here&lt;/a&gt;). What it does is pretty straightforward: it dispatches incoming log messages to the proper methods of the &lt;tt class="docutils literal"&gt;Rollbar&lt;/tt&gt; object. Just like in &lt;tt class="docutils literal"&gt;ConsoleAppender&lt;/tt&gt; I prepend the level and id of the logger to the message.&lt;/p&gt;
&lt;p&gt;Since according to &lt;a class="reference external" href="https://rollbar.com/docs/notifier/rollbar.js/#usage"&gt;the doc&lt;/a&gt; I can only give one message, I put the eventual extra parameters of the function in a JSON object to log them as extra arguments.&lt;/p&gt;
&lt;p&gt;Here is the full code. You can download it &lt;a class="reference external" href="https://www.jujens.eu/static/aurelia-rollbar/rollbar.js"&gt;here&lt;/a&gt;. You can also install it with &lt;tt class="docutils literal"&gt;npm install &lt;span class="pre"&gt;au-rollbar&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RollbarAppender&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mainArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getRollbar&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`DEBUG [&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;]: &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mainArgs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_formatRest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mainArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getRollbar&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`INFO [&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;]  &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mainArgs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_formatRest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mainArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getRollbar&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`WARN [&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;] &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mainArgs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_formatRest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mainArgs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getRollbar&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`ERROR [&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;]  &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;mainArgs&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_formatRest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;_formatRest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stingify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;29 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;getRollbar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;31 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Rollbar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// eslint-disable-line no-console&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;33 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Rollbar is not defined'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// eslint-disable-line no-console&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;35 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;36 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;37 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;38 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;39 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;warning&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;40 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;41 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;42 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;43 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;44 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Rollbar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;45 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;46 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You then need to import it with something like &lt;tt class="docutils literal"&gt;import RollbarAppender from &lt;span class="pre"&gt;'./logging';&lt;/span&gt;&lt;/tt&gt; (or &lt;tt class="docutils literal"&gt;import RollbarAppender from &lt;span class="pre"&gt;'au-rollbar'&lt;/span&gt;&lt;/tt&gt; if you installed it with npm). You then need to configure your logging. Since I doubt you will want to enable it in development, I suggest you configure Aurelia like this (in your &lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt; file):&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RollbarAppender&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'au-rollbar'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-logging'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;standardConfiguration&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Only log to the console when developing (default configuration of developmentLogging)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;developmentLogging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// Production logging is inspired by developmentLogging&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// This loads the ``aurelia-logging-console`` module and then configures logging.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preTask&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;normalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-logging-console'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bootstrapperName&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="c1"&gt;// Once the module is loaded, we configure logging.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="c1"&gt;// We add the ConsoleAppender so all messages are logged in the console of the browser.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addAppender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ConsoleAppender&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="c1"&gt;// If the Rollbar exists in the window object (ie if Rollbar is loaded),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="c1"&gt;// we add the RollbarAppender so messages are sent to rollbar.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="c1"&gt;// If it is not, we print a warning in the browser console.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Rollbar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addAppender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;RollbarAppender&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;                        &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Rollbar is not defined'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// eslint-disable-line no-console&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;29 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="c1"&gt;// Configure logger to log only messages of level warning or above.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;31 &lt;/span&gt;&lt;span class="w"&gt;                    &lt;/span&gt;&lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;33 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;35 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;36 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;37 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;38 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-testing'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;39 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;40 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;41 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;42 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="loading-rollbar"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-4"&gt;Loading Rollbar&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now we need to load the Rollbar script for Rollbar to actually work. Since in addition to logging messages with the &lt;tt class="docutils literal"&gt;Rollbar&lt;/tt&gt; object, Rollbar can log uncaught exception, it is better to load it as early as possible (to detect errors when Aurelia is bootstraping for instance). To achieve this, I decided to put the configuration and quickstart script (from &lt;a class="reference external" href="https://rollbar.com/docs/notifier/rollbar.js/"&gt;here&lt;/a&gt;) into a &lt;tt class="docutils literal"&gt;rollbar.js&lt;/tt&gt; script located in the &lt;tt class="docutils literal"&gt;scripts&lt;/tt&gt; folder. Then, since I use the CLI to build my project, I edited &lt;tt class="docutils literal"&gt;aurelia_project/aurelia.json&lt;/tt&gt; to add &lt;tt class="docutils literal"&gt;&amp;quot;scripts/rollbar.js&amp;quot;,&lt;/tt&gt; in the prepend section of the vendor bundle. So the definition of the vendor bundle looks like:&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="ln"&gt; 1 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 2 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;vendor-bundle.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 3 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;prepend&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 4 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;node_modules/bluebird/js/browser/bluebird.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 5 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;scripts/polyfills.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 6 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;scripts/rollbar.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 7 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;node_modules/requirejs/require.js&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 8 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt; 9 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;dependencies&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;10 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-binding&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;11 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-bootstrapper&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;12 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-dependency-injection&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;13 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-event-aggregator&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;14 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-framework&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;15 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-history&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;16 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-history-browser&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;17 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-loader&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;18 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-loader-default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;19 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-logging&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;20 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-logging-console&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;21 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-metadata&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;22 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-pal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;23 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-pal-browser&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;24 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;25 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-polyfills&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;26 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-route-recognizer&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;27 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-router&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;28 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-task-queue&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;29 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-templating&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;30 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-templating-binding&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;31 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;text&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;32 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;33 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-templating-resources&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;34 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../node_modules/aurelia-templating-resources/dist/amd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;35 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-templating-resources&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;36 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;37 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;38 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-templating-router&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;39 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../node_modules/aurelia-templating-router/dist/amd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;40 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-templating-router&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;41 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;42 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;43 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-testing&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;44 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../node_modules/aurelia-testing/dist/amd&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;45 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-testing&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;46 &lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;env&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;dev&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;47 &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;48 &lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="ln"&gt;49 &lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;If you want to load Rollbar from another location, you can use the &lt;tt class="docutils literal"&gt;rollbarJsUrl&lt;/tt&gt; property in the configuration object and give it the URL from which you want to load the script.&lt;/p&gt;
&lt;p&gt;That's it. If you have any question or remark, please leave a comment.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="Aurelia"></category><category term="aurelia"></category><category term="rollbar"></category></entry><entry><title>A sample application with Aurelia UX</title><link href="https://www.jujens.eu/posts/en/2017/Aug/14/aurelia-ux/" rel="alternate"></link><published>2017-08-14T00:00:00+02:00</published><updated>2017-08-14T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2017-08-14:/posts/en/2017/Aug/14/aurelia-ux/</id><summary type="html">&lt;p&gt;This is a follow up to my &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2016/Mar/15/ionic2-aurelia-f7/"&gt;Small comparison of ionic2 and Aurelia + Framework7 for hybrid mobile applications&lt;/a&gt; article where I compared ionic2 and Aurelia with Framework 7 for building an hybrid applications. At the time, Aurelia UX was not yet ready (and we were expecting a different, closed source …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This is a follow up to my &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2016/Mar/15/ionic2-aurelia-f7/"&gt;Small comparison of ionic2 and Aurelia + Framework7 for hybrid mobile applications&lt;/a&gt; article where I compared ionic2 and Aurelia with Framework 7 for building an hybrid applications. At the time, Aurelia UX was not yet ready (and we were expecting a different, closed source project called Aurelia Interface). Now, Aurelia UX made huge process and I was able to write the sample application in it. I describe here how it went. The code is available &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/tree/master/aurelia-ux-todo"&gt;on github&lt;/a&gt; with the other applications.&lt;/p&gt;
&lt;p&gt;As a reminder, here are the features of the TODO application:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;List all the saved todos (home page)&lt;/li&gt;
&lt;li&gt;Edit an existing todo&lt;/li&gt;
&lt;li&gt;Add a todo&lt;/li&gt;
&lt;li&gt;Mark a todo as done on the home page. Done todos must have their title strike-through on the home page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wrote the application in TypeScript and CSS, since they are the languages Aurelia UX and its showcase application are written in. Here are screenshots of the result:&lt;/p&gt;
&lt;img alt="Page to list all TODOs" class="align-center" src="/images/aurelia-ux/todo-list.png" style="width: 25%;" /&gt;
&lt;img alt="Page to add or edit todo" class="align-center" src="/images/aurelia-ux/add-todo.png" style="width: 25%;" /&gt;
&lt;img alt="The menu of the application" class="align-center" src="/images/aurelia-ux/menu.png" style="width: 25%;" /&gt;
&lt;p&gt;I based my application on the &lt;a class="reference external" href="https://github.com/aurelia/app-ux-showcase"&gt;Aurelia UX showcase app&lt;/a&gt;: I cloned this repo, remove the code I didn't need and started to add my code. It is structured like this:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;An &lt;tt class="docutils literal"&gt;assets&lt;/tt&gt; folder with the images and fonts&lt;/li&gt;
&lt;li&gt;An &lt;tt class="docutils literal"&gt;aurelia_project&lt;/tt&gt; folder that contains the configuration for the &lt;a class="reference external" href="https://github.com/aurelia/cli"&gt;cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A &lt;tt class="docutils literal"&gt;custom_typings&lt;/tt&gt; folder for TypeScript type definitions&lt;/li&gt;
&lt;li&gt;A &lt;tt class="docutils literal"&gt;scripts&lt;/tt&gt; folder created by the cli for some useful scripts (eg &lt;tt class="docutils literal"&gt;require.js&lt;/tt&gt;)&lt;/li&gt;
&lt;li&gt;A &lt;tt class="docutils literal"&gt;src&lt;/tt&gt; folder that contains the application in itself. It contains:&lt;ul&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;main.ts&lt;/tt&gt; file which is the entry point to the application&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;routes.ts&lt;/tt&gt; to configure the routes of the application&lt;/li&gt;
&lt;li&gt;CSS files common to all the application&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;app.ts&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;app.css&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;app.html&lt;/tt&gt; which configure the layout of the page and configures the menu and the router&lt;/li&gt;
&lt;li&gt;A &lt;tt class="docutils literal"&gt;models&lt;/tt&gt; folder that contains type definition of the application for TypeScript&lt;/li&gt;
&lt;li&gt;A &lt;tt class="docutils literal"&gt;pages&lt;/tt&gt; folder which contains the HTML, JS and CSS code for each routes&lt;/li&gt;
&lt;li&gt;A &lt;tt class="docutils literal"&gt;services&lt;/tt&gt; folder that contains services for the application&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now that's done, let's take a look at the code. I won't detail the &lt;tt class="docutils literal"&gt;main.ts&lt;/tt&gt; too much since it is very classic. I'll just point out to &lt;a class="reference external" href="https://github.com/aurelia/app-ux-showcase/blob/master/src/main.ts#L18"&gt;line 18&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-ux'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This is where we enable Aurelia UX in our application. If we don't, we won't be able to use its tags and attributes in our application.&lt;/p&gt;
&lt;p&gt;In the &lt;tt class="docutils literal"&gt;app.ts&lt;/tt&gt;, we import &lt;tt class="docutils literal"&gt;AureliaUX&lt;/tt&gt; from the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-ux&lt;/span&gt;&lt;/tt&gt; module and inject it into our application. If you look at the constructor, you'll see the first interesting piece:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ux&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AureliaUX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ux&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;design&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MAIN_THEME&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;ux&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;design&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MAIN_THEME&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;showNavigationMenu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Here is the definition of &lt;tt class="docutils literal"&gt;MAIN_THEME&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MAIN_THEME&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'#009688'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;accent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'#4CAF50'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;We are using this to set the color of the style of our application. If you look at the corresponding CSS, you can see something like:&lt;/p&gt;
&lt;pre class="code CSS literal-block"&gt;
&lt;span class="nt"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;header&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;${$&lt;/span&gt;&lt;span class="n"&gt;design&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;color&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;$design.primaryForeground&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;As you may guess, thanks to the &lt;tt class="docutils literal"&gt;design&lt;/tt&gt; property of the &lt;tt class="docutils literal"&gt;AureliaUX&lt;/tt&gt; object, our CSS code and our TypeScript code are linked. The content of &lt;tt class="docutils literal"&gt;ux.design.primary&lt;/tt&gt; will replace all the occurrences of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;${$design.primary}&lt;/span&gt;&lt;/tt&gt; in the CSS. And the best part is: it is dynamic! If you change &lt;tt class="docutils literal"&gt;ux.design.primary&lt;/tt&gt; in your application, the style will update to reflect it. You can test this by opening the hamburger menu and clicking on the &lt;em&gt;Alternate theme&lt;/em&gt; check box. This will call the &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/master/aurelia-ux-todo/src/app.ts#L37"&gt;theme changed&lt;/a&gt; function to update the style of the application.&lt;/p&gt;
&lt;p&gt;To use this feature in our HTML, we must:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Require the CSS file with the &lt;tt class="docutils literal"&gt;ux&lt;/tt&gt; hash:&lt;/p&gt;
&lt;pre class="code HTML literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;require&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;./app.css#ux&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;require&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Use &lt;tt class="docutils literal"&gt;styles.header&lt;/tt&gt; as a custom attribute, like this:&lt;/p&gt;
&lt;pre class="code HTML literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt; &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;header-h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;${router.currentInstruction.config.navModel.title}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clickable&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;_self&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delegate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;toggleNavigationMenu()&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;header-burger&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fa fa-bars&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;true&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Simple and efficient, isn't?&lt;/p&gt;
&lt;p&gt;Navigation in the application is handled by Aurelia's router. Since you are probably already familiar with it, I won't detail that here.&lt;/p&gt;
&lt;p&gt;Lets take a look at the edit page. There isn't much in the TS code: we basically setup the validation (to know more about validation, see &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2017/Jan/24/aurelia-validation/"&gt;this article&lt;/a&gt;), rely on the storage service to load the TODO to edit (if we are editing an existing TODO) and save it in the cache. We also use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;this.router.navigateToRoute('list');&lt;/span&gt;&lt;/tt&gt; to navigate in the application.&lt;/p&gt;
&lt;p&gt;The more interesting part is the HTML where I use tags defined by Aurelia UX. The form to add a TODO is located in a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ux-form&lt;/span&gt;&lt;/tt&gt; tag. I then use the custom tags to create:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;The input for the title:&lt;/p&gt;
&lt;pre class="code HTML literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ux-input&lt;/span&gt;
             &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Title&amp;quot;&lt;/span&gt;
             &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo.title &amp;amp; validate&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ux-input&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The textarea:&lt;/p&gt;
&lt;pre class="code HTML literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ux-textarea&lt;/span&gt;
              &lt;span class="na"&gt;auto-resize&lt;/span&gt;
              &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Description&amp;quot;&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo.description &amp;amp; validate&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ux-textarea&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The done checkbox:&lt;/p&gt;
&lt;pre class="code HTML literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ux-field&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ux-checkbox&lt;/span&gt; &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo.done&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Done&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ux-checkbox&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ux-field&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;The save button:&lt;/p&gt;
&lt;pre class="code HTML literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ux-button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;raised&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delegate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;save()&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;!canSave&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ux-button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see, they are simply custom elements and behave like any other custom elements would.&lt;/p&gt;
&lt;p&gt;The list page is quite basic so I won't detail it. If you have questions, please leave a comment.&lt;/p&gt;
&lt;p&gt;What I didn't test:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Building the application and sending it to my phone. I suspect this will be similar to what you can do with Framework 7 or ionic.&lt;/li&gt;
&lt;li&gt;Using SCSS instead of CSS. I frankly have no idea how this would behave in conjuction with how you can interact with CSS inside Aurelia UX (things like &lt;tt class="docutils literal"&gt;ux.design.primary = MAIN_THEME.primary;&lt;/tt&gt; in your JS and &lt;tt class="docutils literal"&gt;background: &lt;span class="pre"&gt;${$design.primary};&lt;/span&gt;&lt;/tt&gt; in the CSS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is currently no documentation about Aurelia UX but the &lt;a class="reference external" href="https://github.com/aurelia/app-ux-showcase"&gt;showcase app&lt;/a&gt; contains example for all the available components. You can clone the repo and launch it with &lt;tt class="docutils literal"&gt;npm install&lt;/tt&gt; to install the dependencies and then &lt;tt class="docutils literal"&gt;au run&lt;/tt&gt; to run it.&lt;/p&gt;
&lt;p&gt;To conclude: I find the project promising. It's easy to work with, has some really nice features (such as the ability to have easy links between the code and the CSS). The look and feel of the application is great. The integration with Aurelia is perfect. It also looks a lot like &lt;a class="reference external" href="http://ionicframework.com/"&gt;ionic&lt;/a&gt; so developers used to this framework shouldn't be lost. They are however some downsides: this is still a work in progress and some components don't exist yet (eg &lt;a class="reference external" href="https://github.com/aurelia/ux/issues/19"&gt;select&lt;/a&gt;). I also found that the checkboxes are not always reliable. Some clicks where not taken into accounts. I don't know if this came from my browser (FireFox for desktop, I haven't tested on mobile) or from the way the check boxes are rendered (they are not real check boxes but divs so they can be styled correctly).&lt;/p&gt;
&lt;p&gt;That's it for today. If you have a question or remark, please leave a comment below.&lt;/p&gt;
</content><category term="Aurelia"></category><category term="ionic2"></category><category term="angular2"></category><category term="aurelia"></category><category term="framework7"></category><category term="android"></category><category term="mobile"></category></entry><entry><title>Form validation with Aurelia</title><link href="https://www.jujens.eu/posts/en/2017/Jan/24/aurelia-validation/" rel="alternate"></link><published>2017-01-24T00:00:00+01:00</published><updated>2017-07-20T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2017-01-24:/posts/en/2017/Jan/24/aurelia-validation/</id><summary type="html">&lt;!-- note:: Update from 2017-07-20: ``aurelia-validation`` now supports events, so it is much easier to disable the submission of the form until it is valid. See the `Disable the submission until the form is valid`_ section for the code. --&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Update from 2017-01-25: after &lt;a class="reference external" href="https://gitter.im/aurelia/validation?at=5887be7fdcb66e4f76a70b0b"&gt;new exchanges&lt;/a&gt; with &lt;a class="reference external" href="https://github.com/dkent600"&gt;Doug Kent&lt;/a&gt; I was able to improve my workaround to disable the submission of the form until it is valid. See the &lt;a class="reference internal" href="#disable-the-submission-until-the-form-is-valid"&gt;Disable the submission until the form is valid&lt;/a&gt; section for the code.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I promised &lt;a class="reference external" href="/posts/en/2016/Mar/15/ionic2-aurelia-f7/"&gt;some months ago&lt;/a&gt; that I will …&lt;/p&gt;</summary><content type="html">&lt;!-- note:: Update from 2017-07-20: ``aurelia-validation`` now supports events, so it is much easier to disable the submission of the form until it is valid. See the `Disable the submission until the form is valid`_ section for the code. --&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Update from 2017-01-25: after &lt;a class="reference external" href="https://gitter.im/aurelia/validation?at=5887be7fdcb66e4f76a70b0b"&gt;new exchanges&lt;/a&gt; with &lt;a class="reference external" href="https://github.com/dkent600"&gt;Doug Kent&lt;/a&gt; I was able to improve my workaround to disable the submission of the form until it is valid. See the &lt;a class="reference internal" href="#disable-the-submission-until-the-form-is-valid"&gt;Disable the submission until the form is valid&lt;/a&gt; section for the code.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I promised &lt;a class="reference external" href="/posts/en/2016/Mar/15/ionic2-aurelia-f7/"&gt;some months ago&lt;/a&gt; that I will talk about form validation with &lt;a class="reference external" href="http://aurelia.io"&gt;Aurelia&lt;/a&gt;. At that time, the validation plugin was still under heavy development. Furthermore, aurelia-validation was dropped in favour of aurelia-validatejs and then aurelia-validatejs was dropped for aurelia-validation. Now things look to have settled down for &lt;a class="reference external" href="https://github.com/aurelia/validation"&gt;aurelia-validation&lt;/a&gt;. That's what I'll talk about today.&lt;/p&gt;
&lt;p&gt;From what I see in &lt;a class="reference external" href="http://aurelia.io/hub.html#/doc/article/aurelia/validation/latest/validation-basics"&gt;the documentation&lt;/a&gt;, the plugin is both powerful and expressive (many rules available, possibility to chain them, error reporting, possibility to create your own rules, possibility to hook on the render phase, …).&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#quick-overview" id="toc-entry-1"&gt;Quick overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#more-advanced-example" id="toc-entry-2"&gt;More advanced example&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#disable-the-submission-until-the-form-is-valid" id="toc-entry-3"&gt;Disable the submission until the form is valid&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#workaround-1" id="toc-entry-4"&gt;Workaround 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#workaround-2-looks-more-solid" id="toc-entry-5"&gt;Workaround 2 (looks more solid)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#workaround-3-how-you-should-do-it" id="toc-entry-6"&gt;Workaround 3 (how you should do it)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#conclusion" id="toc-entry-7"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="quick-overview"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;Quick overview&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To get started, you need to:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Install aurelia-validation: &lt;tt class="docutils literal"&gt;npm install &lt;span class="pre"&gt;--save&lt;/span&gt; &lt;span class="pre"&gt;aurelia-validation&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Enable it in your application (in your &lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt;):&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;standardConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-validation'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;developmentLogging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The more direct way to use validation is to:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Import &lt;tt class="docutils literal"&gt;ValidationControllerFactory&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;ValidationRules&lt;/tt&gt; from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-validation&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Inject &lt;tt class="docutils literal"&gt;ValidationControllerFactory&lt;/tt&gt; in you component &lt;tt class="docutils literal"&gt;&amp;#64;inject(ValidationControllerFactory)&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Create the validation controller: &lt;tt class="docutils literal"&gt;this.controller = &lt;span class="pre"&gt;controllerFactory.createForCurrentScope();&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Use &lt;tt class="docutils literal"&gt;ValidationRules&lt;/tt&gt; to create some validation rules. For instance to make a field named &lt;tt class="docutils literal"&gt;title&lt;/tt&gt; of the current class mandatory: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ValidationRules.ensure('title').required().on(this);&lt;/span&gt;&lt;/tt&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In a nutshell, this JavaScript code:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-framework'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ValidationControllerFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ValidationRules&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-validation'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="err"&gt;&amp;#64;&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ValidationControllerFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TodoPage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controllerFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;controllerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createForCurrentScope&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;ValidationRules&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;with this template:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Title&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;title &amp;amp; validate&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You probably want to view the list of validation errors. To do that, you can use the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;validation-errors&lt;/span&gt;&lt;/tt&gt; attribute like that:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;validation-errors&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;errors&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Title&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;title &amp;amp; validate&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;controller.errors&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;repeat&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;error of controller.errors&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                ${error.message}
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This will display the default error message for each rule that failed. You can change this message with &lt;tt class="docutils literal"&gt;withMessage&lt;/tt&gt;: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ValidationRules.ensure('title').required().withMessage('Title&lt;/span&gt; must be provided in this &lt;span class="pre"&gt;form.').on(this)&lt;/span&gt;&lt;/tt&gt;. You can also use &lt;a class="reference external" href="https://github.com/aurelia/i18n"&gt;aurelia-i18n&lt;/a&gt; to translate these messages (see &lt;a class="reference external" href="http://aurelia.io/hub.html#/doc/article/aurelia/validation/latest/validation-basics/12"&gt;the documentation&lt;/a&gt;).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="more-advanced-example"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-2"&gt;More advanced example&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that we've covered the basics, let's get back to my mobile todo application built with Aurelia and &lt;a class="reference external" href="http://framework7.io/"&gt;Framework7&lt;/a&gt;. I have a form to create a TODO that requires a title:&lt;/p&gt;
&lt;img alt="Page to view and edit todo" src="/images/ionic2-aurelia-f7/todo-view.png" style="width: 100%;" /&gt;
&lt;p&gt;My goal is to use aurelia-validation to:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Prevent the user to create a TODO until a title with at least 3 characters is supplied (just to see how we can chain rules for a property). In order to to that, I just need to use the proper validation rules (since the title is stored in a &lt;tt class="docutils literal"&gt;todo&lt;/tt&gt; property of my object, I don't use &lt;tt class="docutils literal"&gt;.on(this)&lt;/tt&gt; but &lt;tt class="docutils literal"&gt;.on(this.todo)&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;ValidationRules&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;withMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Title must at least be 3 chars long.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Display an error message next to the title with a proper error message. This message will of course be updated as the user types. To achieve that, I need to:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Change how validation is trigger: by default it is on blur (that is when the user leaves the field). Let's import &lt;tt class="docutils literal"&gt;validateTrigger&lt;/tt&gt; and update our controller: &lt;tt class="docutils literal"&gt;this.controller.validateTrigger = validateTrigger.changeOrBlur;&lt;/tt&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Use the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;validation-errors&lt;/span&gt;&lt;/tt&gt; attribute on an element that only contains the input for my title. This way, the value bound to it will only contain errors for this field (by construction, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;validation-errors&lt;/span&gt;&lt;/tt&gt; contains errors only for the fields it contains):&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-inner&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;validation-errors&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;titleErrors&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-title label&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Title
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt; &lt;span class="na"&gt;repeat&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;errorInfo of titleErrors&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;${errorInfo.error.message}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-input&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="cm"&gt;&amp;lt;!-- again since the title is in todo, I have to use todo.title, not just title --&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Title&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo.title &amp;amp; validate&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="section" id="disable-the-submission-until-the-form-is-valid"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-3"&gt;Disable the submission until the form is valid&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At this point I ran into a problem: how can I disable the submission of a TODO until it is valid? If I use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;validation-errors.bind=&amp;quot;errors&amp;quot;&lt;/span&gt;&lt;/tt&gt; on an element that includes the full form, I can use &lt;tt class="docutils literal"&gt;errors.length === 0&lt;/tt&gt; to check that it is valid. The problem is, that when the page loads, the form is invalid but since validation hasn't run yet, the submit button will be active. If I validate the form in the &lt;tt class="docutils literal"&gt;attached&lt;/tt&gt; method of the component, &lt;tt class="docutils literal"&gt;errors&lt;/tt&gt; is not empty (so the button is correctly disabled) but the error message for the title is displayed too soon (before the user event interacted with the form which is not what I want).&lt;/p&gt;
&lt;p&gt;In Angular2, you can rely on the &lt;tt class="docutils literal"&gt;dirty&lt;/tt&gt; property of your form controller to know if the user has started to interact with the form. So I could use something like &lt;tt class="docutils literal"&gt;controller.dirty &amp;amp;&amp;amp; errors.length === 0&lt;/tt&gt; to enable/disable my button (see &lt;a class="reference external" href="https://medium.com/&amp;#64;daviddentoom/angular-2-form-validation-9b26f73fcb81"&gt;this article&lt;/a&gt; for form validation with Angular2).&lt;/p&gt;
&lt;del&gt;
Sadly there is no such property with aurelia-validation. There is &lt;a href="https://github.com/aurelia/validation/issues/318"&gt;an issue about this&lt;/a&gt; with a link to &lt;a href="http://www.sobell.net/aurelia-validation-events/"&gt;this blog post&lt;/a&gt; suggesting to hook on the rendering phase to solve this.
&lt;/del&gt;&lt;p&gt;There a way to easily do this with aurelia. See &lt;a class="reference internal" href="#workaround-3-how-you-should-do-it"&gt;Workaround 3 (how you should do it)&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="workaround-1"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-4"&gt;Workaround 1&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I asked on &lt;a class="reference external" href="https://gitter.im/aurelia/validation"&gt;gitter&lt;/a&gt; for help and &lt;a class="reference external" href="https://github.com/dkent600"&gt;Doug Kent&lt;/a&gt; suggested various &lt;a class="reference external" href="https://gitter.im/aurelia/validation?at=58878411c0de6f017fe67817"&gt;solutions&lt;/a&gt; the most promising being to use two controllers: one to validate the button, one to display the error. I didn't managed to get it to work but, based on this idea, I found a workaround (and I hope we will have a clean method to do this in the future):&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;I keep the controller as is for validation and error display.&lt;/li&gt;
&lt;li&gt;I use a &lt;tt class="docutils literal"&gt;Validator&lt;/tt&gt; to manually validate the form without impacting error reporting. The idea is to:&lt;ol class="arabic"&gt;
&lt;li&gt;Validate the form in &lt;tt class="docutils literal"&gt;attached&lt;/tt&gt;: this form can also be used to edit an existing todo (which has a valid title), so the save button can be enabled or disabled on page load. This validation is done with the &lt;tt class="docutils literal"&gt;Validator&lt;/tt&gt; which means no errors will be reported to the user.&lt;/li&gt;
&lt;li&gt;Watch on the &lt;tt class="docutils literal"&gt;title&lt;/tt&gt; property of my todo and validate the form each time it changes.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is the relevant part of the code:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ObserverLocator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-framework'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ValidationControllerFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ValidationRules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validateTrigger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-validation'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="err"&gt;&amp;#64;&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ValidationControllerFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ObserverLocator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TodoPage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controllerFactory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c1"&gt;// We rely on the controller to display the errors.&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;controllerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createForCurrentScope&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c1"&gt;// We rely on the validator to know if the canSave the todo or not&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c1"&gt;// (update the canSave property in the validation method)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canSave&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;ValidationRules&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;withMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Title must at least be 3 chars long.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateTrigger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validateTrigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeOrBlur&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

        &lt;/span&gt;&lt;span class="nx"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nx"&gt;attached&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="c1"&gt;// results is an array of validation results. Each result has a&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="c1"&gt;// valid property set to true if the rule is valid.&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

            &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canSave&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I then use &lt;tt class="docutils literal"&gt;canSave&lt;/tt&gt; in my template:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#&amp;quot;&lt;/span&gt;
   &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;button button-big button-fill color-green ${canSave ? '' : 'disabled'}&amp;quot;&lt;/span&gt;
   &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delegate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;saveTodo()&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can view the full code &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/9978d01823d30a3c3d733add2c246fbe621bf022/aurelia-f7-todo/app/pages/todo/todo.js"&gt;here&lt;/a&gt; and &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/9978d01823d30a3c3d733add2c246fbe621bf022/aurelia-f7-todo/app/pages/todo/todo.html"&gt;here&lt;/a&gt;. It feels a bit hackish and I don't think this will scale to big forms.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="workaround-2-looks-more-solid"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-5"&gt;Workaround 2 (looks more solid)&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;After new exchanges &lt;a class="reference external" href="https://gitter.im/aurelia/validation?at=5887be7fdcb66e4f76a70b0b"&gt;on gitter&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/dkent600"&gt;Doug Kent&lt;/a&gt; made another suggestion: use a custom validator instead of &lt;tt class="docutils literal"&gt;StandardValidator&lt;/tt&gt;. This way I should be able to hook on the validation phase and call a callback. Guess what: it worked. Let's see how.&lt;/p&gt;
&lt;p&gt;We start by creating an new class respecting the &lt;a class="reference external" href="https://github.com/aurelia/validation/blob/master/src/validator.ts"&gt;Validator interface&lt;/a&gt; and taking as argument an instance of the &lt;tt class="docutils literal"&gt;StandardValidator&lt;/tt&gt; which will do the actual validation work and a callback:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The idea is then to use the promise returned by &lt;tt class="docutils literal"&gt;this.validator.validateObject&lt;/tt&gt; to call our callback each time the promise is resolved:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;validateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;We are almost done. We also need to trigger the validation when a property of the form is validated. For instance, here, when the user modifies the title of the todo, &lt;tt class="docutils literal"&gt;validateObject&lt;/tt&gt; won't be called. The controller will only call &lt;tt class="docutils literal"&gt;validateProperty&lt;/tt&gt;. That's not a big problem: once the property is validated, we can validate the whole form to update &lt;tt class="docutils literal"&gt;TodoPage.canSave&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;validateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Now let's see how we can use this. We initialize the validator and the controller in the constructor:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="c1"&gt;// validator is an instance of StandardValidator that was injected by DI.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;TodoValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateCanSave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;controllerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createForCurrentScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateTrigger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validateTrigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeOrBlur&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Since this page can be used in two cases (creating a new todo and editing an existing one), we differ the creation of the validation rules a little bit: if we set them up in the constructor, they wouldn't apply on the proper object when editing a TODO. We wait for the activation and the page to distinguish between these to cases:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Creating a new TODO&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setupValidation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Editing a TODO&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Fetch the TODO.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Save the TODO in the current object.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c1"&gt;// Setup the validation rules.&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setupValidation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="c1"&gt;// Launch validation to enable the save button if the edited TODO is valid.&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;setupValidation&lt;/tt&gt; method is just here to create our validation rules:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;ValidationRules&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;withMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Title must at least be 3 chars long.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;validate&lt;/tt&gt; method just calls &lt;tt class="docutils literal"&gt;this.validator.validateObject(this.todo);&lt;/tt&gt;. As for &lt;tt class="docutils literal"&gt;updateCanSave&lt;/tt&gt; it updates the &lt;tt class="docutils literal"&gt;canSave&lt;/tt&gt; property based on the results of the validation:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;updateCanSave&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validationResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validationResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canSave&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;And that's it. The main advantage of this method, is that no matter how many fields you have, you don't need extra work. It can also easily be reused for different forms. You can see the full code &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/c5c28bf229071d8cb16d5fc7b13c3bfd4a384e64/aurelia-f7-todo/app/pages/todo/todo.js"&gt;here&lt;/a&gt; (with additional comments) and &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/c5c28bf229071d8cb16d5fc7b13c3bfd4a384e64/aurelia-f7-todo/app/pages/todo/todo.html"&gt;here&lt;/a&gt; (template).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="workaround-3-how-you-should-do-it"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="#toc-entry-6"&gt;Workaround 3 (how you should do it)&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As a follow up to &lt;a class="reference external" href="https://github.com/aurelia/validation/issues/318"&gt;this issue&lt;/a&gt; work has been done by Aurelia's contributor &lt;a class="reference external" href="https://github.com/jdanyow"&gt;Jeremy Danyow&lt;/a&gt; so we can execute a callback each time the validator is called. This greatly simplifies the code. You can view the documentation &lt;a class="reference external" href="https://github.com/aurelia/validation/blob/master/doc/article/en-US/validation-basics.md#events"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the constructor, we create the controller with a new validator and register the callback:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;controllerFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canSave&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;controllerFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createForCurrentScope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateTrigger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validateTrigger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeOrBlur&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateWhole&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The callback passed to &lt;tt class="docutils literal"&gt;this.controller.subscribe&lt;/tt&gt; will be called each time the validator is run, for &lt;tt class="docutils literal"&gt;reset&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;validate&lt;/tt&gt; events. It has one parameter: the event. You can use it to filter by validation type or to get the errors associated with this run. Go &lt;a class="reference external" href="https://github.com/aurelia/validation/blob/master/src/validate-event.ts"&gt;here&lt;/a&gt; to view the full interface.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We don't need to manually run &lt;tt class="docutils literal"&gt;this.validateWhole&lt;/tt&gt; manually to initialize &lt;tt class="docutils literal"&gt;canSave&lt;/tt&gt; correctly. When the form is created and validation is bootstraped, the validator will run with a &lt;tt class="docutils literal"&gt;reset&lt;/tt&gt; event.&lt;/p&gt;
&lt;p&gt;In our case, we want to validate the whole form each time a field is validated so we can update our &lt;tt class="docutils literal"&gt;canSave&lt;/tt&gt; property:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kr"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;validateWhole&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canSave&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;And we are done. This is much simplier and shorter that all the other workaround.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Running validation will not display errors messages associated to any field. So even if you validate multiple fields and display error messages for each one, &lt;tt class="docutils literal"&gt;validateWhole&lt;/tt&gt; will not have an impact.&lt;/p&gt;
&lt;p&gt;You can view the full file &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/master/aurelia-ux-todo/src/pages/edit.ts"&gt;here&lt;/a&gt; and the associated template &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/master/aurelia-ux-todo/src/pages/edit.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-7"&gt;Conclusion&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You should now be able to validate your forms with Aurelia. If you have a question or a remark, please leave a comment.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="Aurelia"></category><category term="aurelia"></category><category term="android"></category><category term="mobile"></category></entry><entry><title>Make navigation user friendly on your Aurelia SPA</title><link href="https://www.jujens.eu/posts/en/2016/Aug/28/navigation-user-friendly/" rel="alternate"></link><published>2016-08-28T00:00:00+02:00</published><updated>2016-08-28T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2016-08-28:/posts/en/2016/Aug/28/navigation-user-friendly/</id><summary type="html">&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#add-a-spinner" id="toc-entry-1"&gt;Add a spinner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#use-the-wait-cursor-while-navigating" id="toc-entry-2"&gt;Use the wait cursor while navigating&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#display-a-message-to-no-script-user" id="toc-entry-3"&gt;Display a message to no-script user&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#more" id="toc-entry-4"&gt;More&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="add-a-spinner"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;Add a spinner&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Add the &lt;a class="reference external" href="http://fontawesome.io/"&gt;font awesome files&lt;/a&gt; to your project (CSS
and fonts files).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Import the font awesome style sheet in your &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt;&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/assets/fonts/fontawesome/font-awesome …&lt;/span&gt;&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</summary><content type="html">&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#add-a-spinner" id="toc-entry-1"&gt;Add a spinner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#use-the-wait-cursor-while-navigating" id="toc-entry-2"&gt;Use the wait cursor while navigating&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#display-a-message-to-no-script-user" id="toc-entry-3"&gt;Display a message to no-script user&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#more" id="toc-entry-4"&gt;More&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="add-a-spinner"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;Add a spinner&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Add the &lt;a class="reference external" href="http://fontawesome.io/"&gt;font awesome files&lt;/a&gt; to your project (CSS
and fonts files).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Import the font awesome style sheet in your &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt;&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;stylesheet&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/assets/fonts/fontawesome/font-awesome.min.css&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Add the CSS code below to create the spinner either directly in the
&lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt; with &lt;tt class="docutils literal"&gt;style&lt;/tt&gt; tag or in a CSS file that you load in it:&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;splash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="kt"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;box-sizing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;border-box&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;splash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;text-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;text-transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Helvetica Neue&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;splash&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;fa-spinner&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;inline-block&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="kt"&gt;px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Update the &lt;tt class="docutils literal"&gt;div&lt;/tt&gt; in which your Aurelia application will be instantiated. Eg, replace:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="na"&gt;aurelia-app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;By:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="na"&gt;aurelia-app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;splash&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Arena of Titans&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;fa fa-spinner fa-spin&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;i&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="use-the-wait-cursor-while-navigating"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-2"&gt;Use the wait cursor while navigating&lt;/a&gt;&lt;/h2&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Change your &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;router-view&lt;/span&gt;&lt;/tt&gt; from:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;router-view&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;router-view&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;router-view&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${router.isNavigating ? 'navigating' : ''}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;router-view&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;this will add a class &lt;tt class="docutils literal"&gt;navigating&lt;/tt&gt; to the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;router-view&lt;/span&gt;&lt;/tt&gt; tag when the
router is navigating and nothing otherwise.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Add this global CSS code to your application:&lt;/p&gt;
&lt;pre class="code css literal-block"&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;navigating&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;navigating&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;wait&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cp"&gt;!important&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="k"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;This will display the wait cursor on the page and reduce the opacity on the
page to signal the user a navigation is going on. You can of course change
this code to match your application.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Don't forget to add the router to your object. In the
&lt;tt class="docutils literal"&gt;configureRouter(config, router)&lt;/tt&gt; function, do:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="display-a-message-to-no-script-user"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-3"&gt;Display a message to no-script user&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some of your users may use an extension like &lt;a class="reference external" href="https://addons.mozilla.org/en-US/firefox/addon/noscript"&gt;noscript&lt;/a&gt; which disable
JavaScript on page you visit. By default, you will greet them with either a
blank page or a never-ending spinner. I think it is more user friendly to show
them a message asking to enable JavaScript. To do, add in your &lt;tt class="docutils literal"&gt;body&lt;/tt&gt; tag:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
 &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Arena of Titans cannot work without JavaScript. Please enable it.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Arena of Titans ne peut pas fonctionner sans JavaScript. Merci de l'activer.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;noscript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="more"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-4"&gt;More&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To view the full details, look at these two commits:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/commits/a0630aadd969e87da85c9b87c972cb94d4443ae7"&gt;feat(index): improve feeling on page load&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/commits/e0ad0e16b804920442cd39749b4b964c64a98580"&gt;feat(app): notify visualy that the router is navigating&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content><category term="Aurelia"></category><category term="aurelia"></category><category term="font awesome"></category></entry><entry><title>Switching an application to aurelia-cli</title><link href="https://www.jujens.eu/posts/en/2016/Aug/17/switch-to-aurelia-cli/" rel="alternate"></link><published>2016-08-17T00:00:00+02:00</published><updated>2016-08-17T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2016-08-17:/posts/en/2016/Aug/17/switch-to-aurelia-cli/</id><summary type="html">&lt;p&gt;About a month ago, I started to make experiments with the &lt;a class="reference external" href="https://github.com/aurelia/webpack-plugin"&gt;webpack plugin for
Aurelia&lt;/a&gt; in order to split my
applications into multiple bundles. The application in question is a strategy
game called Arena of Titans. You can see it &lt;a class="reference external" href="http://www.arenaoftitans.com/"&gt;there&lt;/a&gt; (click play to create a game, or use &lt;a class="reference external" href="http://www.arenaoftitans.com/game/create"&gt;this …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;About a month ago, I started to make experiments with the &lt;a class="reference external" href="https://github.com/aurelia/webpack-plugin"&gt;webpack plugin for
Aurelia&lt;/a&gt; in order to split my
applications into multiple bundles. The application in question is a strategy
game called Arena of Titans. You can see it &lt;a class="reference external" href="http://www.arenaoftitans.com/"&gt;there&lt;/a&gt; (click play to create a game, or use &lt;a class="reference external" href="http://www.arenaoftitans.com/game/create"&gt;this
link&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;It was working well but the initial load of any page was slow. It was easy to
find out why: the bundle loaded by webpack was over 1.3 megabytes big. Why is it
that big? Well, it contained the game board which is about 1.1 megabytes
minified. Hence my will to split it into several bundles in order to speed up
the loading of the site. We still have to load the big board on the game page
but at least the site would work at an acceptable speed.&lt;/p&gt;
&lt;p&gt;I didn't manage to do it with webpack and after some time trying I thought what
about the new &lt;a class="reference external" href="https://github.com/aurelia/cli"&gt;aurelia-cli&lt;/a&gt;? I was able to do
it just fine. Here's how I did it. You can skip to the part about &lt;a class="reference internal" href="#bundles"&gt;bundles&lt;/a&gt; if
you're not interested in the rest.&lt;/p&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#presentation-of-aurelia-cli" id="toc-entry-1"&gt;Presentation of aurelia-cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#preparation" id="toc-entry-2"&gt;Preparation&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#updates-in-main-js" id="toc-entry-3"&gt;Updates in main.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#style" id="toc-entry-4"&gt;Style&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#bundles" id="toc-entry-5"&gt;Bundles&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#problems-encountered-with-bundles-and-their-solutions" id="toc-entry-6"&gt;Problems encountered with bundles and their solutions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#tests" id="toc-entry-7"&gt;Tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#conclusion" id="toc-entry-8"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="presentation-of-aurelia-cli"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;Presentation of aurelia-cli&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/aurelia/cli"&gt;aurelia-cli&lt;/a&gt; is a project aimed to provide a
command line interface to create and run projects written with the &lt;a class="reference external" href="http://aurelia.io/"&gt;Aurelia
JavaScript framework&lt;/a&gt;. You can:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Create a new project with &lt;tt class="docutils literal"&gt;au new &lt;span class="pre"&gt;&amp;lt;project-name&amp;gt;&lt;/span&gt;&lt;/tt&gt;. You will then be guided
by an assistant to configure the project: standard ES6 or typescript, pure CSS
or SCSS/Less, …&lt;/li&gt;
&lt;li&gt;Run the project with &lt;tt class="docutils literal"&gt;au run&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;au run &lt;span class="pre"&gt;--watch&lt;/span&gt;&lt;/tt&gt; to rebuild the app when
you save a modification.&lt;/li&gt;
&lt;li&gt;Test the project with &lt;tt class="docutils literal"&gt;au test&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;au test &lt;span class="pre"&gt;--watch&lt;/span&gt;&lt;/tt&gt; to retest the app
when you save a modification.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It relies on &lt;a class="reference external" href="http://gulpjs.com/"&gt;gulp&lt;/a&gt; to launch its tasks and on
&lt;a class="reference external" href="https://www.browsersync.io/"&gt;Browsersync&lt;/a&gt; to provide a small HTTP server
that will reload you page when you do a modification.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="preparation"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-2"&gt;Preparation&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To avoid messing with the current code, I created a new aurelia-cli project in a
separate folder and I copied the proper files in my project:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The folder &lt;tt class="docutils literal"&gt;aurelia_project&lt;/tt&gt; which contains the gulp tasks required by
aurelia-cli to run and the configuration of the project.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;scripts&lt;/tt&gt; folder which contains &lt;a class="reference external" href="http://requirejs.org"&gt;requirejs&lt;/a&gt; the
module loader used by project running with aurelia-cli and its text plugin to
load the HTML and CSS.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;test/aurelia-karma.js&lt;/span&gt;&lt;/tt&gt; a small file that allows you to use karma with
aurelia-cli projects. See the section about &lt;a class="reference internal" href="#tests"&gt;tests&lt;/a&gt; to learn more on that.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;karma.conf.js&lt;/tt&gt; so my karma configuration file doesn't rely on webpack any
more and meets the expectations of aurelia-cli.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I then updated my &lt;tt class="docutils literal"&gt;package.json&lt;/tt&gt; file. To do that, I just compared my current
&lt;tt class="docutils literal"&gt;package.json&lt;/tt&gt; with the one from the aurelia-cli project: I added the proper
dependencies (like aurelia-cli) and removed the old ones (like webpack).&lt;/p&gt;
&lt;p&gt;To avoid style warning with on the &lt;tt class="docutils literal"&gt;environement.js&lt;/tt&gt; file, I added it to the
&lt;tt class="docutils literal"&gt;.eslintignore&lt;/tt&gt;. This files contains utilities values to configure aurelia
(are we testing? are we in debug mode?):&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I then removed all of webpack configuration files and run &lt;tt class="docutils literal"&gt;rm &lt;span class="pre"&gt;-rf&lt;/span&gt; node_modules
&amp;amp;&amp;amp; npm install&lt;/tt&gt; to update the dependencies.&lt;/p&gt;
&lt;p&gt;Then, I adapted &lt;tt class="docutils literal"&gt;aurelia.json&lt;/tt&gt; to my needs:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The sources of my project are in a folder named &lt;tt class="docutils literal"&gt;app&lt;/tt&gt; and not in &lt;tt class="docutils literal"&gt;src&lt;/tt&gt; as
it is by default. So I changed all paths (a replace &lt;tt class="docutils literal"&gt;src&lt;/tt&gt; by &lt;tt class="docutils literal"&gt;app&lt;/tt&gt; did
it). I also had to correct &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-karma.js&lt;/span&gt;&lt;/tt&gt;. See the section about
&lt;a class="reference internal" href="#tests"&gt;tests&lt;/a&gt; to learn more about that.&lt;/li&gt;
&lt;li&gt;I configured the router to use push states. Which means my URLs look like
this: &lt;tt class="docutils literal"&gt;/game/create&lt;/tt&gt; and not like this &lt;tt class="docutils literal"&gt;#/game/create&lt;/tt&gt;. The problem is
that, by default, requirejs will load its bundle under
&lt;tt class="docutils literal"&gt;/game/scripts/bundle.js&lt;/tt&gt; instead of &lt;tt class="docutils literal"&gt;/scripts/bundle.js&lt;/tt&gt;. So I did my
first patch in aurelia-cli to &lt;a class="reference external" href="https://github.com/aurelia/cli/pull/243"&gt;add an option to use absolute path in requirejs&lt;/a&gt;. This way if you use
&lt;tt class="docutils literal"&gt;&amp;quot;useAbsolutePath&amp;quot;: true&lt;/tt&gt; in the &lt;tt class="docutils literal"&gt;build.targets&lt;/tt&gt; section of your
&lt;tt class="docutils literal"&gt;aurelia.json&lt;/tt&gt; file, your bundles will always be loaded in
&lt;tt class="docutils literal"&gt;/scripts/bundle.js&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also updated my &lt;tt class="docutils literal"&gt;global.scss&lt;/tt&gt; file to load fonts with an absolute URL and
&lt;tt class="docutils literal"&gt;aurelia_project/tasks/run.js&lt;/tt&gt; to change the port of Browsersync and disable
the &lt;a class="reference external" href="https://www.browsersync.io/docs/options#option-ghostMode"&gt;ghost mode&lt;/a&gt;
which mimics on all browsers the action you do in one: I want to be able to test
the game in different browsers running different players. I don't want it to
reproduce what I do in every browsers I have opened.&lt;/p&gt;
&lt;div class="section" id="updates-in-main-js"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-3"&gt;Updates in main.js&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I reverted Aurelia's configuration function to the classical &lt;tt class="docutils literal"&gt;export function
configure(aurelia)&lt;/tt&gt; and I added the two lines below to enable development
logging only on debug mode and to enable the testing plugin when testing:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;developmentLogging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;testing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-testing'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I also updated my configuration of &lt;a class="reference external" href="https://www.npmjs.com/package/bluebird"&gt;bluebird.js&lt;/a&gt; a promise library for javascript in
&lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt; from:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bluebird'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;longStackTraces&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;warnings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;longStackTraces&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;warnings&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="style"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-4"&gt;Style&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With webpack, you include the SCSS files like javascript but with the extension,
like this: &lt;tt class="docutils literal"&gt;import ./style/global.scss&lt;/tt&gt;. Webpack will then compile the file
and include it in the bundle thanks the loader you've configured. With
aurelia-cli, things are a little different. You include the file in your HTML
files like you require custom elements:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;require&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;./style/global.css&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;require&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Note that the extension of the file must be &lt;tt class="docutils literal"&gt;.css&lt;/tt&gt; whether you're using true
CSS or another language compiled to CSS like SCSS. The reason is that it will be
loaded as CSS. You just have to configure the &lt;tt class="docutils literal"&gt;cssPreprosessor&lt;/tt&gt; in your
&lt;tt class="docutils literal"&gt;aurelia.json&lt;/tt&gt; for the code to be correctly processed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="nt"&gt;&amp;quot;cssProcessor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sass&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;displayName&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Sass&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;fileExtension&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;.scss&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;source&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;app/**/*.scss&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="bundles"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-5"&gt;Bundles&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The ability to split my app into multiple bundles is the main reason I switched
to aurelia-cli. To create a bundle, all you have to do is to add an object with
a name and a list of dependencies to the &lt;tt class="docutils literal"&gt;build.bundles&lt;/tt&gt; array of your
&lt;tt class="docutils literal"&gt;aurelia.json&lt;/tt&gt; file.&lt;/p&gt;
&lt;p&gt;It works great but the first time I tried, my bundles were not filled
correctly. I tried to include files like this: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;quot;[site/**/*.js]&amp;quot;&lt;/span&gt;&lt;/tt&gt; but it
didn't work. I did some search and I found &lt;a class="reference external" href="https://github.com/aurelia/cli/issues/203#issuecomment-232845622"&gt;this comment&lt;/a&gt; by
&lt;a class="reference external" href="https://github.com/TylerJPresley"&gt;TylerJPresley&lt;/a&gt; on &lt;a class="reference external" href="https://github.com/aurelia/cli/issues/203"&gt;a issue about creating
multiple bundles with aurelia-cli&lt;/a&gt;
which gave me the solution: you have to put stars in front of the file
name. Like this: &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;quot;[**/site/**/*.js]&amp;quot;&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;site-bundle.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;source&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/site/**/*.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/site/**/*.{css,html}&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;With that, my bundles were correctly created and filled but they were not
loaded. However, it turns out that I was missing some files in them: I did put
the site and game files but not &lt;tt class="docutils literal"&gt;app.js&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;environment.js&lt;/tt&gt; and
&lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt;. As you might expect, without these files Aurelia could not work
correctly. So I create a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;common-bundle.js&lt;/span&gt;&lt;/tt&gt; for them and other files needed in
the whole application:&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;common-bundle.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;source&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/locale/**/*.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/app.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/environment.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/main.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/services/options.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/services/storage.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/services/browser-sniffer.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/widgets/aot-options/*.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/app.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/widgets/aot-options/*.html&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/widgets/aot-options/*.css&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/style/*.css&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;In addition to bundling your app files, you can also include non Aurelia
dependencies from outside your project if they rely on &lt;a class="reference external" href="http://requirejs.org/docs/whyamd.html"&gt;AMD&lt;/a&gt;. For instance, here's how I define my
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;game-create-bundle.js&lt;/span&gt;&lt;/tt&gt; with &lt;a class="reference external" href="https://clipboardjs.com/"&gt;clipboardjs&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;game-create-bundle.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;dependencies&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;clipboard&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../node_modules/clipboard/dist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;clipboard&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;source&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;[**/game/create/**/*.js]&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
       &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;**/game/create/**/*.{css,html}&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can also prepend javascript files to a bundle. That's how requirejs and
bluebird are loaded. Here's the relevant excerpt from the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;vendor-bundle.js&lt;/span&gt;&lt;/tt&gt;
(defined &lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/src/456d0578c1918bd9c7d9a3cd922f79ad99f1e49d/aurelia_project/aurelia.json?fileviewer=file-view-default#aurelia.json-148"&gt;here&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="nt"&gt;&amp;quot;prepend&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;node_modules/bluebird/js/browser/bluebird.js&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;scripts/require.js&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;At last but not least, some Aurelia modules contains multiple files that must be
resolved. To bundle those correctly, you need to use an object instead of the
name of the module in you &lt;tt class="docutils literal"&gt;dependencies&lt;/tt&gt; array. Like this:&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="nt"&gt;&amp;quot;dependencies&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-binding&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;i18next&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;path&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../node_modules/i18next/dist/commonjs&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;index&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can view the full definition of my &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;vendor-bundle.js&lt;/span&gt;&lt;/tt&gt; where this example
is taken &lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/src/456d0578c1918bd9c7d9a3cd922f79ad99f1e49d/aurelia_project/aurelia.json?fileviewer=file-view-default#aurelia.json-148"&gt;here&lt;/a&gt;
and the definition of all my bundles &lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/src/456d0578c1918bd9c7d9a3cd922f79ad99f1e49d/aurelia_project/aurelia.json?fileviewer=file-view-default#aurelia.json-83"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All in all, I think that bundling with aurelia-cli is very powerful, works well
and, once you know for the double stars, is quite easy to setup with the help of
a new project to give you the base configuration.&lt;/p&gt;
&lt;p&gt;In the end, I have 6 bundles for the application:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;vendor-bundle.js&lt;/span&gt;&lt;/tt&gt; with requirejs, &lt;a class="reference external" href="https://www.npmjs.com/package/bluebird"&gt;bluebird.js&lt;/a&gt; a promise library and all Aurelia
related files.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;common-bundle.js&lt;/span&gt;&lt;/tt&gt; that contains the translations, &lt;tt class="docutils literal"&gt;app.js&lt;/tt&gt;, &lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;environement.js&lt;/tt&gt; and various services and widgets common to the site and
the game.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;site-bundle.js&lt;/span&gt;&lt;/tt&gt; that contains all pages and widgets for the sites.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;game-common-bundle.js&lt;/span&gt;&lt;/tt&gt; that contains files required to create and play the
game.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;game-create-bundle.js&lt;/span&gt;&lt;/tt&gt; that contains all the widgets to create the game.&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;game-play-bundle.js&lt;/span&gt;&lt;/tt&gt; that contains all you need to actually play the game.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="problems-encountered-with-bundles-and-their-solutions"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-6"&gt;Problems encountered with bundles and their solutions&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;aurelia-cli loads its bundles with &lt;a class="reference external" href="https://requirejs.org"&gt;requirejs&lt;/a&gt; which
I find great. Until during a test session a friend reported that he was not
redirected to the game page when he first tried to create a game. The second
time he tried everything worked fine.&lt;/p&gt;
&lt;p&gt;I dug into the problem and found out why it was failing. The bundle containing
the game is still big (about 1.3 megabytes). On some slow connection, it took
more than 7 seconds to load it. Which means that we encountered the default
timeout for requirejs and the app would consider the bundle could not be loaded
and thus didn't redirect the player to the page even after the script was
loaded. requirejs nicely logged an error with &lt;a class="reference external" href="http://requirejs.org/docs/errors.html#timeout"&gt;this link&lt;/a&gt; in the console.&lt;/p&gt;
&lt;p&gt;The solution is to use the &lt;tt class="docutils literal"&gt;waitSeconds&lt;/tt&gt; option of requirejs to increase the
value of the timeout (you can also disable it &lt;a class="reference external" href="http://requirejs.org/docs/api.html#config-waitSeconds"&gt;as the documentation&lt;/a&gt; says). I did some
tests directly in the generated bundle and it work.&lt;/p&gt;
&lt;p&gt;The problem is that at the time of this writing, aurelia-cli doesn't allow you
to give custom options to the loader. So, I made a &lt;a class="reference external" href="https://github.com/aurelia/cli/pull/282"&gt;pull request to allow the
user to do just that&lt;/a&gt; in order to
correctly solve my problem while still using the cli. I hope it will be merged
soon.&lt;/p&gt;
&lt;p&gt;In order to further improve load time on these slow connection, I preload the
big &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;game-play-bundle.js&lt;/span&gt;&lt;/tt&gt; with the board as soon as a user reaches the create
game page. To do that, I added the line below in the constructor of the create
game page:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'game/play/widgets/board/board'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You must use this syntax to load the script asynchronously. If you use:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'game/play/widgets/board/board'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;the script will be loaded synchronously which means the user can't prepare the
game until it is completely loaded. That is of course not my goal.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="tests"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-7"&gt;Tests&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First, since the code is not in the &lt;tt class="docutils literal"&gt;src&lt;/tt&gt; folder but in &lt;tt class="docutils literal"&gt;app&lt;/tt&gt;, I had to
correct &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-karma.js&lt;/span&gt;&lt;/tt&gt; (see &lt;a class="reference internal" href="#preparation"&gt;Preparation&lt;/a&gt; for where it comes from) the
file that boostrap karma for usage with aurelia-cli. I had to correct this line
(&lt;a class="reference external" href="https://github.com/aurelia/cli/blob/master/lib/resources/test/aurelia-karma.js#L52"&gt;52&lt;/a&gt;
at the time of this writing):&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;originalDefine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/base/src/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;into:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;originalDefine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/base/app/'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I think it would be better for aurelia-karma to use &lt;tt class="docutils literal"&gt;paths.root&lt;/tt&gt; for
&lt;tt class="docutils literal"&gt;aurelia.json&lt;/tt&gt; (more general). I gave it a try but didn't succeed. But there
may be other ways to do this. See &lt;a class="reference external" href="https://github.com/aurelia/cli/issues/246"&gt;my issue on the subject for more details
paths.root from aurelia.json&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my test files, I had to change some import paths, remove my &lt;tt class="docutils literal"&gt;import
&lt;span class="pre"&gt;../setup&lt;/span&gt;&lt;/tt&gt; since it is automatically loaded by &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-karma&lt;/span&gt;&lt;/tt&gt;. The
&lt;tt class="docutils literal"&gt;test/unit/setup.js&lt;/tt&gt; has the &lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/src/9d3b2546251f532c4713a6e3f95db288a175dac8/test/unit/setup.js?at=master&amp;amp;fileviewer=file-view-default"&gt;same content as before&lt;/a&gt;
to import ployfills and initialize Aurelia:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-polyfills'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-pal-browser'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I also had to move my &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;test-utils.js&lt;/span&gt;&lt;/tt&gt; which contains various stubs I use in my
unit tests from the &lt;tt class="docutils literal"&gt;test/unit&lt;/tt&gt; folder into the &lt;tt class="docutils literal"&gt;app&lt;/tt&gt; folder so it is
correctly transpiled by babel. I also created a &lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/src/456d0578c1918bd9c7d9a3cd922f79ad99f1e49d/aurelia_project/aurelia.json?fileviewer=file-view-default#aurelia.json-218"&gt;dedicated bundle&lt;/a&gt;
named &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;test-utils-bundle.js&lt;/span&gt;&lt;/tt&gt; to avoid loading it in the true application.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-8"&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is it! Converting the project wasn't very hard but some problems (absolute
loading of bundles, some problems with testing and the inability to configure
requirejs) required some time to make it work correctly. Now I think that the
project is ready for the future of Aurelia tooling and I don't think I'll need
to switch tools again.&lt;/p&gt;
&lt;p&gt;If you want all the gory details, take a look at &lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/commits/456d0578c1918bd9c7d9a3cd922f79ad99f1e49d"&gt;this merge commit&lt;/a&gt;
and the commits before it in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-cli&lt;/span&gt;&lt;/tt&gt; branch.&lt;/p&gt;
&lt;p&gt;Otherwise, you also take a look at my &lt;tt class="docutils literal"&gt;aurelia.json&lt;/tt&gt; file &lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans/src/456d0578c1918bd9c7d9a3cd922f79ad99f1e49d/aurelia_project/aurelia.json?fileviewer=file-view-default"&gt;here&lt;/a&gt;,
browse the complete code of the app &lt;a class="reference external" href="https://bitbucket.org/arenaoftitans/arena-of-titans"&gt;there&lt;/a&gt; and see it in action
&lt;a class="reference external" href="http://www.arenaoftitans.com/"&gt;on the website&lt;/a&gt; (click play to create a game,
or use &lt;a class="reference external" href="http://www.arenaoftitans.com/game/create"&gt;this link&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;If you have a remark or question about this article or the game, leave a comment
below or contact me &lt;a class="reference external" href="https://twitter.com/jenselme_"&gt;on twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Next step on my agenda, setup code coverage of the original sources of the app
(and not the bundle, that would be too easy). I hope I'll be able to make it
work and write about it soon. Stay tuned!&lt;/p&gt;
&lt;/div&gt;
</content><category term="Aurelia"></category><category term="aurelia"></category><category term="aurelia-cli"></category></entry><entry><title>Plugin for statistics with Piwik on an Aurelia SPA</title><link href="https://www.jujens.eu/posts/en/2016/Jun/05/aurelia-piwik/" rel="alternate"></link><published>2016-06-05T00:00:00+02:00</published><updated>2016-06-05T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2016-06-05:/posts/en/2016/Jun/05/aurelia-piwik/</id><summary type="html">&lt;p&gt;On &lt;a class="reference external" href="http://www.arenaoftitans.com/"&gt;Arena of Titans&lt;/a&gt; a game based on the
&lt;a class="reference external" href="http://aurelia.io"&gt;Aurelia&lt;/a&gt; web framework I am developing with a friend, we
use &lt;a class="reference external" href="https://piwik.org"&gt;Piwik&lt;/a&gt; to have statistics about our visitors. When we
were about to launch the game, I realized that Piwik was not logging all the
pages but only the pages on …&lt;/p&gt;</summary><content type="html">&lt;p&gt;On &lt;a class="reference external" href="http://www.arenaoftitans.com/"&gt;Arena of Titans&lt;/a&gt; a game based on the
&lt;a class="reference external" href="http://aurelia.io"&gt;Aurelia&lt;/a&gt; web framework I am developing with a friend, we
use &lt;a class="reference external" href="https://piwik.org"&gt;Piwik&lt;/a&gt; to have statistics about our visitors. When we
were about to launch the game, I realized that Piwik was not logging all the
pages but only the pages on which you entered. It makes perfect sense:
navigation on the site is handled by Aurelia which means there is no page load
and if there is no page load, Piwik doesn't load the navigation.&lt;/p&gt;
&lt;p&gt;To go around this problem, I created a small plugin for Aurelia
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-piwik&lt;/span&gt;&lt;/tt&gt;. What it does is straightforward: it register a callback for
the &lt;tt class="docutils literal"&gt;router:navigation:success&lt;/tt&gt; event which is fired by Aurelia's router on
successful navigation. In this callback, I use Piwik's API to log the
navigation.&lt;/p&gt;
&lt;p&gt;You can easily use this plugin once installed by registering it in your
&lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;standardConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;developmentLogging&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-piwik'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I must say I am impressed by how easy it was to implement and use. After nearly
seven months of Aurelia usage, I am really glad to use this framework. If you
haven't checked it out yet, please do!&lt;/p&gt;
&lt;p&gt;The source are available under the MIT license &lt;a class="reference external" href="https://github.com/Jenselme/aurelia-piwik"&gt;on GitHub&lt;/a&gt;. The package is also available on
&lt;a class="reference external" href="https://www.npmjs.com/package/aurelia-piwik"&gt;npm&lt;/a&gt;.&lt;/p&gt;
</content><category term="Aurelia"></category><category term="aurelia"></category><category term="Piwik"></category></entry><entry><title>Resolving blank page problem of Aurelia app with webpack on Safari</title><link href="https://www.jujens.eu/posts/en/2016/Jun/03/safari-aurelia-i18n/" rel="alternate"></link><published>2016-06-03T00:00:00+02:00</published><updated>2016-06-03T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2016-06-03:/posts/en/2016/Jun/03/safari-aurelia-i18n/</id><summary type="html">&lt;p&gt;While asking users to test an Aurelia application, it appeared that it didn't
work on Safari due to this error:&lt;/p&gt;
&lt;img alt="Error in Safari console: Unhandled rejection webpackContextResolve" src="/images/safari-aurelia-i18n.png" /&gt;
&lt;p&gt;It comes from the fact that Safari doesn't support the internationalization
(window.Intl) API.&lt;/p&gt;
&lt;p&gt;In order to solve it, I had to manually include the &lt;a class="reference external" href="https://github.com/andyearnshaw/Intl.js/"&gt;Intl.js&lt;/a&gt;
polyfill. Since it …&lt;/p&gt;</summary><content type="html">&lt;p&gt;While asking users to test an Aurelia application, it appeared that it didn't
work on Safari due to this error:&lt;/p&gt;
&lt;img alt="Error in Safari console: Unhandled rejection webpackContextResolve" src="/images/safari-aurelia-i18n.png" /&gt;
&lt;p&gt;It comes from the fact that Safari doesn't support the internationalization
(window.Intl) API.&lt;/p&gt;
&lt;p&gt;In order to solve it, I had to manually include the &lt;a class="reference external" href="https://github.com/andyearnshaw/Intl.js/"&gt;Intl.js&lt;/a&gt;
polyfill. Since it is already pulled as a dependency, I just had to adapt the
bootstrap function of Aurelia with the setup code for Webpack of Intl.js:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Intl not present'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ensure&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="s1"&gt;'intl'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="s1"&gt;'intl/locale-data/jsonp/en.js'&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'intl'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'intl/locale-data/jsonp/en.js'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="nx"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;


 &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;standardConfiguration&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;developmentLogging&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'aurelia-i18n'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'-'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;

             &lt;/span&gt;&lt;span class="c1"&gt;// register backend plugin&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;i18next&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;XHR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;

             &lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;loadLocales&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                     &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
                         &lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enTranslations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'200'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
                         &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
                     &lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'fr'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
                         &lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frTranslations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'200'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
                         &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
                     &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
                         &lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'404'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
                         &lt;/span&gt;&lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

             &lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="nx"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                     &lt;/span&gt;&lt;span class="nx"&gt;loadPath&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{{lng}}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                     &lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                     &lt;/span&gt;&lt;span class="nx"&gt;ajax&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;loadLocales&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="nx"&gt;lng&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'t'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'i18n'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="nx"&gt;fallbackLng&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'en'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;

     &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;aurelia&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
</content><category term="Aurelia"></category><category term="aurelia"></category><category term="aurelia-i18n"></category><category term="i18n"></category><category term="Intl"></category><category term="Safari"></category><category term="Webpack"></category></entry><entry><title>How I switched an Aurelia application to webpack</title><link href="https://www.jujens.eu/posts/en/2016/Apr/24/switch-to-webpack/" rel="alternate"></link><published>2016-04-24T00:00:00+02:00</published><updated>2016-08-01T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2016-04-24:/posts/en/2016/Apr/24/switch-to-webpack/</id><summary type="html">&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I wrote this post when &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-webpack&lt;/span&gt;&lt;/tt&gt; wast still in pre 1.0.0 release. As &lt;a class="reference external" href="http://www.jujens.eu/posts/en/2016/Apr/24/switch-to-webpack/#isso-74"&gt;niieani pointed out in the comments&lt;/a&gt; part of it may be outdated and you way want to use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-webpack&lt;/span&gt;&lt;/tt&gt; with &lt;a class="reference external" href="https://github.com/easy-webpack/core"&gt;easy-webpack&lt;/a&gt; instead. The general ideas are still valid though.&lt;/p&gt;
&lt;p&gt;First thing first: why would …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I wrote this post when &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-webpack&lt;/span&gt;&lt;/tt&gt; wast still in pre 1.0.0 release. As &lt;a class="reference external" href="http://www.jujens.eu/posts/en/2016/Apr/24/switch-to-webpack/#isso-74"&gt;niieani pointed out in the comments&lt;/a&gt; part of it may be outdated and you way want to use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-webpack&lt;/span&gt;&lt;/tt&gt; with &lt;a class="reference external" href="https://github.com/easy-webpack/core"&gt;easy-webpack&lt;/a&gt; instead. The general ideas are still valid though.&lt;/p&gt;
&lt;p&gt;First thing first: why would I want to use &lt;a class="reference external" href="https://webpack.github.io/"&gt;webpack&lt;/a&gt; with &lt;a class="reference external" href="aurelia.io"&gt;Aurelia&lt;/a&gt; instead of the
default JSPM?&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;I was tired of JSPM which I find buggy&lt;/li&gt;
&lt;li&gt;No need for gulp anymore&lt;/li&gt;
&lt;li&gt;All deps are in the &lt;tt class="docutils literal"&gt;package.json&lt;/tt&gt; file and &lt;tt class="docutils literal"&gt;node_modules&lt;/tt&gt; folder&lt;/li&gt;
&lt;li&gt;webpack can build all kind of files (JS, TS, CSS, JSON, …) and perform
optimization on them&lt;/li&gt;
&lt;li&gt;Awesome dev server with hot reload.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you are interested in the subject, you can read &lt;a class="reference external" href="http://ilikekillnerds.com/2016/03/ditching-jspmsystem-js-webpack/"&gt;this article from Dwayne
Charrington&lt;/a&gt; which
gives more details on why you may want to use webpack.&lt;/p&gt;
&lt;p&gt;Here are the steps I followed. I did them nearly a month back but haven't found
the time to write this article before today, so it is based on my small notes
and memories. I apologize if this article is not as precise as it could be. If
you have a specific question or run into trouble, please leave a
comment. Anyway, the thing to remember is that the migration went well and
webpack works very well with Aurelia.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Update &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt;. Copy/Pasting the &lt;a class="reference external" href="https://github.com/aurelia/skeleton-navigation/blob/master/skeleton-es2016-webpack/index.html"&gt;index.html&lt;/a&gt;
from the skeleton with little adaptation should do the trick. You will also
need a &lt;tt class="docutils literal"&gt;index.prod.html&lt;/tt&gt; file for use in production. Again, &lt;a class="reference external" href="https://github.com/aurelia/skeleton-navigation/blob/master/skeleton-es2016-webpack/index.prod.html"&gt;the skeleton
has what you need&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Update dependencies in &lt;tt class="docutils literal"&gt;package.json&lt;/tt&gt;. Adapt &lt;a class="reference external" href="https://github.com/aurelia/skeleton-navigation/blob/master/skeleton-es2016-webpack/package.json"&gt;the one from the skeleton&lt;/a&gt;. You
may need to add dependencies beside webpack specific ones (specified in the
skeleton) to get your app working. For instance, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;aurelia-logging-console&lt;/span&gt;&lt;/tt&gt;
was not explicitly in my &lt;tt class="docutils literal"&gt;package.json&lt;/tt&gt; (not even under the &lt;tt class="docutils literal"&gt;jspm&lt;/tt&gt;
key). You can also get rid of the &lt;tt class="docutils literal"&gt;jspm&lt;/tt&gt; key. In my case, Aurelia will give
you a nice debug message if you are lacking a required dependency.&lt;/li&gt;
&lt;li&gt;Update configuration of Karma. See &lt;a class="reference external" href="https://github.com/aurelia/skeleton-navigation/blob/master/skeleton-es2016-webpack/karma.conf.js"&gt;karma.conf in the skeleton&lt;/a&gt;. For
your tests to work, you are very likely to need to bootstrap your tests by
adding to each file (you can also put that in a &lt;tt class="docutils literal"&gt;setup.js&lt;/tt&gt; file and import
it in your test files):&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="code js literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-pal-browser'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/pre&gt;
&lt;ol class="arabic simple" start="4"&gt;
&lt;li&gt;Update &lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt; (create it if you don't have one). See &lt;a class="reference external" href="https://github.com/aurelia/skeleton-navigation/blob/master/skeleton-es2016-webpack/src/main.js"&gt;the skeleton for
inspiration&lt;/a&gt;. &lt;strong&gt;Important
note&lt;/strong&gt;: if you want you style files to be loaded with webpack, they must be
imported in a JS file. &lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt; can be a good place to place these import.&lt;/li&gt;
&lt;li&gt;Copy &lt;a class="reference external" href="https://github.com/aurelia/skeleton-navigation/blob/master/skeleton-es2016-webpack/webpack.config.js"&gt;webpack.config.js&lt;/a&gt;
and &lt;a class="reference external" href="https://github.com/aurelia/skeleton-navigation/blob/master/skeleton-es2016-webpack/webpack.prod.config.js"&gt;webpack.prod.config.js&lt;/a&gt;
and adapt them if need be.&lt;/li&gt;
&lt;li&gt;Remove JSPM files: &lt;tt class="docutils literal"&gt;rm &lt;span class="pre"&gt;-rf&lt;/span&gt; jspm_packages config.js&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Remove unneeded node modules (eg jspm) &lt;tt class="docutils literal"&gt;rm &lt;span class="pre"&gt;-rf&lt;/span&gt; node_modules&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Remove gulp related files: &lt;tt class="docutils literal"&gt;rm &lt;span class="pre"&gt;-rf&lt;/span&gt; gulpfile.js build&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Install new dependencies: &lt;tt class="docutils literal"&gt;npm install&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Take care of submodules &lt;strong&gt;Not needed with aurelia-webpack 1.0.0&lt;/strong&gt;: if you see an error like this:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="literal-block"&gt;
.*$:106 Uncaught (in promise) Error: Cannot find module './aurelia-i18n/t'.
.*$:106 Uncaught (in promise) Error: Cannot find module './aurelia-i18n/nf'.
.*$:106 Uncaught (in promise) Error: Cannot find module './aurelia-i18n/df'.
Uncaught (in promise) Error: Cannot find module './aurelia-i18n/rt'.
&lt;/pre&gt;
&lt;p&gt;You need to enable sub-modules for this modules, with something like this:&lt;/p&gt;
&lt;pre class="code js literal-block"&gt;
&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AureliaWebpackPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;includeSubModules&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;moduleId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;aurelia-i18n&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;See the &lt;a class="reference external" href="https://github.com/aurelia/webpack-plugin/#configuration-options"&gt;documentation of the webpack plugin for more information&lt;/a&gt; on that.&lt;/p&gt;
&lt;p&gt;Normally you are ready to launch
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;./node_modules/.bin/webpack-dev-server&lt;/span&gt; &lt;span class="pre"&gt;--config&lt;/span&gt;
webpack.config.js &lt;span class="pre"&gt;--hot&lt;/span&gt; &lt;span class="pre"&gt;--inline&lt;/span&gt; &lt;span class="pre"&gt;--progress&lt;/span&gt; &lt;span class="pre"&gt;--devtool&lt;/span&gt; eval&lt;/tt&gt; and start
developing with Aurelia and webpack.&lt;/p&gt;
</content><category term="Aurelia"></category><category term="aurelia"></category><category term="webpack"></category></entry><entry><title>Small comparison of ionic2 and Aurelia + Framework7 for hybrid mobile applications</title><link href="https://www.jujens.eu/posts/en/2016/Mar/15/ionic2-aurelia-f7/" rel="alternate"></link><published>2016-03-15T00:00:00+01:00</published><updated>2017-08-14T00:00:00+02:00</updated><author><name>Julien Enselme</name></author><id>tag:www.jujens.eu,2016-03-15:/posts/en/2016/Mar/15/ionic2-aurelia-f7/</id><summary type="html">&lt;p&gt;&lt;strong&gt;Update (2017-08-14):&lt;/strong&gt; I finally published the part on Aurelia UX. See it &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2017/Aug/14/aurelia-ux/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Updates (2017-01-24):&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;aurelia-interface has been deprecated in favour of &lt;a class="reference external" href="https://github.com/aurelia/ux"&gt;aurelia-ux&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Add a link to &lt;a class="reference external" href="/posts/en/2017/Jan/24/aurelia-validation/"&gt;my article&lt;/a&gt; about &lt;a class="reference external" href="http://aurelia.io/hub.html#/doc/article/aurelia/validation/latest/validation-basics"&gt;aurelia-validation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I don't think I'll start a project to use Aurelia with Framework7. I want to test aurelia-ux first. However …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;&lt;strong&gt;Update (2017-08-14):&lt;/strong&gt; I finally published the part on Aurelia UX. See it &lt;a class="reference external" href="https://www.jujens.eu/posts/en/2017/Aug/14/aurelia-ux/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Updates (2017-01-24):&lt;/strong&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;aurelia-interface has been deprecated in favour of &lt;a class="reference external" href="https://github.com/aurelia/ux"&gt;aurelia-ux&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Add a link to &lt;a class="reference external" href="/posts/en/2017/Jan/24/aurelia-validation/"&gt;my article&lt;/a&gt; about &lt;a class="reference external" href="http://aurelia.io/hub.html#/doc/article/aurelia/validation/latest/validation-basics"&gt;aurelia-validation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;I don't think I'll start a project to use Aurelia with Framework7. I want to test aurelia-ux first. However, someone is starting one: &lt;a class="reference external" href="https://github.com/alflennik/au7"&gt;https://github.com/alflennik/au7&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#application-features" id="toc-entry-1"&gt;Application features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#ionic2" id="toc-entry-2"&gt;Ionic2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#aurelia-and-framework7" id="toc-entry-3"&gt;Aurelia and Framework7&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="#bonus" id="toc-entry-4"&gt;Bonus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#conclusion" id="toc-entry-5"&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;I spent some time recently to develop a mobile application with the &lt;a class="reference external" href="http://ionicframework.com/"&gt;ionic
framework&lt;/a&gt;. This framework allows you to create
mobile applications for Android and iOS with HTML5 and CSS3 and &lt;a class="reference external" href="angularjs.org"&gt;AngularJS&lt;/a&gt;. Which means you can develop native like applications with
the ease of development of a web application: debug in a browser, use Javascript
and a framework you probably already know and that's it! No java or Swift, just
the power of the web. The application is then build for your phone with &lt;a class="reference external" href="https://cordova.apache.org"&gt;Apache
Cordova&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since they have a version 2 coming soon based on &lt;a class="reference external" href="https://angular.io/"&gt;Angular2&lt;/a&gt;, I decided to use directly this version (currently in
beta).&lt;/p&gt;
&lt;p&gt;However, on a another project, I use &lt;a class="reference external" href="aurelia.io"&gt;Aurelia&lt;/a&gt; a JavaScript
framework built with ES6 and around web standards aimed to create Single Page
Applications easily. And I must say I love it. It embraces ES6 and the new
possibilities offered by the language and its use of convention over
configuration makes it easy to work with.&lt;/p&gt;
&lt;p&gt;So I wanted to try to build a mobile application with it. There are plans for a
commercial addon called &lt;a class="reference external" href="https://github.com/joelcoxokc/aurelia-interface"&gt;Aurelia Interface&lt;/a&gt;. Sadly there is not much
information about it available apart from &lt;a class="reference external" href="https://vimeo.com/146402788"&gt;that video&lt;/a&gt;. After a small discussion on &lt;a class="reference external" href="https://gitter.im/Aurelia/Discuss"&gt;Aurelia's gitter&lt;/a&gt;, &lt;a class="reference external" href="https://gitter.im/Aurelia/Discuss?at=56e2cf603194fbd110962c9e"&gt;Scalpal suggested&lt;/a&gt; to look at
&lt;a class="reference external" href="http://framework7.io/"&gt;Framework7&lt;/a&gt; an HTML/CSS (with small bits of
JavaScript) framework to do pretty much the same thing as ionic without the
dependency on a particular framework. That's what I did and it turned out to
work well with Aurelia.&lt;/p&gt;
&lt;p&gt;Let's test!&lt;/p&gt;
&lt;div class="section" id="application-features"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-1"&gt;Application features&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To compare the two possibilities, I wanted to do a basic todo list applications
that can:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;List all the saved todos (home page)&lt;/li&gt;
&lt;li&gt;Edit an existing todo&lt;/li&gt;
&lt;li&gt;Add a todo&lt;/li&gt;
&lt;li&gt;Mark a todo as done on the home page. Done todos must have their title
strike-through on the home page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All todos must be stored in the permanent SqlStorage of the phone.&lt;/p&gt;
&lt;p&gt;In additions to these features, both applications must work on a browser (for
debug and development purposes), on an android emulator and on an android phone.&lt;/p&gt;
&lt;p&gt;Here is how the home page looks like (Aurelia + Framework7 on the left, Ionic2 +
Angular2 on the right):&lt;/p&gt;
&lt;img alt="main page of the application" src="/images/ionic2-aurelia-f7/todos-list.png" style="width: 100%;" /&gt;
&lt;p&gt;And the todo page (Aurelia + Framework7 on the left, Ionic2 + Angular2 on the
right):&lt;/p&gt;
&lt;img alt="Page to view and edit todo" src="/images/ionic2-aurelia-f7/todo-view.png" style="width: 100%;" /&gt;
&lt;p&gt;The complete code of the applications is available &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7"&gt;on github&lt;/a&gt; under the
MIT license. Normally, the READMEs are complete enough to let you try the
applications. If you encounter any problem, leave a comment or open an issue!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ionic2"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-2"&gt;Ionic2&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I wrote this version in &lt;a class="reference external" href="http://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; a
language made by Microsoft to ease the development of huge frontend applications
and not ES6 since it is the recommended way for Angular2 applications (Angular2
is itself written in TypeScript).&lt;/p&gt;
&lt;p&gt;To organize the application, I followed the standard structure proposed by
ionic:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;An &lt;tt class="docutils literal"&gt;app&lt;/tt&gt; folder with the code. In this app folder:&lt;ul&gt;
&lt;li&gt;each pages (JS, HTML and eventually SASS) are grouped in a folder within
&lt;tt class="docutils literal"&gt;pages&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;Global theme SASS files are in the &lt;tt class="docutils literal"&gt;theme&lt;/tt&gt; folder.&lt;/li&gt;
&lt;li&gt;Boostrap (app.ts) and utils (models.ts) are directly in &lt;tt class="docutils literal"&gt;app&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;tt class="docutils literal"&gt;build&lt;/tt&gt; folder used for debugging and by Cordova to generate the final
application. It contains the &lt;tt class="docutils literal"&gt;index.html&lt;/tt&gt; generated by ionic and will
contain all the built files.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First, let's talk of &lt;tt class="docutils literal"&gt;models.ts&lt;/tt&gt;. It only contains one class and is used in
the application as a type for the TypeScript compiler (and completion).&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;In &lt;tt class="docutils literal"&gt;app.ts&lt;/tt&gt; the application is bootstrapped. It is generated by ionic. I only
changed the value of &lt;tt class="docutils literal"&gt;MyApp#rootPage&lt;/tt&gt; so its value is &lt;tt class="docutils literal"&gt;ListTodosPage&lt;/tt&gt; the
JavaScript class that defines my root page.&lt;/p&gt;
&lt;p&gt;That leads us to the first important concept of Ionic2: navigation doesn't work
with URLs. As you'll see if you launch the app, the URL is never updated. To
navigate between pages, you use a &lt;tt class="docutils literal"&gt;NavController&lt;/tt&gt; object and pass the page on
which you want to go, like that:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TodoPage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;A page being a class decorated with &lt;tt class="docutils literal"&gt;&amp;#64;Page&lt;/tt&gt; a decorator that comes from the
ionic framework.&lt;/p&gt;
&lt;p&gt;On the main page, we display the navbar and the list of all todos. To do that,
you use in your template the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ion-navbar&lt;/span&gt;&lt;/tt&gt; element. This allows you to easily
customize the navbar for each page. Ionic will also automatically add a &lt;em&gt;Back&lt;/em&gt;
button in this navbar when you navigate to another page.&lt;/p&gt;
&lt;p&gt;The main content of the page must be inside the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ion-content&lt;/span&gt;&lt;/tt&gt; element. Almost
all the elements you will put in it are defined by ionic to help you and starts
with &lt;em&gt;ion&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You can also add Angular2 markup:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;if the tag starts by a star (*), it's a template, like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;*ngFor=&amp;quot;#todo&lt;/span&gt; of
todos, #index = index&amp;quot;&lt;/tt&gt;. Note that the syntax is particular: to create a
variable in a template, you must put a pound sing (#) before the name of the
variable.&lt;/li&gt;
&lt;li&gt;if the tag is between parenthesis it is bound to an event like
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;(click)=&amp;quot;viewTodo(todo)&amp;quot;&lt;/span&gt;&lt;/tt&gt;. The value of the tag will be executed when the
event occurs.&lt;/li&gt;
&lt;li&gt;if the tag is between bracket like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;[class]=&amp;quot;getClass(todo)&amp;quot;&lt;/span&gt;&lt;/tt&gt; it means its
value is one way bound to the expression you pass.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-navbar&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="na"&gt;navbar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;TODO list&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-navbar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-content&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-list&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-item&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="na"&gt;ngFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#todo of todos, #index = index&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
               &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-label&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;getClass&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;)&amp;quot;&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;)=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;viewTodo&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="err"&gt;)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ todo.title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
               &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;item-right&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;)=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;markTodoDone&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="err"&gt;)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Toggle done&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-list&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;full&lt;/span&gt; &lt;span class="na"&gt;round&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;)=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;addTodo&lt;/span&gt;&lt;span class="err"&gt;()&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Add&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-content&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;On the TypeScript side, we have a class &lt;tt class="docutils literal"&gt;ListTodosPage&lt;/tt&gt; decorated by
&lt;tt class="docutils literal"&gt;&amp;#64;Page&lt;/tt&gt;. This decorator is only used here to defined the template used for the
page but it is also used to define which pipes (filters in Angular2 terminology)
and directives are available in the template.&lt;/p&gt;
&lt;p&gt;In the constructor, we create the SQL database if it doesn't exist and retrieve
the todos:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'CREATE TABLE IF NOT EXISTS todos(id integer primary key unique, title VARCHAR(10), description TEXT, done BOOLEAN)'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SELECT * FROM todos'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;To view a particular todo, we use the navigation controller with navigation
parameters:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TodoPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;To add a todo, we just omit the navigation parameter.&lt;/p&gt;
&lt;p&gt;We can then retrieve the todo on the &lt;tt class="docutils literal"&gt;TodoPage&lt;/tt&gt; like this:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NavController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;navParams&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;NavParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;navParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;navParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Once we have our todo (the one we want to edit or a new one), we can create the
form and bind its default value to the existing todo. This way, if we have an
existing todo, the from will be prefilled with the correct values.&lt;/p&gt;
&lt;p&gt;I must say that forms in Angular2 are a huge beast and I am not sure to fully
see the interest of such a complex system (at least in most cases). The
programming API and the template tags are powerful and allow you to create fine
grained validators to suit your needs but they are hard to understand at
first. You have a simple syntax which suffers from lack of functionality and a
powerful and verbose one (which I use here). Let's look at the code:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;Validators&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Control&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoForm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ControlGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'description'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todoForm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'done'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;First we defined the group of input we want. We give them a default value and
eventually the validator use to determine if the value is correct or not. Then
we create the form and extract from it the controls that will be used in the
template for validation.&lt;/p&gt;
&lt;p&gt;Now we can look at the template. I won't dig into it. If you want to learn more
about forms in Angular2, visit &lt;a class="reference external" href="http://learnangular2.com/forms/"&gt;this page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Just see how we use the controls to see if a field is correct or not and note
that we use the &lt;tt class="docutils literal"&gt;[ngFormModel]&lt;/tt&gt; tag to bind the form to the proper form
object.&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;ngFormModel&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;todoForm&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;ngSubmit&lt;/span&gt;&lt;span class="err"&gt;)=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;todoForm&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-item&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;!&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valid&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;touched&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-label&lt;/span&gt; &lt;span class="na"&gt;floating&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Title&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;ngFormControl&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-input&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="err"&gt;*&lt;/span&gt;&lt;span class="na"&gt;ngIf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;title.hasError('required') &amp;amp;&amp;amp; title.touched&amp;quot;&lt;/span&gt;
         &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;error-box&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
             * Title is required!
     &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-item&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;!&lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valid&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;touched&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-label&lt;/span&gt; &lt;span class="na"&gt;floating&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Description&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-textarea&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;ngFormControl&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-textarea&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-item&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;!&lt;/span&gt;&lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valid&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;touched&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Done&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ion-checkbox&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;ngFormControl&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-checkbox&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ion-item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;full&lt;/span&gt; &lt;span class="na"&gt;round&lt;/span&gt; &lt;span class="na"&gt;danger&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;reset&amp;quot;&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;)=&amp;quot;&lt;/span&gt;&lt;span class="na"&gt;cancel&lt;/span&gt;&lt;span class="err"&gt;()&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;full&lt;/span&gt; &lt;span class="na"&gt;round&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;submit&amp;quot;&lt;/span&gt; &lt;span class="err"&gt;[&lt;/span&gt;&lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="err"&gt;]=&amp;quot;!&lt;/span&gt;&lt;span class="na"&gt;todoForm&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;valid&lt;/span&gt;&lt;span class="err"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;We can then save the todo with an SQL query and parameters replacement:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s1"&gt;'UPDATE todos SET title = ?, description = ?, done = ? WHERE id = ?'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;todoValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todoValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todoValues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="aurelia-and-framework7"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-3"&gt;Aurelia and Framework7&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I decided to use &lt;a class="reference external" href="https://webpack.github.io/"&gt;webpack&lt;/a&gt; (an official skeleton
is &lt;a class="reference external" href="http://blog.durandal.io/2016/03/10/aurelia-loves-webpack/"&gt;available&lt;/a&gt;)
instead of the default SystemJS. Two reasons for that:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;It's a good way to test webpack support&lt;/li&gt;
&lt;li&gt;During my initial research I found that people had problems with SystemJS and
cordova. Since webpack is used by ionic2, I expected it to work well.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I followed the same structure than for ionic: each pages in
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;app/pages/&amp;lt;pagename&amp;gt;&lt;/span&gt;&lt;/tt&gt;, services in &lt;tt class="docutils literal"&gt;app/services&lt;/tt&gt; and bootstrap (&lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt;
and &lt;tt class="docutils literal"&gt;app.js&lt;/tt&gt; directly in the app folder.&lt;/p&gt;
&lt;p&gt;The HTML code in index.html and app.html is almost a copy/paste from the getting
started of Framework7. There is just one point in app.html that is different: in
the getting started, the code is design for iOS. To use the material design
stylesheet (like I did), replace &lt;tt class="docutils literal"&gt;&amp;lt;div &lt;span class="pre"&gt;class=&amp;quot;pages&lt;/span&gt; &lt;span class="pre"&gt;navbar-through&lt;/span&gt;
&lt;span class="pre"&gt;toolbar-through&amp;quot;&amp;gt;&lt;/span&gt;&lt;/tt&gt; by &lt;tt class="docutils literal"&gt;&amp;lt;div &lt;span class="pre"&gt;class=&amp;quot;pages&lt;/span&gt; &lt;span class="pre"&gt;navbar-fixed&lt;/span&gt;
&lt;span class="pre"&gt;toolbar-through&amp;quot;&amp;gt;&lt;/span&gt;&lt;/tt&gt;. Otherwise, the content of the page will partially be in the
navbar (due to a missing &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;padding-top:&lt;/span&gt; 46px&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;main.js&lt;/tt&gt; contains nothing particular, it's just the standard main provided by
the skeleton.&lt;/p&gt;
&lt;p&gt;Since navigation in the app will rely on the Aurelia router, we have to defines
our route in &lt;tt class="docutils literal"&gt;app.js&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;configureRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'Aurelia'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'todos'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'todos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;moduleId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'pages/list-todos/list-todos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'todos'&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'todo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'todo/:id'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'todo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;moduleId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'pages/todo/todo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'View Todo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&lt;span class="w"&gt;

     &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;And to navigate, I use either:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigateToRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'todo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;To view/edit a precise todo. This allows me to get the todo with the given id
from storage. To add a new todo, I use:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/todo'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;To navigate to the route without the parameter.&lt;/p&gt;
&lt;p&gt;Concerning the templates, nothing fancy. Just think to put your code in a div
with either the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;list-block&lt;/span&gt;&lt;/tt&gt; or the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;content-block&lt;/span&gt;&lt;/tt&gt; class depending on what
you want to do:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-content&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;repeat&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo of todos&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delegate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;viewTodo(todo)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-inner ${todo.done === true || todo.done === 'true' ? 'done' : ''}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
           ${todo.title}
       &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;button button-fill&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delegate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;toggleDone(todo)&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Toggle Done&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;We use Aurelia's binding mechanism to make the link with the JavaScript:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;We repeat the element with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;repeat.for=&amp;quot;todo&lt;/span&gt; of todos&amp;quot;&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Bind a click event to a function with &lt;tt class="docutils literal"&gt;click.delegate&lt;/tt&gt;&lt;/li&gt;
&lt;li&gt;Use string interpolation to display the values with &lt;tt class="docutils literal"&gt;${todo.title}&lt;/tt&gt; or to
update the value of a tag with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;class=&amp;quot;item-inner&lt;/span&gt; ${todo.done === true ||
todo.done === 'true' ? 'done' : &lt;span class="pre"&gt;''}&amp;quot;&lt;/span&gt;&lt;/tt&gt;. The part from &lt;tt class="docutils literal"&gt;${&lt;/tt&gt; to &lt;tt class="docutils literal"&gt;}&lt;/tt&gt; will be
replaced by the value of the expression computed by Aurelia.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main difference with the ionic app is that I used &lt;a class="reference external" href="https://github.com/localForage/localForage"&gt;localForage&lt;/a&gt; in a custom class to store the
todos. localForage is a small library by Mozilla that uses localStorage if neither IndexedDB nor WebSQL is supported with an API close the localStorage. With ionic, you have a simple way to use SQLite on the phone and
WebSQL on your browser with nice capabilities for SQL queries but that's a ionic
thing. Since behind the scenes, it relies on Cordova, it should be possible to
use it with Aurelia and Framework7 but it felt overkill for my small test.&lt;/p&gt;
&lt;p&gt;On the page to edit todos, nothing fancy. I create a nice looking form with the
good classes. The structure is a little bit complicated but is necessary to have
the proper theme. The rest is just classic html (here the code for one field):&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-content&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-inner&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-title label&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Title&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;item-input&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;text&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Title&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;todo.title&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Just note that in forms, Aurelia will do two way data bindings with
&lt;tt class="docutils literal"&gt;value.bind&lt;/tt&gt; whereas everywhere else it will do one way data bindings by
default for performance reasons.&lt;/p&gt;
&lt;p&gt;In the JavaScript, we just have to load the proper todo:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;activate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;To allow the todo to be saved, I do a basic check on the title (it must not be
empty). No message saying that the field is required if the user touches it is displayed. That's the
other import difference with ionic. It is due to a bug in &lt;a class="reference external" href="https://github.com/aurelia/validation/issues/226"&gt;aurelia-validation&lt;/a&gt; about how translations are
loaded with webpack. This is clearly behind what I did in ionic but I'll update
the code and this article once it is fixed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I created a new post about validation and Aurelia. The code samples used in it are based on this application You can see it &lt;a class="reference external" href="/posts/en/2017/Jan/24/aurelia-validation/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;canSave&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="section" id="bonus"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="#toc-entry-4"&gt;Bonus&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It's not useful for my application, but I wanted to show how to use Framework7's
JavaScript should you need it. In a javascript file (you can name it &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/master/aurelia-f7-todo/app/services/f7.js"&gt;f7.js&lt;/a&gt;)
put the code below:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="c1"&gt;// Load JS for webpack&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'framework7'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="c1"&gt;// Create F7 object&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;F7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Framework7&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;material&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You can then use it as you would expect:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;F7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'f7'&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'aurelia-framework'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;&amp;#64;&lt;/span&gt;&lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;F7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ListTodosPage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
     &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;f7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;f7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;And in your HTML:&lt;/p&gt;
&lt;pre class="code html literal-block"&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delegate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;f7.openPanel('left')&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;#&amp;quot;&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;button button-big button-fill&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Open Left Panel&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;To see a usage, go &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/master/aurelia-f7-todo/app/pages/list-todos/list-todos.js"&gt;here&lt;/a&gt;
for my code and &lt;a class="reference external" href="https://github.com/Jenselme/tests-ionic2-and-aurelia-framework7/blob/master/aurelia-f7-todo/app/pages/list-todos/list-todos.html"&gt;there&lt;/a&gt;
for the associated template.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="#toc-entry-5"&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Both application have roughly the same amount of code: 164 lines of JS and 40
lines of HTML for ionic and 143 lines of JS and 103 lines of HTML for
Aurelia/Framework7. Since my storage service is about 40 lines that are
handled by directly by ionic, I have about 100 lines of JS code for
Aurelia. There is more HTML but it's mostly the bootstrap required for
Framework7.&lt;/li&gt;
&lt;li&gt;I find Aurelia lighter and easier to use. Even if typescript has some nice
features like type error detection and a better auto-completion (since it can
be based on the type of the object), it looks Javaish with type declarations
and interfaces. In addition to that, the syntax for the template is weird (and
it breaks some HTML validators like the one integrated in NetBeans IDE). I've
used it for some days now, so I begin to get use to it but with my fist
experience with Aurelia I didn't have this incomfortable moments at first:
templating is plain and simple &lt;tt class="docutils literal"&gt;tag.bind&lt;/tt&gt; (default) or &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;tag.two-way&lt;/span&gt;&lt;/tt&gt; or
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;tag.one-way&lt;/span&gt;&lt;/tt&gt; depending on what you want to do, string interpolation works
everywhere and is just plain ES6 template strings.&lt;/li&gt;
&lt;li&gt;ionic uses a lot of custom elements and tags. I guess the goal is to make the
code less verbose, but sometime it is confusing. For instance, to make a text
input field, you use &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;ion-input&lt;/span&gt; &lt;span class="pre"&gt;type=&amp;quot;text&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/tt&gt;. So logically to make a
checkbox, I tried &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;ion-input&lt;/span&gt; &lt;span class="pre"&gt;type=&amp;quot;checkbox&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/tt&gt; (which doesn't work)
instead of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;&amp;lt;ion-checkbox&lt;/span&gt; &lt;span class="pre"&gt;[ngFormControl]=&amp;quot;done&amp;quot;&lt;/span&gt; /&amp;gt;&lt;/tt&gt;. On the
contrary, Framework7 is just plain HTML. You just have to respect a certain
structure (well detailed on the documentation) and put the proper classes on
your element. So it can integrate with pretty much everything and it's quite
easy to learn.&lt;/li&gt;
&lt;li&gt;ionic has some good bindings with Corodova. For instance its ability to use
WebSQL on your browser but SQLite on your phone. There's no easy way (yet?) to
do that with Aurelia/Framework7. Maybe Aurelia-Interface will have that too.&lt;/li&gt;
&lt;li&gt;the startup time of the application was quite slow (several seconds) with both
solutions. Maybe there is a way to improve that, I admit I haven't dug into it
yet.&lt;/li&gt;
&lt;li&gt;webpack is awesome ;-)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end, ionic has a good cli, documentation and some bindings for Cordova
which make it complete and interesting. But in my opinion, the
Aurelia/Framework7 feels more natural mostly because of the weird design choices of
Angular2 and ionic has lots of custom element over HTML.&lt;/p&gt;
&lt;p&gt;Maybe I should start something (skeleton and command line tool) to work with
Aurelia, Framework7 and Cordova. If there's no news of Aurelia-Interface soon, I
think it's highly probable that I'll try. I'll update this post if I do (or
decide not to).&lt;/p&gt;
&lt;/div&gt;
</content><category term="Aurelia"></category><category term="ionic2"></category><category term="angular2"></category><category term="aurelia"></category><category term="framework7"></category><category term="android"></category><category term="mobile"></category></entry></feed>