- front-end 2017-03-19T16:58:15+01:00 https://fadeit.dk/blog/tag/front-end.html CSS backdrop filters 2016-09-27T16:22:00+02:00 https://fadeit.dk/blog /2016/09/27/backdrop-(filter)-it-like-its-hot <p>There are many new CSS features to get excited about (<a href="http://css4.rocks/">awesome list here</a>?). Tons of selectors &amp; structural properties that I can’t wait to see implemented in all browsers. <br /></p> <p><small>Side note: OMG CSS grid 😱, amirite? Did you even see this:</small></p> <div style="width: 100%"> <blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">This is a very basic type of layout CSS Grid will let you create without any additional markup <a href="https://t.co/HGOd0RGnQ1">pic.twitter.com/HGOd0RGnQ1</a></p>&mdash; Wes Bos (@wesbos) <a href="https://twitter.com/wesbos/status/777955345146777600">September 19, 2016</a></blockquote> </div> <blockquote><p lang="en" dir="ltr">Here is was the CSS behind that looks like: <a href="https://t.co/BNiJnZ7zaX">pic.twitter.com/BNiJnZ7zaX</a></p>&mdash; Wes Bos (@wesbos) <a href="https://twitter.com/wesbos/status/777957127558082561">September 19, 2016</a></blockquote> <p>However, I’m the kind of person that gets excited about the small things, <code class="highlighter-rouge">backdrop-filter</code> included. As humble as it seems, it can enable a ton of design stunts for those that can wield it.</p> <h2 id="what-you-can-do-with-backdrop-filter">What you can do with backdrop-filter</h2> <p>For centuries, designers and developers have been trying to figure out how to adjust text color when placed on top of a background. If the background is light, the text has to be dark and vice-versa. <br /> Similar to centering elements before flexbox, many gave up and looked into other professions. Others hacked their way out of it with cheap tricks, such as text-shadow / box-shadow (I’m looking at you, Facebook!).</p> <p>It doesn’t have to be that way. Here’s what we can achieve with <code class="highlighter-rouge">backdrop-filter: inverse(1)</code>:</p> <p><span style="text-align: center; width: 100%; display: inline-block;"> <img src="/blog/assets/backdrop-(filter)-it-like-its-hot/css-backdrop-trick.gif" alt="Backdrop filter trick" /> </span></p> <p>Contrary to other solutions, this one is independent from the text itself. It’s also flexible IMO &amp; it can be used for other use cases too.<br /> There’s a small trick to it. To apply the <code class="highlighter-rouge">inverse</code> filter only on the text and prevent messing with the background, the background can be inverted again (<code class="highlighter-rouge">filter</code> can be used for that). Here’s what I mean:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="c">/* has to have z-index &gt; 'before' &amp; &lt; 'after' */</span> <span class="nt">p</span> <span class="p">{</span> <span class="c">/* ... */</span> <span class="p">}</span> <span class="c">/* the background, has to have z-index &lt; 'p' */</span> <span class="nt">section</span><span class="nd">:before</span><span class="p">{</span> <span class="nl">background-color</span><span class="p">:</span> <span class="no">green</span><span class="p">;</span> <span class="nl">filter</span><span class="p">:</span> <span class="nb">invert</span><span class="p">(</span><span class="m">1</span><span class="p">);</span> <span class="c">/* ... */</span> <span class="p">}</span> <span class="c">/* has to have z-index &gt; 'p' */</span> <span class="nt">section</span><span class="nd">:after</span><span class="p">{</span> <span class="py">backdrop-filter</span><span class="p">:</span> <span class="nb">invert</span><span class="p">(</span><span class="m">1</span><span class="p">);</span> <span class="c">/* ... */</span> <span class="p">}</span> </code></pre> </div> <p>The full code for it is available below.</p> <div style="width: 100%;"> <p data-height="265" data-theme-id="0" data-slug-hash="XjRoZz" data-default-tab="css,result" data-user="danmindru" data-embed-version="2" class="codepen">See the Pen <a href="https://codepen.io/danmindru/pen/XjRoZz/">CSS filter to invert text color</a> by Dan Mindru (<a href="http://codepen.io/danmindru">@danmindru</a>) on <a href="http://codepen.io">CodePen</a>.</p> </div> <p><br /> Unless you are on <strong>Safari 9+</strong>, you’ll see the inverted background (pink) and nothing else. <br /> Browser support <a href="http://caniuse.com/#feat=css-backdrop-filter">is limited</a> at the moment. <br /> <strong>Update</strong>: you can also make it work in the current Chrome build by enabling the <code class="highlighter-rouge">'Experimental Web Platform features Mac, Windows, Linux, Chrome OS, Android'</code> flag.</p> <p>But that’s not all! Like <code class="highlighter-rouge">filter</code>, we can use a bunch of effects, such as <code class="highlighter-rouge">brightness</code>, <code class="highlighter-rouge">contrast</code>, <code class="highlighter-rouge">grayscale</code>, <code class="highlighter-rouge">hue-rotate</code> (awesome!), <code class="highlighter-rouge">saturate</code> and more. <br /> So many possibilities, so little time! What will you build?</p> <h2 id="cool-stuff-you-can-do-with-it-suggested-by-the-community-yay">Cool stuff you can do with it (suggested by the community, yay!)</h2> <p>Got a cool use case for backdrop-filter? Drop us a line and we’ll include it here!</p> <h4 id="night-mode">Night mode</h4> <p><span style="text-align: center; width: 100%; display: inline-block; background-color: whitesmoke;"> <img src="/blog/assets/backdrop-(filter)-it-like-its-hot/backdrop-filter-css-night-mode.gif" alt="Night mode" /> </span></p> <p>Proof of concept: <a href="https://codepen.io/danmindru/pen/rrwpow">yes sir!</a> <br /> Thanks for the suggestion <a href="https://disqus.com/by/bkinney/">@bkinney</a></p> <h4 id="frosted-glass-effect">Frosted glass effect</h4> <p><span style="text-align: center; width: 100%; display: inline-block; background-color: whitesmoke;"> <img src="/blog/assets/backdrop-(filter)-it-like-its-hot/backdrop-filter-apple-frost-effect.gif" alt="Frosted glass effect" /> </span></p> <p>Proof of concept: <a href="https://codepen.io/danmindru/pen/vXJNgg">sure!</a> <br /> (Due to popular demand)</p> <h2 id="why-does-it-exist">Why does it exist</h2> <p>But wait, we already have <code class="highlighter-rouge">filter</code>? What’s the difference? <br /> Simply put, <code class="highlighter-rouge">filter</code> applies a certain (duh) filter on an element. On the other hand, <code class="highlighter-rouge">backdrop-filter</code> applies the effect to the layers (elements) underneath it.</p> <p><small>Don’t confuse setting opacity &lt; 1 to an element that has a <code class="highlighter-rouge">filter</code> with applying that filter to the elements underneath it. It’s completely different.</small></p> <p>The origin of <code class="highlighter-rouge">backdrop-filter</code> is tied to Apple’s frosted glass design on iOS (and macOS). It’s fairly popular these days, with many devs trying to reproduce it one way or another. In recent Webkit builds, creating the frosted glass effect is as easy as:</p> <p><img src="/blog/assets/backdrop-(filter)-it-like-its-hot/css-backdrop-filter-apple.jpg" alt="Apple backdrop filter" /> If you want to read more about it there’s plenty of info in <a href="https://webkit.org/blog/3632/introducing-backdrop-filters/">this blog post</a>.</p> <h2 id="why-i-dont-care-about-the-frost-effect">Why I don’t care about the frost effect</h2> <p>It’s just a trend. It’s going to disappear when the next trend comes out… use it with caution if you have to (damn, I should remove it from <a href="http://mindrudan.com">mindrudan.com</a>).</p> <h2 id="wrap-up">Wrap up</h2> <p>Convincing browsers vendors to implement this experimental feature won’t be easy. As we all know, the best way to do it is with a good song. Here it goes:</p> <p><i> …<br /> When the browser pimp’s in the crib ma<br /> (Back)drop it like it’s hot<br /> (Back)drop it like it’s hot<br /> I got the rolly on my DOM and I’m pouring Chandon<br /> And I roll the best stylesheet cause I got it going on<br /> … </i></p> <script async="" src="//platform.twitter.com/widgets.js" charset="utf-8"></script> <script async="" src="//assets.codepen.io/assets/embed/ei.js"></script> Firebase advanced data modelling and role based authorization 2016-08-25T16:10:30+02:00 https://fadeit.dk/blog /2016/08/25/firebase-advanced-data-modelling-and-role-based-authentication-authorization <p>It’s been almost 5 years since Firebase was released. It looked cool from the beginning, but I never got to use it in a project. <br /> That’s going to change today, because I am set on using it and there’s no turning back.</p> <p>I’m not experienced with it, so this is just my idea of how to handle things. If you have a better idea, feel free to share so we can all become better at Firebasing!</p> <h2 id="project-requirements">Project requirements</h2> <p>Before diving deep into how I modelled my data, it’s important to understand what are the entities involved, how they relate to each other &amp; what kind of roles I have to deal with.</p> <p>I’ll try not to go into details; they shouldn’t affect my general approach (the stupid devil is <strong>*always*</strong> in the detail).</p> <h4 id="entities">1. Entities</h4> <p>I called the main objects in my real-time database entities: name them as you wish, they are just top-level creatures. The main rule I followed when designing these entities: avoid deep nesting (<a href="https://www.youtube.com/watch?v=9gnvC-xIDgs">related</a>, by <a href="http://twitter.com/chrisesplin">Chris Esplin</a>).</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">Auth</span><span class="err">:</span> <span class="p">{</span> <span class="cm">/* Model designed by Firebase, we can't touch this tanana */</span> <span class="p">}</span> <span class="nl">Users</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* $uid from Firebase Auth */</span> <span class="nl">$uid</span><span class="p">:</span> <span class="p">{</span> <span class="nl">role</span><span class="p">:</span> <span class="nb">String</span><span class="p">,</span> <span class="c1">// i.e. 'admin', more on that later</span> <span class="p">}</span> <span class="p">}</span> <span class="nl">Projects</span><span class="p">:</span> <span class="p">{</span> <span class="nl">$pid</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* Metadata */</span> <span class="p">}</span> <span class="p">}</span> <span class="nl">Files</span><span class="p">:</span> <span class="p">{</span> <span class="nl">$pid</span><span class="p">:</span> <span class="p">{</span> <span class="nl">$fid</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* File */</span><span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="nl">Tasks</span><span class="p">:</span> <span class="p">{</span> <span class="nl">$pid</span><span class="p">:</span> <span class="p">{</span> <span class="nl">$tid</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* Task */</span><span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="nl">Messages</span><span class="p">:</span> <span class="p">{</span> <span class="nl">$pid</span><span class="p">:</span> <span class="p">{</span> <span class="nl">$mid</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* Message */</span><span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p><br /></p> <h4 id="user-roles--relationships">2. User roles &amp; relationships</h4> <p>Once more, I might be omitting some details here and there. Here are the main roles &amp; their permissions:</p> <ul> <li> <p>Admin</p> <ul> <li><strong>Everything</strong>: Create, Read, Update, Delete</li> </ul> </li> <li> <p>Manager</p> <ul> <li><strong>Projects</strong>, <strong>Tasks</strong>, <strong>Files</strong>: Create, Read, Update, Delete</li> <li><strong>Files</strong> Create, Read, Update, Delete</li> <li><strong>Messages</strong>: Create, Read, Update, Delete</li> <li><strong>Tasks</strong>: Create, Read, Update, Delete</li> <li><strong>Users</strong>: Create, Read</li> <li><strong>Self</strong> Read, Update, Delete (users/{$ownUid})</li> </ul> </li> <li> <p>User</p> <ul> <li><strong>Projects</strong>, <strong>Tasks</strong> Read</li> <li><strong>Files</strong> Create, Read, Update, Delete</li> <li><strong>Messages</strong> Read</li> <li><strong>Own Messages</strong> Create, Update, Delete</li> <li><strong>Tasks</strong> Read, Update</li> <li><strong>Self</strong> Read, Update, Delete (users/{$ownUid})</li> </ul> </li> <li> <p>External users / collaborators</p> <ul> <li><strong>Some Projects</strong> Read</li> <li><strong>Some Tasks</strong> Read</li> <li><strong>Files</strong> Create, Read</li> <li><strong>Own Files</strong> Update, Delete</li> <li><strong>Self</strong> Read, Update, Delete (users/{$ownUid})</li> </ul> </li> </ul> <h2 id="general-approach">General approach</h2> <p>As you can see, it’s quite messy. Expressing this with conditionals (i.e. <code class="highlighter-rouge">if(user.admin === true){...}</code>) would be a terrible idea. Here’s why:</p> <ul> <li>hard to document</li> <li>hard to maintain</li> <li>hard to change / scale</li> <li>code coupling</li> <li>a pain to express as Firebase’s JSON rules</li> </ul> <p>I’d like to avoid all this so I ditched the idea quickly. In fact, I can’t see how this approach would work with most tools / systems, not only with Firebase, but that’s another story. <br /> What I do want to go for instead is an activity-based authorization system.</p> <p>Here’s how that’ll work: By default no user has any permission. Then, I create a new entity in my real-time database, called <strong>Permissions</strong> where I can define roles (that give certain permissions). I’ll key by role name and write all permissions for a certain role. Example: <br /></p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">Permissions</span><span class="err">:</span> <span class="p">{</span> <span class="nl">user</span><span class="p">:</span> <span class="p">{</span> <span class="c1">// User role</span> <span class="nl">users</span><span class="p">:</span> <span class="p">{</span> <span class="nl">own</span><span class="p">:</span> <span class="p">{</span> <span class="c1">// can read, update, delete own data</span> <span class="nl">r</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">u</span><span class="err">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">d</span><span class="err">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">other</span><span class="err">:</span> <span class="p">{}</span> <span class="c1">// can't read/write 'other' (not owned) data; we can even omit this field</span> <span class="p">},</span> <span class="cm">/* * ... * skipped some entities because 'messages' are more interesting * ... */</span> <span class="nx">messages</span><span class="err">:</span> <span class="p">{</span> <span class="nl">c</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="c1">// can create messages</span> <span class="p">},</span> <span class="nx">message</span><span class="err">:</span> <span class="p">{</span> <span class="nl">own</span><span class="p">:</span> <span class="p">{</span> <span class="c1">// can update &amp; delete own messages</span> <span class="nl">r</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">u</span><span class="err">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nx">d</span><span class="err">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">other</span><span class="err">:</span> <span class="p">{</span> <span class="c1">// can read other messages</span> <span class="nl">r</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p>A small note here, my permissions mirror CRUD, but they can be anything really. I also grouped them in ‘own’ &amp; ‘other’ for readability, but it might as well be <code class="highlighter-rouge">canReadOwn</code> or <code class="highlighter-rouge">canReadOther</code>. For my use case the above is good enough, but you might choose to have more fine-grained control in yours.</p> <p>The way this will work is that upon creation, users are assigned a role. It’s important to note that only admins can add users to this app (there’s no open registration… kind of). Because I don’t want to use a back-end, there’s an immediate problem with this: there’s actually no way to restrict Firebase registration through the console. With some nifty scripts clever people could create an account for themselves, but the joke’s on them. <br /> Firstly, I’ll make sure a <code class="highlighter-rouge">changeRole</code> permission exists for writing to <code class="highlighter-rouge">/users/$uid/role</code>, which only admins have (they’ll have permission to do everything, including this). <br /> Secondly, if there’s no <code class="highlighter-rouge">/users/$uid/role</code> set, I restrict all read &amp; write access. That should keep those pesky clever users from messing with my database.</p> <p>Now that’s fine and dandy, but how to actually implement this?<br /> Don’t worry friend, we got this.</p> <h2 id="bolt-ftw">Bolt FTW</h2> <p>If you didn’t come across it yet, <a href="https://github.com/firebase/bolt">Firebase Bolt</a> is a compiler for Firebase rules (it might be illegal, but could we call it a Firebase rules transpiler?!). It’s supposedly experimental, so make sure to double-check the output after compiling (unit test!).</p> <p>Now, even with Bolt, I’ve noticed I’d be in trouble already. I want to have CRUD-like rules, but Firebase only knows about Read / Write. How to restrict deletion then? <br /> I had to choose between (I believe) two options:</p> <ul> <li><strong>A.</strong> build objects such that they have <code class="highlighter-rouge"><span class="p">{</span><span class="w"> </span><span class="err">public:</span><span class="w"> </span><span class="err">{/*</span><span class="w"> </span><span class="err">what</span><span class="w"> </span><span class="err">I</span><span class="w"> </span><span class="err">have</span><span class="w"> </span><span class="err">so</span><span class="w"> </span><span class="err">far</span><span class="w"> </span><span class="err">*/</span><span class="w"> </span><span class="p">}</span><span class="err">,</span><span class="w"> </span><span class="err">private:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="err">deleted:</span><span class="w"> </span><span class="err">Boolean</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="err">}</span></code> paths &amp; add a field named ‘deleted’ on <code class="highlighter-rouge">private</code>, for which I add stricter write rules, i.e. only admins will have permissions to write (NB: this way data will never actually be deleted). Then, I’d tweak the rules to prevent access for items with <code class="highlighter-rouge">private.deleted === true</code></li> <li><strong>B.</strong> use <code class="highlighter-rouge">newData.exists()</code> together with my permissions, which will prevent deletion for users that are not authorized to delete</li> </ul> <p>I went with <strong>B.</strong>, but some might find <strong>A.</strong> to be suitable: that way data can be restored later if needed. After I’ll deploy the first version of the app, the only way to change my decision is via (probably) a complicated migration script.</p> <p>I’ll have a similar issue with ‘create’. There’s no create rule in Firebase, but there are ways to make that work too.<br /> Long story short, Bolt supports splitting the <code class="highlighter-rouge">write</code> rule into <code class="highlighter-rouge">create</code>, <code class="highlighter-rouge">update</code>, <code class="highlighter-rouge">delete</code> (<a href="https://github.com/firebase/bolt/blob/master/docs/language.md#write-aliases">see here</a>). It’s a matter of preference in the end, but I’ll stick with it for the sake of simplicity. You can achieve the same in a more explicit manner.</p> <h2 id="bolt-implementation">Bolt implementation</h2> <p>Finally, the fun part. I’m going to sketch out how to implement this in Bolt, but take it with a pinch of salt. Most likely I’ll write up a quick new post on what changes I did to it before actually deploying. For now, here goes:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="cm">/* * Checks if a given action is allowed. * * @param {object} obj The object to check. * @param {string} entity The entity to check. * @param {string} permission The permission to check for. * @param {boolean} ownResources A flag that disables 'own' data checks if set (if false, users can perform an action if they own the resource) * */</span> <span class="nx">isAllowed</span><span class="p">(</span><span class="nx">obj</span><span class="p">,</span> <span class="nx">entity</span><span class="p">,</span> <span class="nx">permission</span><span class="p">,</span> <span class="nx">ownResources</span><span class="p">)</span> <span class="p">{</span> <span class="nx">auth</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span> <span class="o">&amp;&amp;</span> <span class="c1">// has to be authenticated</span> <span class="nx">root</span><span class="p">.</span><span class="nx">users</span><span class="p">[</span><span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span><span class="p">].</span><span class="nx">role</span> <span class="o">&amp;&amp;</span> <span class="c1">// has to have a role set</span> <span class="p">(</span> <span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span> <span class="o">!==</span> <span class="nx">obj</span><span class="p">.</span><span class="nx">owner</span> <span class="c1">// user is not owner of the entity</span> <span class="p">?</span> <span class="nx">root</span><span class="p">.</span><span class="nx">permissions</span><span class="p">[</span><span class="nx">root</span><span class="p">.</span><span class="nx">users</span><span class="p">[</span><span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span><span class="p">].</span><span class="nx">role</span><span class="p">][</span><span class="nx">entity</span><span class="p">].</span><span class="nx">all</span> <span class="o">===</span> <span class="kc">true</span> <span class="o">||</span> <span class="c1">// allow everything</span> <span class="cm">/* entity w/o owner: doesn't require extra nesting in the permissions obj (.other) */</span> <span class="nx">root</span><span class="p">.</span><span class="nx">permissions</span><span class="p">[</span><span class="nx">root</span><span class="p">.</span><span class="nx">users</span><span class="p">[</span><span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span><span class="p">].</span><span class="nx">role</span><span class="p">][</span><span class="nx">entity</span><span class="p">][</span><span class="nx">permission</span><span class="p">]</span> <span class="o">===</span> <span class="kc">true</span> <span class="o">||</span> <span class="nx">root</span><span class="p">.</span><span class="nx">permissions</span><span class="p">[</span><span class="nx">root</span><span class="p">.</span><span class="nx">users</span><span class="p">[</span><span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span><span class="p">].</span><span class="nx">role</span><span class="p">][</span><span class="nx">entity</span><span class="p">].</span><span class="nx">other</span><span class="p">[</span><span class="nx">permission</span><span class="p">]</span> <span class="o">===</span> <span class="kc">true</span> <span class="p">:</span> <span class="p">(</span><span class="o">!</span><span class="nx">ownResources</span> <span class="p">?</span> <span class="nx">root</span><span class="p">.</span><span class="nx">permissions</span><span class="p">[</span><span class="nx">root</span><span class="p">.</span><span class="nx">users</span><span class="p">[</span><span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span><span class="p">].</span><span class="nx">role</span><span class="p">][</span><span class="nx">entity</span><span class="p">].</span><span class="nx">own</span><span class="p">[</span><span class="nx">permission</span><span class="p">]</span> <span class="o">===</span> <span class="kc">true</span> <span class="p">:</span> <span class="kc">false</span><span class="p">)</span> <span class="p">)</span> <span class="p">}</span> <span class="cm">/* Nobody can access anything by default */</span> <span class="nx">path</span> <span class="o">/</span> <span class="p">{</span> <span class="nx">read</span><span class="p">()</span> <span class="p">{</span> <span class="kc">false</span> <span class="p">}</span> <span class="nx">write</span><span class="p">()</span> <span class="p">{</span> <span class="kc">false</span> <span class="p">}</span> <span class="p">}</span> <span class="cm">/* Example of permission check: Projects &amp; Project */</span> <span class="nx">type</span> <span class="nx">Projects</span> <span class="p">{</span> <span class="nx">create</span><span class="p">()</span> <span class="p">{</span> <span class="nx">isAllowed</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="s1">'projects'</span><span class="p">,</span> <span class="s1">'c'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">type</span> <span class="nx">Project</span> <span class="p">{</span> <span class="nx">read</span><span class="p">()</span> <span class="p">{</span> <span class="nx">isAllowed</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="s1">'project'</span><span class="p">,</span> <span class="s1">'r'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span> <span class="p">}</span> <span class="nx">update</span><span class="p">()</span> <span class="p">{</span> <span class="nx">isAllowed</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="s1">'project'</span><span class="p">,</span> <span class="s1">'u'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span> <span class="p">}</span> <span class="k">delete</span><span class="p">()</span> <span class="p">{</span> <span class="nx">isAllowed</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="s1">'project'</span><span class="p">,</span> <span class="s1">'d'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">path</span> <span class="o">/</span><span class="nx">projects</span> <span class="nx">is</span> <span class="nx">Projects</span> <span class="p">{}</span> <span class="nx">path</span> <span class="o">/</span><span class="nx">projects</span><span class="o">/</span><span class="p">{</span><span class="nx">$pid</span><span class="p">}</span> <span class="nx">is</span> <span class="nx">Project</span> <span class="p">{}</span> <span class="nx">path</span> <span class="o">/</span><span class="nx">users</span><span class="o">/</span><span class="p">{</span><span class="nx">$uid</span><span class="p">}</span><span class="sr">/role { /</span><span class="o">*</span> <span class="nx">Only</span> <span class="nx">admins</span> <span class="nx">should</span> <span class="nx">be</span> <span class="nx">able</span> <span class="nx">to</span> <span class="nx">write</span> <span class="nx">the</span> <span class="nx">role</span><span class="p">.</span> <span class="o">*</span><span class="sr">/</span><span class="err"> </span> <span class="nx">read</span><span class="p">()</span> <span class="p">{</span> <span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span> <span class="o">==</span> <span class="nx">$uid</span> <span class="p">}</span> <span class="cm">/* Users can read their own role */</span> <span class="nx">write</span><span class="p">()</span> <span class="p">{</span> <span class="nx">isAllowed</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="s1">'user'</span><span class="p">,</span> <span class="s1">'changeRole'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">path</span> <span class="o">/</span><span class="nx">projects</span><span class="o">/</span><span class="p">{</span><span class="nx">$uid</span><span class="p">}</span><span class="sr">/owner { /</span><span class="o">*</span> <span class="nx">Users</span> <span class="nx">can</span> <span class="nx">only</span> <span class="nx">write</span> <span class="nx">to</span> <span class="nx">their</span> <span class="nx">own</span> <span class="s1">'owner'</span> <span class="nx">fields</span><span class="p">.</span> <span class="o">*</span><span class="sr">/</span><span class="err"> </span> <span class="nx">write</span><span class="p">()</span> <span class="p">{</span> <span class="nx">auth</span><span class="p">.</span><span class="nx">uid</span> <span class="o">==</span> <span class="nx">$uid</span> <span class="p">}</span> <span class="p">}</span> <span class="cm">/* * ... * Other entities omitted; they would look similar. * Each entity with an owner will have a `/owner` rule. * That prohibits anybody from changing it, like we have for `/projects/{$uid}/owner` */</span><span class="sr">/</span><span class="err"> </span></code></pre> </div> <p>That’s about it. It compiles via Bolt, so it should work… maybe with some tweaks here &amp; there. With proper unit tests, this baby’s gonna be able to handle whatever you throw at it.</p> <p>To give one more example, here’s how the admin role would look:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">Permissions</span><span class="err">:</span> <span class="p">{</span> <span class="nl">admin</span><span class="p">:</span> <span class="p">{</span> <span class="c1">// Admin role: allow everything</span> <span class="nl">users</span><span class="p">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">projects</span><span class="err">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">project</span><span class="err">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">tasks</span><span class="err">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">task</span><span class="err">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">files</span><span class="err">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">file</span><span class="err">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">messages</span><span class="err">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">message</span><span class="err">:</span> <span class="p">{</span> <span class="nl">all</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span> <span class="p">}</span> <span class="cm">/* ... */</span> <span class="p">}</span> </code></pre> </div> <h2 id="front-end-implementation">Front-end implementation</h2> <p>Firebase doesn’t require a back-end, but you can use one if you want to (or absolutely need it). For this project, I chose not to use a back-end. That means that I’ll handle the UI rendering 100% in the front-end. In turn, that will require that I know what a certain user is allowed to do upon authentication. This way I can show / hide relevant parts of the UI (i.e. a delete button).</p> <p>No matter if you are spinning up a React, Angular, etc. app or even cowboying your way through with a Vanilla JS app, the implementation shouldn’t differ much. <br /> One way to do it is load permissions when a resource is accessed (in the context of the current auth. user). Similarly to the Bolt implementation, a generic <code class="highlighter-rouge">isAllowed()</code> method should check if certain actions can be performed. Another way is to make use of the real-time database and keep an eye out for permission changes. Of course, that means all permissions will be loaded upon login.</p> <h2 id="caveats">Caveats</h2> <p><small>(yes, I used this word because I don’t like pitfalls. do you?!)</small></p> <ol> <li>Don’t take unit testing lightly, you need to do it properly (Bolt == experimental)</li> <li>It’s a bit hard to see the full picture; there might still be some security issues, but hey, it’s a complex app with many user roles (see 1.).</li> <li>Not being able to restrict registration makes me feel a bit uneasy, so perhaps a back-end would be necessary somewhere down the road.</li> </ol> <p>I want to believe that this strategy will work all the way &amp; I won’t need any server logic. Is that maybe too ambitious?<br /> Would love to get some feedback from experienced Firebasing guru-ninjas.</p> HTML emails & email signatures, how hard can it be?! 2015-10-21T02:23:32+02:00 https://fadeit.dk/blog /2015/10/21/html-emails-and-email-signatures-how-hard-can-it-be <h2 id="tldr">TL;DR</h2> <p>I put together some gulp stuff to generate simple HTML email signatures / templates &amp; hope others will find it useful. Check out <a href="https://github.com/fadeit/responsive-html-email-signature">the repo</a>. There’s plenty to do to improve it, so fork away! The current samples look like this:</p> <p><img src="/blog/assets/html-emails-and-email-signatures-how-hard-can-it-be/html-responsive-email-template-signature-light.png" alt="Light Signature" /></p> <p><img src="/blog/assets/html-emails-and-email-signatures-how-hard-can-it-be/html-responsive-email-template-signature-dark.png" alt="Dark Signature" /></p> <h2 id="intro">Intro</h2> <p>A few weeks ago I was listening to the the JavaScript Jabber <a href="https://devchat.tv/js-jabber/177-jsj-ui-validation-with-oren-rubin/mini_player">episode 177</a> with <a href="https://twitter.com/shexman">Oren Rubin</a>. For those that know nothing about Oren, he’s been developing stuff for the past 20 years. In his career he worked with machine learning, distributed computing and compilers, just to name a few. He moved on to web development afterwards, which he admits in the podcast was ‘the really hard stuff’. I know many of you would be tempted to close this article, run outside and burst into tears of joy… but there’s actually more to this post!</p> <p>Web development <strong>is hard</strong> indeed. It also seems like it’s not going to get easier anytime soon. Hell, the next generation of web developers won’t be able to work on a so called ‘web application’ without using some sort of transpiler. You will be the laughing stock of the web if you are still writing in ES5 (or ES6 in about a year or so). The days of writing code in Notepad++ and refreshing a page in a browser are gone and they are not coming back.</p> <p>So… you need to know all the new stuff, but that’s not enough. You also need to know all the old stuff, because the web is not your colleague with a MacBook Pro using the latest version of Chrome. The web is any device with an internet connection, so we have to develop accordingly. I’ll say it again:</p> <blockquote> <p>Web development is hard. Damn, web development is hard!</p> </blockquote> <p>I believe the web today is not much different from the web 10 years ago (in a philosophical sense at least), however the ‘what’ and ‘how’ of web development have changed quite a lot. I am not talking about JS frameworks here or smartphone apps, but the stuff ‘under the hood’: the way we think of progressive enhancement (after all, we should learn from past mistakes), mobile-first designs, responsive layouts, etc.</p> <p>You see, a lot of these revolutionary changes are strongly related to layout. In fact, web layout is changing again as we speak (web-time, not human-time). We moved from tables, wacky flash sites knew how to navigate, grids, now (almost) flexbox, tomorrow CSS shapes / multi columns layouts and who knows… as VR enters mainstream - some kind of 3D layout that we can’t even imagine yet.</p> <p>(I get it, I’m bad at intros but I’ll make my point super soon, promise)</p> <p>Before we look ahead, we need to take a good look back. We need to focus on a place forgotten by the web… a dark and scary box that gives me shivers when I think about it. I am of course talking about <code class="highlighter-rouge">email</code>, the last medium on the web were table layouts are the norm.</p> <p>We need to take a good look at it and face the truth - we messed up.</p> <p>Don’t get me wrong, there has been progress, but in general email clients still suck. If there were some ‘html email standards’ the story might have been similar to the browser one, but there aren’t. Everybody is free to do whatever they want. <br /> I am not thinking about crazy things here. It would be enough if we could ditch table layouts and inline styles. I would also be happy if email clients would have some basic support for media queries. Sure, it would be even nicer if we were able to do CSS3 animations or whatnot, but that’s not what I really care about.</p> <p>It’s hard enough to work with nested tables as it is. When you throw a sausage of inline CSS in the mix, various techniques to punch your computer will suddenly become appealing to you.</p> <h2 id="the-bad-news">The bad news</h2> <p>There’s no way to get rid of tables / nested tables. There is just hope and tears. I’ll point out a very unlikely solution later in the article.</p> <h2 id="the-good-news---in-your-face-inline-css">The good news - In your face, inline CSS</h2> <p>As you probably figured out from the intro, I got fed up with all of this. That inline s*** is impossible to maintain. So I started on a very basic (I thought) gulp build system to reduce my increasingly high blood pressure. About 10 npm modules later, I realized this entire business could take a couple of hours.</p> <p>My attempt to destroy inline styles and attributes was fairly successful. I used <a href="https://github.com/jonkemp/gulp-inline-css">gulp-inline-css</a>, thanks to <a href="https://twitter.com/jonkemp">@jonkemp</a>. <br /><small>By the way, inlining CSS is the new black (performance benefits), check out <a href="https://github.com/addyosmani/critical">this cool lib</a></small>.</p> <p>Unfortunately, you can’t get rid of all of them. I learned that if you wish to be on the safe side, you should use attributes whenever you can (progressive enhancement, remember?). Here are some of the things that don’t work:</p> <ul> <li><strong>width</strong> will be added as an attribute only if it’s provided in pixels</li> <li><strong>height</strong> won’t be added as an attribute at all</li> <li><strong>background-color</strong> won’t become <strong>bgcolor</strong></li> <li><strong>text-align</strong> won’t become <strong>align</strong></li> <li><strong>vertical-align</strong> won’t become <strong>valign</strong></li> <li>etc. See <a href="https://github.com/jonkemp/inline-css/issues/8">this issue</a> for more</li> </ul> <p>On the flip side, <strong>border</strong>, <strong>cellpadding</strong> &amp; <strong>cellspacing</strong> will work just fine.</p> <h2 id="re-using-templates">Re-using templates</h2> <p>It’s common that footers or headers are redundant across templates. I figured that the ability to include templates would come in handy, so I installed the awesome <a href="https://github.com/jas/gulp-preprocess">gulp-preprocess</a> plugin.</p> <p>I could then include files by writing up some HTML comment directives (e.g. <em>‹!– @include head.inc.html –›</em> and that’s about it. There are other useful directives, for displaying stuff from a configuration file you can use <em>‹!– @echo foo –›</em>.</p> <p>If pre-process was a dependency anyway, I figured might as well go deeper and do a configuration-based setup. Fast-forward a few npm modules and I ended up with a build system that can take multiple templates &amp; can handle contact info for a team (if you want it too). Here’s an overview of how it works:</p> <p><img src="/blog/assets/html-emails-and-email-signatures-how-hard-can-it-be/html-responsive-email-template-build-diagram.png" alt="Diagram: overview of what the build process can do" /></p> <h2 id="going-forward">Going forward</h2> <p>If gulp was already there I thought I should also add the classical pre-build clean-up &amp; watch the HTML, CSS &amp; configuration files for changes. I went further and minified HTML &amp; inline styles (<em>‹style›</em>), embedded images in the template (base64) for local assets and probably some other smart thing that I can’t remember at the moment.</p> <p>But why stop here? Let’s throw everything we have at it… hell, can we even make a HTML email ‘transpiler’, which converts semantic HTML into a table layout &amp; adds schema.org attributes automagically? Let’s generate screenshots of tests on the popular email clients, all at the convenience of our terminal. Let’s add pre-processor support… How do <strong>you</strong> think we can make writing email templates easier?</p> <p><a href="https://github.com/fadeit/responsive-html-email-signature/issues">Let’s get this over with, once and for all!</a></p> Loading translations from Google Spreadsheets 2015-09-04T11:16:30+02:00 https://fadeit.dk/blog /2015/09/04/managing-translations-with-gulp-or-grunt <h2 id="introduction">Introduction</h2> <p>This is a follow-up post to our <a href="https://fadeit.dk/blog/post/managing-angular-translate-translations">previous</a> translation management article. To recap - in that article we solved the issue of loading ‘fresh’ translation on development server using nginx and proxying translation requests to the api which in turn will fetch new translations from Google Spreadsheets. It can be seen as overkill - this flow complicates nginx configuration, slows down translation loading on every refresh and will make a ton of (unnecessary) requests while developing. While it’s nice to always get the latest translations, truth is that most of the time you don’t need to fetch translations from the cloud. In this post we’ll explore loading translations only during the build process, thus eliminating the need to proxy requests and write api endpoint in the first place.</p> <h2 id="workflow">Workflow</h2> <p>Since writing previous post, I have also changed my mind about lazy-loading translations using partial loader. This is primarily due to the lengths I had to go to cloak translations while they’re loading, especially considering that the size of the translations file is marginal for most applications. By packaging translations in the same minified file as the application I don’t have to worry about <a href="https://fadeit.dk/blog/post/preload-angular-translate-partial-loader">pre-loading</a> translations or flickering as they are being loaded. So we will introduce an extra task to our workflow that will fetch translations from Google Spreadsheets and converts it to a javascript file ready to be used by angular-translate.</p> <h2 id="using-gulp">Using Gulp</h2> <p>Although here at fadeit we started out with using Grunt exclusively, we’ve come to appreciate Gulp more in our recent projects. Primarily since writing custom tasks in good ol’ JavaScript is more intuitive as opposed to Grunt’s configuration approach. Surely piping the result of previous task directly to next one without having to write it to disk is a plus, but not in current context.</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">gulp</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'gulp'</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">request</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'request'</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'fs'</span><span class="p">);</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="s1">'translations'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">cb</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">uuid</span> <span class="o">=</span> <span class="s1">'1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk'</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">page</span> <span class="o">=</span> <span class="s1">'od6'</span> <span class="kd">var</span> <span class="nx">url</span> <span class="o">=</span> <span class="s1">'https://spreadsheets.google.com/feeds/list/'</span> <span class="o">+</span> <span class="nx">uuid</span> <span class="o">+</span> <span class="s1">'/'</span> <span class="o">+</span> <span class="nx">page</span> <span class="o">+</span> <span class="s1">'/public/values?alt=json'</span><span class="p">;</span> <span class="nx">request</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">res</span><span class="p">,</span> <span class="nx">body</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">json</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">body</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">translations</span> <span class="o">=</span> <span class="p">{</span> <span class="s1">'en'</span><span class="p">:</span> <span class="p">{},</span> <span class="s1">'da'</span><span class="p">:</span> <span class="p">{}</span> <span class="p">};</span> <span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">json</span><span class="p">.</span><span class="nx">feed</span><span class="p">.</span><span class="nx">entry</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">entry</span> <span class="o">=</span> <span class="nx">json</span><span class="p">.</span><span class="nx">feed</span><span class="p">.</span><span class="nx">entry</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span> <span class="kd">var</span> <span class="nx">key</span> <span class="o">=</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">gsx$key</span><span class="p">.</span><span class="nx">$t</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">enValue</span> <span class="o">=</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">gsx$en</span><span class="p">.</span><span class="nx">$t</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">daValue</span> <span class="o">=</span> <span class="nx">entry</span><span class="p">.</span><span class="nx">gsx$da</span><span class="p">.</span><span class="nx">$t</span><span class="p">;</span> <span class="nx">translations</span><span class="p">.</span><span class="nx">en</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">enValue</span><span class="p">;</span> <span class="nx">translations</span><span class="p">.</span><span class="nx">da</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">daValue</span><span class="p">;</span> <span class="p">}</span> <span class="nx">writeTranslations</span><span class="p">(</span><span class="nx">translations</span><span class="p">,</span> <span class="nx">options</span><span class="p">.</span><span class="nx">src</span> <span class="o">+</span> <span class="s1">'/app/translations.js'</span><span class="p">,</span> <span class="nx">cb</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span> <span class="kd">function</span> <span class="nx">writeTranslations</span><span class="p">(</span><span class="nx">translations</span><span class="p">,</span> <span class="nx">file</span><span class="p">,</span> <span class="nx">cb</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">contents</span> <span class="o">=</span> <span class="s2">"var appModule = angular.module('fadeit');\n"</span><span class="p">;</span> <span class="nx">contents</span> <span class="o">+=</span> <span class="s2">"appModule.config(function($translateProvider){\n"</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">property</span> <span class="k">in</span> <span class="nx">translations</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">lang</span> <span class="o">=</span> <span class="nx">translations</span><span class="p">[</span><span class="nx">property</span><span class="p">];</span> <span class="nx">contents</span> <span class="o">+=</span> <span class="s2">"$translateProvider.translations('"</span> <span class="o">+</span> <span class="nx">property</span> <span class="o">+</span> <span class="s2">"',\n"</span><span class="p">;</span> <span class="nx">contents</span> <span class="o">+=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">lang</span><span class="p">);</span> <span class="nx">contents</span> <span class="o">+=</span> <span class="s2">");\n"</span><span class="p">;</span> <span class="p">}</span> <span class="nx">contents</span> <span class="o">+=</span> <span class="s2">"});"</span><span class="p">;</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">writeFile</span><span class="p">(</span><span class="nx">file</span><span class="p">,</span> <span class="nx">contents</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">err</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="nx">err</span><span class="p">){</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s1">'Writing translations failed:'</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="p">}</span> <span class="nx">cb</span><span class="p">();</span> <span class="p">});</span> <span class="p">}</span> </code></pre> </div> <p>As seen from the code, couple modules are used - namely <code class="highlighter-rouge">request</code> to fetch the JSON data and <code class="highlighter-rouge">fs</code> to write a file. The task won’t only write the file, but it will also generate an angular.js module file that will configure translations ready to be used. It can be seen a bit hacky to generate javascript file like that, but I’ve yet to come up with more clean solution. The upside is that this code is not prone to change and I haven’t touched it since writing it. There’s caveat though - if you are using <a href="http://www.browsersync.io/">browsersync</a>, then it may trigger refresh before the translations are actually pulled (because other files changed before). To fix this, we need to make sure that translations are loaded before any other tasks take place. It is easily solved with gulp-sync module, to execute certain tasks synchronously. Same would also apply if the process will concatenate files together - we need to make sure new translations are loaded before that happens. In the sample build process below I’ve split up the build process in two portions where each of them is executed asynchronously. The callback at the end of the gulp task is necessary so that gulp-sync knows when to start processing next batch of tasks.</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">gulpsync</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'gulp-sync'</span><span class="p">)(</span><span class="nx">gulp</span><span class="p">);</span> <span class="nx">gulp</span><span class="p">.</span><span class="nx">task</span><span class="p">(</span><span class="s1">'build'</span><span class="p">,</span> <span class="nx">gulpsync</span><span class="p">.</span><span class="nx">sync</span><span class="p">([[</span><span class="s1">'translations'</span><span class="p">,</span> <span class="s1">'clean'</span><span class="p">],</span> <span class="p">[</span><span class="s1">'html'</span><span class="p">,</span> <span class="s1">'scripts'</span><span class="p">]]));</span> </code></pre> </div> <h2 id="using-grunt">Using Grunt</h2> <p>Firstly I must apologise as I did not take time to develop a pure grunt way of doing so. This is because it started out as a python script which I later incorporated in the grunt workflow. The script will serve same purpose as the one above, with a minor difference of calling an external python executeable instead.</p> <div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">json</span> <span class="kn">import</span> <span class="nn">urllib.request</span> <span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">OrderedDict</span> <span class="n">sheet_id</span> <span class="o">=</span> <span class="s">'1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk'</span><span class="p">;</span> <span class="n">page_id</span> <span class="o">=</span> <span class="s">'od6'</span> <span class="n">base</span> <span class="o">=</span> <span class="s">'https://spreadsheets.google.com/feeds/list/{0}/{1}/public/values?alt=json'</span> <span class="n">translation_config</span> <span class="o">=</span> <span class="s">'src/app/translations.js'</span> <span class="n">url</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sheet_id</span><span class="p">,</span> <span class="n">page_id</span><span class="p">)</span> <span class="n">json_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">url</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf8'</span><span class="p">))</span> <span class="c">#we need to build different data structure</span> <span class="n">en_translation_map</span> <span class="o">=</span> <span class="n">OrderedDict</span><span class="p">()</span> <span class="n">da_translation_map</span> <span class="o">=</span> <span class="n">OrderedDict</span><span class="p">()</span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">json_data</span><span class="p">[</span><span class="s">'feed'</span><span class="p">][</span><span class="s">'entry'</span><span class="p">]:</span> <span class="n">key</span> <span class="o">=</span> <span class="n">entry</span><span class="p">[</span><span class="s">'gsx$key'</span><span class="p">][</span><span class="s">'$t'</span><span class="p">]</span> <span class="n">en_value</span> <span class="o">=</span> <span class="n">entry</span><span class="p">[</span><span class="s">'gsx$en'</span><span class="p">][</span><span class="s">'$t'</span><span class="p">]</span> <span class="n">da_value</span> <span class="o">=</span> <span class="n">entry</span><span class="p">[</span><span class="s">'gsx$da'</span><span class="p">][</span><span class="s">'$t'</span><span class="p">]</span> <span class="n">en_translation_map</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">en_value</span> <span class="n">da_translation_map</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">da_value</span> <span class="c">#Javascript for translations file we are generating</span> <span class="n">config</span> <span class="o">=</span> <span class="s">""" var appModule = angular.module('fadeit'); appModule.config(function($translateProvider){ """</span> <span class="n">en_translations</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">en_translation_map</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span> <span class="n">da_translations</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">da_translation_map</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">)</span> <span class="n">config</span> <span class="o">=</span> <span class="n">config</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">$translateProvider.translations('en',"</span> <span class="o">+</span> <span class="n">en_translations</span> <span class="o">+</span> <span class="s">');'</span> <span class="n">config</span> <span class="o">=</span> <span class="n">config</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">$translateProvider.translations('da',"</span> <span class="o">+</span> <span class="n">da_translations</span> <span class="o">+</span> <span class="s">');'</span> <span class="n">config</span> <span class="o">=</span> <span class="n">config</span> <span class="o">+</span> <span class="s">"</span><span class="se">\n</span><span class="s">});"</span> <span class="c">#close function</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">translation_config</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">outfile</span><span class="p">:</span> <span class="n">outfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">config</span><span class="p">)</span> </code></pre> </div> <p>Now we can save this file in the project (in our case src/translations.py) and call it using grunt-shell during build time.</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">shell</span><span class="err">:</span> <span class="p">{</span> <span class="nl">translations</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">stdout</span><span class="p">:</span> <span class="kc">true</span> <span class="p">},</span> <span class="nx">command</span><span class="err">:</span> <span class="s1">'python3 src/translations.py'</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p>Since grunt executes tasks synchronously, all there is left to do is call this task in the beginning of the build process and it’ll generate a following file.</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">appModule</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'fadeit'</span><span class="p">);</span> <span class="nx">appModule</span><span class="p">.</span><span class="nx">config</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$translateProvider</span><span class="p">)</span> <span class="p">{</span> <span class="nx">$translateProvider</span><span class="p">.</span><span class="nx">translations</span><span class="p">(</span><span class="s1">'en'</span><span class="p">,</span> <span class="p">{</span> <span class="s2">"HELLO"</span><span class="p">:</span> <span class="s2">"Hello"</span> <span class="p">}</span> <span class="p">);</span> <span class="nx">$translateProvider</span><span class="p">.</span><span class="nx">translations</span><span class="p">(</span><span class="s1">'da'</span><span class="p">,</span> <span class="p">{</span> <span class="s2">"HELLO"</span><span class="p">:</span> <span class="s2">"Hej"</span> <span class="p">}</span> <span class="p">);</span> <span class="p">});</span> </code></pre> </div> <h2 id="closing-words">Closing words</h2> <p>Over-all I’ve come to prefer loading translations during build time instead. While it will add few seconds to the build process, I will make it up by not having to wait for translations being proxied on every reload. If I already have a server running and just wish to reload translations, I can issue the command directly instead of re-building everything. For gulp it would be <code class="highlighter-rouge">gulp translations</code> and for grunt <code class="highlighter-rouge">grunt shell:translations</code>. I’ve also decided to ditch versioning translations and generally will add <code class="highlighter-rouge">src/translations.js</code> to <code class="highlighter-rouge">.gitignore</code>.</p> Socket.io with AngularJS boilerplate 2015-05-29T13:18:15+02:00 https://fadeit.dk/blog /2015/05/29/socketio-angular-ultra-simple-boilerplate <p><a href="http://socket.io">Socket.io</a> has truly been a godsend to making realtime applicaions. What used to require writing complex code, ajax polling and other tricks of the trade is now a few lines of code. And it’s fast too - in fact we have a situation where client sends a POST request to python server, which in turn is passed to node server via <a href="http://redis.io">redis</a> <a href="http://redis.io/topics/pubsub">pub/sub</a> and that in turn send a message to the client with socket.io. The interesting bit is that although the socket.io round-trip involves more components, it delivers the message back to client before the response is received. While pure HTTP implementation with network latency in the mix might win the race, it is still a valid contender.</p> <p>I often find myself experimenting on some concept where I need a quick and simple boilerplate to get me going - setting it all up takes time after-all. There are great demos out there, but they often cover more ground than necessary, what I am mostly interested in is the bare minimum working backbone that I can build upon. So here is a stripped-down chat application in just 2 files, doesn’t even contain an http server!</p> <h2 id="serverjs">Server.js</h2> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">io</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'socket.io'</span><span class="p">).</span><span class="nx">listen</span><span class="p">(</span><span class="mi">3000</span><span class="p">);</span> <span class="nx">io</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'connection'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">socket</span><span class="p">){</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'message'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">msg</span><span class="p">){</span> <span class="nx">io</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">'message'</span><span class="p">,</span> <span class="nx">msg</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Server up!"</span><span class="p">);</span> </code></pre> </div> <h2 id="indexhtml">Index.html</h2> <p>Since Angular is not aware of socket receiving a message, we need to wrap the <code class="highlighter-rouge">$scope</code> update in an <code class="highlighter-rouge">$apply</code> function so that angular would pick it up.</p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;!doctype html&gt;</span> <span class="nt">&lt;html</span> <span class="na">ng-app=</span><span class="s">"myApp"</span><span class="nt">&gt;</span> <span class="nt">&lt;head&gt;</span> <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.js"</span><span class="nt">&gt;&lt;/script&gt;</span> <span class="nt">&lt;script </span><span class="na">src=</span><span class="s">"https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.min.js"</span><span class="nt">&gt;&lt;/script&gt;</span> <span class="nt">&lt;/head&gt;</span> <span class="nt">&lt;body</span> <span class="na">ng-controller=</span><span class="s">"myAppCtrl"</span><span class="nt">&gt;</span> <span class="nt">&lt;textarea</span> <span class="na">ng-model=</span><span class="s">"messages"</span> <span class="na">rows=</span><span class="s">15</span> <span class="na">cols=</span><span class="s">80</span><span class="nt">&gt;&lt;/textarea&gt;&lt;br/&gt;</span> <span class="nt">&lt;input</span> <span class="na">ng-model=</span><span class="s">"message"</span><span class="nt">&gt;&lt;/input&gt;</span> <span class="nt">&lt;button</span> <span class="na">ng-click=</span><span class="s">"send()"</span><span class="nt">&gt;</span>Send<span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;script&gt;</span> <span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s2">"myApp"</span><span class="p">,</span> <span class="p">[]);</span> <span class="nx">app</span><span class="p">.</span><span class="nx">controller</span><span class="p">(</span><span class="s2">"myAppCtrl"</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">$scope</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">socket</span> <span class="o">=</span> <span class="nx">io</span><span class="p">(</span><span class="s1">'http://localhost:3000'</span><span class="p">);</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">messages</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">send</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s1">'message'</span><span class="p">,</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">message</span><span class="p">);</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span> <span class="p">}</span> <span class="nx">socket</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s1">'message'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">message</span><span class="p">){</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">$apply</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">messages</span> <span class="o">+=</span> <span class="nx">message</span> <span class="o">+</span> <span class="s2">"\n"</span><span class="p">;</span> <span class="p">});</span> <span class="p">});</span> <span class="p">});</span> <span class="nt">&lt;/script&gt;</span> <span class="nt">&lt;/body&gt;</span> <span class="nt">&lt;/html&gt;</span> </code></pre> </div> <h2 id="setup">Setup</h2> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>npm install socket.io node server.js </code></pre> </div> Getting started with Vue.js: AngularJS perspective 2015-05-14T15:14:03+02:00 https://fadeit.dk/blog /2015/05/14/getting-started-with-vuejs-angularjs-perspective <h2 id="background">Background</h2> <p>First and foremost, what is Vue.js and why should you use it?</p> <p>Vue is a library for developing interactive web interfaces. Its API is inspired by Angular &amp; Backbone, but you can read more about it in <a href="http://vuejs.org/guide/">this guide</a>. What’s more interesting is <u>why use it</u>, especially when you are familiar with Angular. First of all, it’s very flexible. It allows mixing and matching all the libraries you love. That means you can create your own little front-end stack. Secondly, it’s more snappy because it’s simpler and doesn’t use dirty checking to observe changes. On top of that, learning Vue doesn’t take much time. This will make it easy to bring in new team members.</p> <p>You can read even more about it in Vue’s <a href="http://vuejs.org/guide/faq.html">FAQ page</a>, where Vue is compared to Angular, React and more. <br /><small><strong>Updated 15 May 2015</strong></small></p> <p>I jumped on the AngularJS train more than 1 year ago and had lots of fun working with it. From August to January 2015 I had the chance to research Angular application structures. The study was in relation to performance, but it touched on many other subjects, such as component reusability and learnability.</p> <p>I am new to Vue.js, therefore this article will be about my first encounter with the library. Later on I will try to make a more comprehensive analysis.</p> <blockquote> <p>Angular <b class="title">1.*</b> and Vue.js <b class="title">0.11</b> are considered in the article. Breaking changes might come in <a href="https://github.com/vuejs/Discussion/issues/158">Vue 0.12</a>. As for Angular, version 2 will be a completely different beast.</p> </blockquote> <h2 id="vuejs-and-angularjs">Vue.js and AngularJS</h2> <p>I felt comfortable with Vue within a few days. It might be because it resembles other frameworks. It might be because of its simplicity, I couldn’t tell. To better portray the similarities &amp; differences between the two, I devised a brief comparison of some of the high-level concepts in Angular and Vue. Here’s what I ended up with:</p> <table class="fdt-table"> <thead> <tr> <th>AngularJS</th> <th>Vue.js</th> <th>Notes</th> </tr> </thead> <tbody> <tr> <td style="width: 33.33333%;"> <b class="title">Angular Modules</b> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'myModule'</span><span class="p">,</span> <span class="p">[...]);</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Vue components</b> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">Vue</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span> <span class="na">data</span><span class="p">:</span> <span class="kd">function</span><span class="p">(){</span> <span class="k">return</span> <span class="p">{...}</span> <span class="p">},</span> <span class="na">created</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{...},</span> <span class="na">ready</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{...},</span> <span class="na">components</span><span class="p">:</span> <span class="p">{...},</span> <span class="na">methods</span><span class="p">:</span> <span class="p">{...},</span> <span class="na">watch</span><span class="p">:</span> <span class="p">{...}</span> <span class="c1">//(other props excluded)</span> <span class="p">});</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Module notes</b><br /> Modules are a container in Angular, holding other entities such as controllers, directives, etc. In Vue they hold most component logic. </td> </tr> <tr> <td style="width: 33.33333%;"> <b class="title">Vue directives</b><br /> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">myModule</span><span class="p">.</span><span class="nx">directive</span><span class="p">(</span><span class="s1">'directiveName'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">injectables</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="p">{</span> <span class="na">restrict</span><span class="p">:</span> <span class="s1">'A'</span><span class="p">,</span> <span class="na">template</span><span class="p">:</span> <span class="s1">'&lt;div&gt;&lt;/div&gt;'</span><span class="p">,</span> <span class="na">controller</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">},</span> <span class="na">compile</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{...},</span> <span class="na">link</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">}</span> <span class="c1">//(other props excluded)</span> <span class="p">};</span> <span class="p">});</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Angular directives</b><br /> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">Vue</span><span class="p">.</span><span class="nx">directive</span><span class="p">(</span><span class="s1">'my-directive'</span><span class="p">,</span> <span class="p">{</span> <span class="na">bind</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{...},</span> <span class="na">update</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">newValue</span><span class="p">,</span> <span class="nx">oldValue</span><span class="p">)</span> <span class="p">{...},</span> <span class="na">unbind</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{...}</span> <span class="p">});</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Directive notes</b><br /> Directives are not as powerful in Vue; they seem to be more focused. In Angular a directive can be many things, better resembling a component in the Vue world. </td> </tr> <tr> <td style="width: 33.33333%;"> <b class="title">Vue filters</b><br /> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">myModule</span><span class="p">.</span><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="err">‘</span><span class="nx">filterName</span><span class="s1">', []) .filter('</span><span class="nx">reverse</span><span class="err">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="kd">function</span><span class="p">(</span><span class="nx">input</span><span class="p">)</span> <span class="p">{...};</span> <span class="p">});</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Angular filters</b><br /> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">Vue</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="s1">'reverse'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="kd">function</span><span class="p">(</span><span class="nx">value</span><span class="p">){...};</span> <span class="p">});</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Filter notes</b><br /> Filters aren’t much different, although Vue provides read/write options. (see <a href="http://vuejs.org/guide/custom-filter.html" target="_blank">this guide</a>) </td> </tr> </tbody> </table> <p>Because Vue will only help us manage the VM, it won’t tackle some of the challenges that Angular covers out of the box. Services / HTTP reqs, routing, promises (just to name a few) are not among Vue’s concerns. That’s good and bad. Perhaps “bad” is an exaggeration; but it’s a negative aspect for someone that got used to Angular and didn’t have to lift a finger for it. In other words, we have to mix and match libraries as we go to cover our needs. That can be quite a task, but some developers love having this kind of control (others think it’s a waste of time). However, once you find a few good libraries, you won’t have to do it again. I think this kind of flexibility is very empowering. Imagine building your own smaller, focused framework (like Angular) on top of a good foundation: Vue.</p> <p>Creating a personalized front-end stack is great fun too. The first stack I ended up with was made out of: Director (routing), Reqwest (you guessed it), Q (promises) and of course Vue. The best name I can make out of these is QRVD (kinda sounds like curved). For the rest of the article you could have this stack in mind, although it won’t change much if you don’t.</p> <h2 id="templating">Templating</h2> <p>For the same reasons that’s easy to write templates in Angular, it’s really easy to write them in Vue. In fact, it’s a bit of a struggle to find differences between the two.</p> <p>Here’s an overview:</p> <table class="fdt-table"> <thead> <tr> <th>AngularJS</th> <th>Vue.js</th> <th>Notes</th> </tr> </thead> <tbody> <tr> <td style="width: 33.33333%;"> <b class="title">Angular interpolation</b> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">{{</span><span class="nx">myVariable</span><span class="p">}}</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Vue interpolation</b> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">{{</span><span class="nx">myVariable</span><span class="p">}}</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Interpolation notes</b><br /> Interpolating an object or array won't work out of the box in Vue ([Object] will be displayed). I always found that useful for debugging in Angular. Vue does come with a built-in json filter for it though: </td> </tr> <tr> <td style="width: 33.33333%;"> <b class="title">Angular model binding</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">ng-model=</span><span class="s">"myVar"</span><span class="nt">&gt;</span> <span class="nt">&lt;p</span> <span class="na">ng-bind=</span><span class="s">"myVar"</span><span class="nt">&gt;&lt;/p&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Vue model binding</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;input</span> <span class="na">type=</span><span class="s">"text"</span> <span class="na">v-model=</span><span class="s">"myVar"</span><span class="nt">&gt;</span> <span class="nt">&lt;p</span> <span class="na">v-model=</span><span class="s">"myVar"</span><span class="nt">&gt;&lt;/p&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"></td> </tr> <tr> <td style="width: 33.33333%;"> <b class="title">Angular Loops</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;li</span> <span class="na">ng-repeat=</span><span class="s">"item in items"</span> <span class="na">class=</span><span class="s">"item-{{$index}}"</span><span class="nt">&gt;</span> {{item.myProperty}} <span class="nt">&lt;/li&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Vue Loops</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;li</span> <span class="na">v-repeat=</span><span class="s">"items"</span> <span class="na">class=</span><span class="s">"item-{{$index}}"</span><span class="nt">&gt;</span> {{myProperty}} <span class="nt">&lt;/li&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Loop notes</b><br /> Both support model options (ex. debounce for a loop filter). BTW, we can also assign repeated objects in Vue, like in Angular:<br /> <b>v-repeat='item: items'</b> </td> </tr> <tr> <td style="width: 33.33333%;"> <b class="title">Angular conditionals</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">ng-if=</span><span class="s">"myVar"</span><span class="nt">&gt;&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">ng-show=</span><span class="s">"myVar"</span><span class="nt">&gt;&lt;/div&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Vue conditionals</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">v-if=</span><span class="s">"myVar"</span><span class="nt">&gt;&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">v-show=</span><span class="s">"myVar"</span><span class="nt">&gt;&lt;/div&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"></td> </tr> <tr> <td style="width: 33.33333%;"> <b class="title">Angular conditional classes</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">ng-class=</span><span class="s">"{‘active’: myVar}"</span><span class="nt">&gt;&lt;/div&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Vue conditional classes</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">v-class=</span><span class="s">"active: myVar"</span><span class="nt">&gt;&lt;/div&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Conditional classes notes</b><br /> Another example that shows similarities. <br /> Both also support <b>v-attr/ng-attr</b>. </td> </tr> <tr> <td style="width: 33.33333%;"> <b class="title">Angular event binding</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">ng-click=</span><span class="s">"myMethod($event)"</span><span class="nt">&gt;&lt;/div&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Vue event binding</b><br /> <figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">v-on=</span><span class="s">"click: myMethod($event)"</span><span class="nt">&gt;&lt;/div&gt;</span></code></pre></figure> </td> <td style="width: 33.33333%;"> <b class="title">Event binding notes</b><br /> The generic <b>v-on</b> directive makes things more consistent than in Angular, but I guess it's down to personal preference. </td> </tr> </tbody> </table> <p>The list goes on. There’s even a v-cloak directive that hides the bindings before they are rendered (as ng-cloak does).</p> <p>However, there’s one directive that you won’t find in Angular: <code class="highlighter-rouge">v-with</code>. Attaching this directive and passing a data object will allow a child VM to inherit data from his parent. In my first encounter, I though of it as a drastically simplified Angular Controller, managing the data flow across the application (without the need of an equivalent to <code class="highlighter-rouge">$scope</code>. By default, components will have an isolated scope. That makes this directive essential, although components also accept <em>inherit</em> as a property (see <a href="http://vuejs.org/guide/components.html#Scope_Inheritance">more on scope inheritance</a>).</p> <h2 id="modularity--application-structures">Modularity / Application structures</h2> <p>As I mentioned, I researched this area in Angular. My conclusion was that Angular both nailed it and it didn’t. You can create great modular architectures that are indeed maintainable, reusable, comprehensible, etc. However, you’ll need a monster-build strategy to go with it, or perhaps use tools like <a href="http://requirejs.org/">Require.js</a> or <a href="http://browserify.org/">Browserify</a> to ease the pain. That’s not even the worst part. If you are just starting with Angular, you probably won’t come across a good structuring guide, which is a shame. It’s only later (or maybe too late) that Angular developers decide to look into it.</p> <p>So how does Vue.js perform? More or less the same. There’s a very short section in the <a href="http://vuejs.org/guide/application.html">Vue guide</a> about app structuring, but it’s not enough. I believe developers are able to produce kick-ass modular architectures with Vue, but there are no comprehensive guides or suggestions on how to do it. I wasn’t satisfied, so in the ‘Building larger applications’ section I tried to sketch out an example that may help developers new to Vue.</p> <h2 id="components">Components</h2> <p>Before that, I have to add a comment on components. I think, Vue managed to clearly separate components, which we can’t really say about Angular. It’s perhaps closer to Angular 2, which is great news in my mind. <br /> As a result, there’s no need for a <code class="highlighter-rouge">$scope</code> equivalent.</p> <h2 id="scopes--data-flow">Scopes / Data flow</h2> <p>I truly believe Vue is elegant when it comes to Data flow &amp; control. It felt natural and there was no need for an emit/broadcast mechanism to communicate with parent components (although Vue supports such events). There is a downside to it: the components and views become even more coupled (but hey, it is called a VM). Sometimes it’s tricky to understand which data is inherited from which <code class="highlighter-rouge">v-with</code>, so use it sparingly. There are ways to prevent spaghetti code from creeping in. Services are one good method, but there’s not much documentation about them.</p> <h2 id="services">Services</h2> <p><em>My source is written in CoffeeScript, so I’ll provide examples in both Coffee and Vanilla JS format.</em></p> <p>One approach for writing services is to use a <code class="highlighter-rouge">HTTP</code> -&gt; <code class="highlighter-rouge">Service</code> -&gt; <code class="highlighter-rouge">Component</code> mechanism. HTTP has to be generic, so we can extend it for each object:</p> <table class="fdt-table"> <thead> <tr> <th>CoffeeScript</th> <th>JavaScript</th> </tr> </thead> <tbody> <tr> <td style="width: 50%;"> <figure class="highlight"><pre><code class="language-coffeescript" data-lang="coffeescript"><span class="k">class</span> <span class="nx">HTTP</span> <span class="na">constructor</span><span class="o">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="p">...</span> <span class="na">get</span><span class="o">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="p">...</span> <span class="na">post</span><span class="o">:</span> <span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">...</span> <span class="na">delete</span><span class="o">:</span> <span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">...</span> <span class="na">patch</span><span class="o">:</span> <span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">...</span></code></pre></figure> </td> <td style="width: 50%;"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">HTTP</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="nx">HTTP</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">get</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{...};</span> <span class="nx">HTTP</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">post</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{...};</span> <span class="nx">HTTP</span><span class="p">.</span><span class="nx">prototype</span><span class="p">[</span><span class="s2">"delete"</span><span class="p">]</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span> <span class="p">{...};</span> <span class="nx">HTTP</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">patch</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">url</span><span class="p">,</span> <span class="nx">data</span><span class="p">)</span> <span class="p">{...};</span> <span class="k">return</span> <span class="nx">HTTP</span><span class="p">;</span> <span class="p">})();</span></code></pre></figure> </td> </tr> </tbody> </table> <p>Just for reference, a patch request using reqwest looks like this:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">reqwest</span><span class="p">({</span> <span class="na">url</span><span class="p">:</span> <span class="nx">url</span><span class="p">,</span> <span class="na">type</span><span class="p">:</span> <span class="s1">'json'</span><span class="p">,</span> <span class="na">method</span><span class="p">:</span> <span class="s1">'patch'</span><span class="p">,</span> <span class="na">data</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">data</span><span class="p">),</span> <span class="na">contentType</span><span class="p">:</span> <span class="s1">'application/json'</span> <span class="p">});</span> </code></pre> </div> <p>With CoffeeScript we can extend the HTTP class to adapt it for other use cases. For example, the User object could look like: <br /> (Remember: Angular already has the $http service, so we don’t have to do any of these bits)</p> <table class="fdt-table"> <thead> <tr> <th>CoffeeScript</th> <th>JavaScript</th> </tr> </thead> <tbody> <tr> <td style="width: 50%;"> <figure class="highlight"><pre><code class="language-coffeescript" data-lang="coffeescript"><span class="k">class</span> <span class="nx">UserHTTP</span> <span class="k">extends</span> <span class="nx">HTTP</span> <span class="na">all</span><span class="o">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="vi">@</span><span class="na">get</span><span class="p">(</span><span class="s">"/v1/users"</span><span class="p">)</span> <span class="na">remove</span><span class="o">:</span> <span class="p">(</span><span class="nx">id</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="vi">@</span><span class="na">delete</span><span class="p">(</span><span class="err">“</span><span class="c1">#{/v1/users/#{id}”)</span> <span class="c1">#...</span></code></pre></figure> </td> <td style="width: 50%;"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">UserHTTP</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">superClass</span><span class="p">)</span> <span class="p">{</span> <span class="nx">extend</span><span class="p">(</span><span class="nx">UserHTTP</span><span class="p">,</span> <span class="nx">superClass</span><span class="p">);</span> <span class="kd">function</span> <span class="nx">UserHTTP</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">UserHTTP</span><span class="p">.</span><span class="nx">__super__</span><span class="p">.</span><span class="nx">constructor</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">arguments</span><span class="p">);</span> <span class="p">}</span> <span class="nx">UserHTTP</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">all</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s2">"/v1/users"</span><span class="p">);</span> <span class="p">};</span> <span class="nx">UserHTTP</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">remove</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="err">“</span><span class="o">/</span><span class="nx">v1</span><span class="o">/</span><span class="nx">users</span><span class="o">/</span><span class="err">“</span> <span class="o">+</span> <span class="nx">id</span><span class="p">);</span> <span class="p">};</span> <span class="c1">//...</span> <span class="k">return</span> <span class="nx">UserHTTP</span><span class="p">;</span> <span class="p">})(</span><span class="nx">HTTP</span><span class="p">);</span></code></pre></figure> </td> </tr> </tbody> </table> <p>Then come the actual services, which are perhaps closer to what we’re used to in Angular. For the same Object, we need to pass UserHTTP as a constructor param and then we’ll be able to call its methods and process the data as needed (before or after requests are sent).</p> <table class="fdt-table"> <thead> <tr> <th>CoffeeScript</th> <th>JavaScript</th> </tr> </thead> <tbody> <tr> <td style="width: 50%;"> <figure class="highlight"><pre><code class="language-coffeescript" data-lang="coffeescript"><span class="k">class</span> <span class="nx">UserService</span> <span class="na">constructor</span><span class="o">:</span> <span class="p">(</span><span class="vi">@</span><span class="na">HTTP</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="na">all</span><span class="o">:</span> <span class="p">()</span> <span class="o">-&gt;</span> <span class="vi">@</span><span class="na">HTTP</span><span class="p">.</span><span class="na">all</span><span class="p">()</span> <span class="na">remove</span><span class="o">:</span> <span class="p">(</span><span class="nx">id</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="c1"># Here we can do some data processing</span> <span class="c1"># ...</span> <span class="vi">@</span><span class="na">HTTP</span><span class="p">.</span><span class="na">remove</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span></code></pre></figure> </td> <td style="width: 50%;"> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">UserService</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span> <span class="kd">function</span> <span class="nx">UserService</span><span class="p">(</span><span class="nx">HTTP</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">HTTP</span> <span class="o">=</span> <span class="nx">HTTP</span><span class="p">;</span> <span class="p">}</span> <span class="nx">UserService</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">all</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{...};</span> <span class="nx">UserService</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">remove</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//Here we can do some data processing</span> <span class="c1">//...</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">HTTP</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="nx">id</span><span class="p">);</span> <span class="p">};</span> <span class="k">return</span> <span class="nx">UserService</span><span class="p">;</span> <span class="p">})();</span></code></pre></figure> </td> </tr> </tbody> </table> <p>Finally, in any component we can instantiate a service and call the methods that in turn will make an HTTP request.</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="p">...</span> <span class="nx">HTTP</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">HTTP</span><span class="p">();</span> <span class="nx">userService</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">UserService</span><span class="p">(</span><span class="nx">HTTP</span><span class="p">.</span><span class="nx">users</span><span class="p">);</span> <span class="p">...</span> <span class="c1">// Inside a Vue component</span> <span class="p">...</span> <span class="nl">ready</span><span class="p">:</span> <span class="kd">function</span><span class="p">(){</span> <span class="nx">vm</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span> <span class="nx">userService</span><span class="p">.</span><span class="nx">all</span><span class="p">()</span> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">users</span><span class="p">){</span> <span class="nx">vm</span><span class="p">.</span><span class="nx">$set</span><span class="p">(</span><span class="s1">'users'</span><span class="p">,</span> <span class="nx">users</span><span class="p">);</span> <span class="p">});</span> <span class="p">...</span> </code></pre> </div> <p>The next section contains a diagram that illustrates how this all fits together.</p> <h2 id="building-larger-applications">Building larger applications</h2> <p>First of all, what is a large app? Well, potentially any app can become large. When it’s hard to understand how things are working and the structure is not comprehensible for outsiders, then you’re probably dealing with a large app.</p> <p>In my case, after about a week of working on a Vue application the structure was almost chaotic. It wasn’t easy to tell how it all fits together. On one hand, components weren’t properly separated, but this doesn’t have anything to do with Vue. So it was time for taking a step back, analyzing and refactoring. I sketched out my components and tried to create a structure that would make things better.</p> <p>Before you check out the diagram below, it is worth mentioning that Browserify was used to split components up. As I said before, you can either follow this approach or create a build strategy with Gulp/Grunt that concatenates the files. <br /><br /> Get a full copy of the diagram <a href="/blog/assets/getting-started-with-vuejs-angularjs-perspective/vue-large-app-structure-diagram.svg">here</a>.</p> <p><img src="/blog/assets/getting-started-with-vuejs-angularjs-perspective/vue-large-app-structure-diagram.svg" alt="Diagram: one way to structure larger Vue.js apps." /></p> <p>In terms of file/directory structure, here’s how a Vue app could look like:</p> <p>(get a full copy <a href="/blog/assets/getting-started-with-vuejs-angularjs-perspective/vue-large-app-directory-structure.svg">here</a>)</p> <p><img src="/blog/assets/getting-started-with-vuejs-angularjs-perspective/vue-large-app-directory-structure.svg" alt="Possible directory structure for a larger Vue.js app." /></p> <h2 id="final-words">Final words</h2> <p>I hope these sketches will be a good starting point/source of inspiration for devs that are starting out.</p> <p>In future posts I’ll probably write more about testing, maintainability and scalability, if I discover something interesting.</p> <p>My final words are: try out Vue, you might find it useful for many of your projects. It’s a nice little library with as many super powers as Angular!</p> How to manage multilingual website translations? 2015-05-08T12:25:54+02:00 https://fadeit.dk/blog /2015/05/08/managing-angular-translate-translations <p><strong>Update</strong>: I have written a <a href="https://fadeit.dk/blog/post/managing-translations-with-gulp-or-grunt">follow-up post</a> where I load translations during gulp/grunt build process instead of nginx proxy.</p> <h2 id="introduction">Introduction</h2> <p>This article focuses on translation management for AngularJS, however it can be applied to any translation management process. Truth is… I haven’t been a fan of the approach <a href="https://en.wikipedia.org/wiki/Gettext">gettext</a> takes, primarily because of the translation management process. I find the work-flow of having to extract translations from the code (.po file), send them to translator, compiling (.mo file) and deploying to consume significant amount of time. Key based translation like angular-translate may simplify certain aspects, but JSON files are still a limitation. They are uncomfortable to edit, prone to merge conflicts and have a strict format.</p> <h2 id="our-apporach">Our apporach</h2> <p>Fortunately there is a tool out there that offers flexible user interface, real-time editing, it fast and easy to integrate with - Google Sheets. You may know that it is possible to publish spreadsheet as HTML, however what they don’t tell you is that there is also an endpoint for returning the data in JSON format.</p> <p><img src="/blog/assets/managing-angular-translate-translations/publish.png" alt="Publishing Google Sheets" /></p> <p>Google will then give you a URL that looks like this:</p> <p><a href="https://docs.google.com/spreadsheets/d/1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk/pubhtml">https://docs.google.com/spreadsheets/d/1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk/pubhtml</a>.</p> <p>To access JSON variant, take the id of document - <code class="highlighter-rouge">1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk</code> and use it in spreadsheet feed service URL:</p> <p><a href="https://spreadsheets.google.com/feeds/list/1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk/od6/public/values?alt=json">https://spreadsheets.google.com/feeds/list/1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk/od6/public/values?alt=json</a>.</p> <p>That URL however will only display data for first page only - by default first page id in Google Docs is <code class="highlighter-rouge">od6</code>. In order to organize translations we want to store them on multiple pages (I have created Common &amp; Register pages in the screenshot above), therefore we need to get IDs of other pages using yet another feed:</p> <p><a href="https://spreadsheets.google.com/feeds/worksheets/1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk/private/full">https://spreadsheets.google.com/feeds/worksheets/1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk/private/full</a>.</p> <p>From the XML we can search for <code class="highlighter-rouge">&lt;/id&gt;</code> to extract other sheet IDs. As we see, the ID for second sheet is no longer following any pattern - <a href="https://spreadsheets.google.com/feeds/list/1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk/o14w8rc/public/values?alt=json">o14w8rc</a>.</p> <p>Even more feeds are covered on the <a href="https://developers.google.com/google-apps/spreadsheets/">Official API documentation</a>.</p> <p><img src="/blog/assets/managing-angular-translate-translations/formatted.png" alt="Formatted JSON from Google Spreadsheet feed" /></p> <p>While JSON representation is good starting point, it is a serialized representation of the document that contains a lot of meta-data. We are only interesed in the nested values, so data-structure to be transformed into what i18n framework can read. For that we are going to implement an API endpoint that will fetch translations from Google Docs, transform the structure and return translations. As seen from the screenshot above, the pattern for extracting a value is <code class="highlighter-rouge">feed-&gt;entry-&gt;gsx${language}-&gt;$t</code>. The minimal example below utilizes <a href="http://flask.pocoo.org/">Flask microframework</a> written in Python</p> <div class="language-python highlighter-rouge"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">os</span> <span class="kn">import</span> <span class="nn">json</span> <span class="kn">import</span> <span class="nn">urllib</span> <span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">abort</span> <span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">OrderedDict</span> <span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span> <span class="nd">@app.route</span><span class="p">(</span><span class="s">'/translation/&lt;lang&gt;/&lt;filename&gt;'</span><span class="p">)</span> <span class="k">def</span> <span class="nf">fetch_translation</span><span class="p">(</span><span class="n">lang</span><span class="p">,</span> <span class="n">filename</span><span class="p">):</span> <span class="c"># Mapping Google Docs page ID to the translation file</span> <span class="n">page_map</span> <span class="o">=</span> <span class="p">{</span> <span class="s">'common.json'</span><span class="p">:</span> <span class="s">'od6'</span><span class="p">,</span> <span class="s">'register.json'</span><span class="p">:</span> <span class="s">'o14w8rc'</span> <span class="p">}</span> <span class="n">sheet_id</span> <span class="o">=</span> <span class="s">'1FsVuRLbtgxMZvWd4mpnKiAhqVYap-ZAx08LBeZ9HFJk'</span> <span class="n">base</span> <span class="o">=</span> <span class="s">'https://spreadsheets.google.com/feeds/list/{0}/{1}/public/values?alt=json'</span> <span class="k">if</span> <span class="n">filename</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">page_map</span><span class="p">:</span> <span class="n">abort</span><span class="p">(</span><span class="mi">404</span><span class="p">)</span> <span class="n">page_id</span> <span class="o">=</span> <span class="n">page_map</span><span class="p">[</span><span class="n">filename</span><span class="p">]</span> <span class="n">url</span> <span class="o">=</span> <span class="n">base</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">sheet_id</span><span class="p">,</span> <span class="n">page_id</span><span class="p">)</span> <span class="n">json_data</span> <span class="o">=</span> <span class="n">json</span><span class="o">.</span><span class="n">loads</span><span class="p">(</span><span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">url</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf8'</span><span class="p">))</span> <span class="c"># We need to build different data structure</span> <span class="n">translation_map</span> <span class="o">=</span> <span class="n">OrderedDict</span><span class="p">()</span> <span class="k">for</span> <span class="n">entry</span> <span class="ow">in</span> <span class="n">json_data</span><span class="p">[</span><span class="s">'feed'</span><span class="p">][</span><span class="s">'entry'</span><span class="p">]:</span> <span class="n">key</span> <span class="o">=</span> <span class="n">entry</span><span class="p">[</span><span class="s">'gsx$key'</span><span class="p">][</span><span class="s">'$t'</span><span class="p">]</span> <span class="n">value</span> <span class="o">=</span> <span class="n">entry</span><span class="p">[</span><span class="s">'gsx${0}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">lang</span><span class="p">)][</span><span class="s">'$t'</span><span class="p">]</span> <span class="k">if</span> <span class="n">value</span><span class="p">:</span> <span class="n">translation_map</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span> <span class="k">return</span> <span class="n">json</span><span class="o">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">translation_map</span><span class="p">)</span> <span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span> <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span> </code></pre> </div> <p>Now let’s run the server and fetch translations at http://127.0.0.1:5000/translation/en-us/register.json</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>curl http://127.0.0.1:5000/translation/en-us/register.json <span class="o">{</span><span class="s2">"WELCOME"</span>: <span class="s2">"Welcome"</span><span class="o">}</span> </code></pre> </div> <p>Fetching translations is convenient while developing and translating, however we wouldn’t want production server to fetch translations from Google Docs every time it gets a request. Easiest solution would be to cache translations for certain amount of time, we also want to version control translations, therefore the strategy is a little different. Production server would be serving static JSON files from the disc, whereas on development instance NGINX will intercept any requests going to /translation and proxy them to the application server instead:</p> <p><img src="/blog/assets/managing-angular-translate-translations/flow.png" alt="Translation loading flow" /></p> <p>For development NGINX configuration we need to add a location block like so:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="c">#proxy translations from Google Docs on development</span> location /translation <span class="o">{</span> proxy_pass http://127.0.0.1:5000<span class="nv">$uri</span>; proxy_redirect off; proxy_set_header Host <span class="nv">$host</span>; proxy_set_header X-Real-IP <span class="nv">$remote_addr</span>; proxy_set_header X-Forwarded-For <span class="nv">$proxy_add_x_forwarded_for</span>; <span class="o">}</span> </code></pre> </div> <h2 id="conclusion">Conclusion</h2> <p>All translations now come from one consistent source, no more mismatches and conflicts.</p> <p>With Git we get:</p> <ul> <li>Accidental modifications in Google Docs don’t automatically end up in production</li> <li>Each translation has a revision history&lt;/li&gt;&lt;li&gt;Finding differences between edits is simple</li> <li>Blame view helps finding wrongdoer</li> </ul> <p>Google Docs brings to the table:</p> <ul> <li>Spotting missing translations is easy because all languages are visible on same sheet (.json files are separate for each language)</li> <li>Access and control management of editors is simple</li> <li>Multiple people can edit same document in realtime</li> <li>Changes to translations are visible immediately (on development setup)</li> <li>Sheets supports highlighting and commenting</li> </ul> <p>Dependencies needed to run the flask server:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code><span class="c">#Install dependencies</span> sudo apt-get install virtualenv python3.4-dev python-pip <span class="c">#Create virtual environment</span> virtualenv -p /usr/bin/python3.4 venv <span class="c">#Activate virtual environment</span> <span class="nb">source </span>venv/bin/activate <span class="c">#Install libraries</span> pip install flask <span class="c">#Run server</span> python server.py <span class="c">#Fetch translations</span> http://localhost:5000/translation/en-us/register.json </code></pre> </div> Know your cascading stylesheets OR how should we organize CSS? 2015-04-16T14:10:00+02:00 https://fadeit.dk/blog /2015/04/16/know-your-cascading-stylesheets-or-how-should-we-organize-css <p>Here’s my experience: every time I start a new project I feel like re-thinking my CSS strategy. I never felt satisfied with the quality of the stylesheets at the end of a project.</p> <p>The main reason is that there are two types of CSS: the original pretty CSS and ‘after a few months’ CSS. Most of us start out ambitious, carefully writing properties and actually documenting the process. When fixes or features need to be added after a few weeks, we just throw properties wherever to get it done, because deadlines.</p> <h2 id="the-problem-with-css">The problem with CSS</h2> <p>Documentation, formatting, frameworks, pre-processors, specificity - they all are trouble makers. Any of these might force you to give up and sell your soul. I am looking at you, the so called developer that appended <code class="highlighter-rouge">!important</code> to a property. In the W3C spec, “important” is documented as “you gave up and now a scrambled mess of selectors that nobody understands took control of your stylesheet”.</p> <p>In all seriousness now, you should never have to write it. However, there are many other signs that show you lost your grip. For example, if you have the same selector in many places and you aren’t sure what exactly you’re changing when updating a property.</p> <p>I am not writing this article because I have it all figured it out, but because I have some clues on how to improve the quality of CSS. I don’t want it to feel like a set of rules, but rather the start of a constructive dialog.</p> <h2 id="some-pre-processor-context">Some pre-processor context</h2> <p>We need to get one thing straight. I am using a CSS pre-processors (LESS, have been using SASS too) and I can’t go back. It makes my stylesheets more consistent and enables me to write less (duh) code. It also makes it easy to change global properties that (ironically) truly cascade on all elements. This article is not about pre-processors and they are most certainly NOT a solution to CSS organization issues. While they might be helpful, they bring their own problems. I will touch the subject here and there, because many front-enders rely on pre-processors to keep their sanity.</p> <h2 id="the-solutions-today">The solutions today</h2> <h3 id="properties">Properties</h3> <p>Let’s start with the easy part.</p> <p>When it comes to properties, what works and what doesn’t?</p> <p>I’ve been going the ‘random way’ until now and everything turned out ok. I wouldn’t say no to another approach. Here are the most popular options:</p> <ol> <li>Group by type</li> <li>Order by line length</li> <li>Alphabetize</li> </ol> <h4 id="group-by-type">1. Group by type</h4> <p>This method was once (and maybe still is) the most popular method of writing properties. Nicolas Gallager’s <a href="https://github.com/necolas/idiomatic-css">idiomatic CSS</a> recommends this style for smaller teams, but suggests alphabetising for larger teams.</p> <p>Here’s one how to do it:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">section</span> <span class="p">{</span> <span class="c">/* Display &amp; Box Model */</span> <span class="nl">display</span><span class="p">:</span> <span class="nb">inline</span><span class="p">;</span> <span class="nl">box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">15px</span><span class="p">;</span> <span class="nl">padding</span><span class="p">:</span> <span class="m">5px</span> <span class="m">10px</span> <span class="m">15px</span> <span class="m">0</span><span class="p">;</span> <span class="nl">border</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">dashed</span> <span class="m">#000</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">10px</span> <span class="m">20px</span><span class="p">;</span> <span class="c">/* Color */</span> <span class="nl">background</span><span class="p">:</span> <span class="m">#FFF</span><span class="p">;</span> <span class="nl">color</span><span class="p">:</span> <span class="m">#333</span><span class="p">;</span> <span class="c">/* Text */</span> <span class="nl">font-family</span><span class="p">:</span> <span class="n">Helvetica</span><span class="p">,</span> <span class="n">Arial</span><span class="p">,</span> <span class="nb">sans-serif</span><span class="p">;</span> <span class="nl">font-size</span><span class="p">:</span> <span class="m">12px</span><span class="p">;</span> <span class="nl">line-height</span><span class="p">:</span> <span class="m">16px</span><span class="p">;</span> <span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span> <span class="c">/* Other */</span> <span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>We are free to add our own types, such as <code class="highlighter-rouge">Effects</code> (can be border-radius, box-shadow, etc), <code class="highlighter-rouge">Animations &amp; Transitions</code> or <code class="highlighter-rouge">Transforms</code>. There aren’t any rules, we can create our own as long as we are consistent. The downside is that the rest of the team has to learn these rules, which is not very efficient.</p> <h4 id="order-by-line-length">2. Order by line length</h4> <p>This is the ‘old school’ method. Some developers like it because it decreases the amount of lines and gives them a better overview of the stylesheet. I don’t think this method is realistic today, especially when we have to pile up a bunch of duplicate properties to support all browsers.</p> <p>Here’s why it worked before:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">section</span> <span class="p">{</span> <span class="nl">display</span><span class="p">:</span> <span class="nb">inline</span><span class="p">;</span> <span class="nl">overflow-x</span><span class="p">:</span> <span class="nb">hidden</span><span class="p">;</span> <span class="nl">box-sizing</span><span class="p">:</span> <span class="n">border-box</span><span class="p">;</span> <span class="nl">width</span><span class="p">:</span> <span class="m">15px</span><span class="p">;</span> <span class="nl">height</span><span class="p">:</span> <span class="m">15px</span><span class="p">;</span> <span class="p">}</span> <span class="nt">section</span> <span class="nt">ul</span> <span class="p">{</span> <span class="nl">background-color</span><span class="p">:</span><span class="no">red</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>…and here’s why it doesn’t work today:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">section</span> <span class="nt">ul</span> <span class="nt">li</span> <span class="p">{</span> <span class="nl">animation</span><span class="p">:</span><span class="n">animationFrames</span> <span class="n">linear</span> <span class="m">3s</span><span class="p">;</span> <span class="nl">animation-iteration-count</span><span class="p">:</span> <span class="n">infinite</span><span class="p">;</span> <span class="nl">animation-fill-mode</span><span class="p">:</span> <span class="n">forwards</span><span class="p">;</span> <span class="nl">-webkit-animation</span><span class="p">:</span> <span class="n">animationFrames</span> <span class="n">linear</span> <span class="m">3s</span><span class="p">;</span> <span class="nl">-webkit-animation-iteration-count</span><span class="p">:</span> <span class="n">infinite</span><span class="p">;</span> <span class="nl">-webkit-animation-fill-mode</span><span class="p">:</span><span class="n">forwards</span><span class="p">;</span> <span class="nl">-moz-animation</span><span class="p">:</span> <span class="n">animationFrames</span> <span class="n">linear</span> <span class="m">3s</span><span class="p">;</span> <span class="nl">-moz-animation-iteration-count</span><span class="p">:</span> <span class="n">infinite</span><span class="p">;</span> <span class="nl">-moz-animation-fill-mode</span><span class="p">:</span><span class="n">forwards</span><span class="p">;</span> <span class="nl">-o-animation</span><span class="p">:</span> <span class="n">animationFrames</span> <span class="n">linear</span> <span class="m">3s</span><span class="p">;</span> <span class="nl">-o-animation-iteration-count</span><span class="p">:</span> <span class="n">infinite</span><span class="p">;</span> <span class="nl">-o-animation-fill-mode</span><span class="p">:</span><span class="n">forwards</span><span class="p">;</span> <span class="nl">-ms-animation</span><span class="p">:</span> <span class="n">animationFrames</span> <span class="n">linear</span> <span class="m">3s</span><span class="p">;</span> <span class="nl">-ms-animation-iteration-count</span><span class="p">:</span> <span class="n">infinite</span><span class="p">;</span> <span class="nl">-ms-animation-fill-mode</span><span class="p">:</span><span class="n">forwards</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>To be fair about it, I should point out that there is a way to make it better. By using a task runner (such as <a href="http://gruntjs.com/">Grunt</a> or <a href="http://gulpjs.com/">Gulp</a>) we can automatically prefix the properties that require a vendor prefix. This way we’ll get rid of many of the duplicate styles:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">section</span> <span class="nt">ul</span> <span class="nt">li</span> <span class="p">{</span> <span class="nl">animation</span><span class="p">:</span><span class="n">animationFrames</span> <span class="n">linear</span> <span class="m">3s</span><span class="p">;</span> <span class="nl">animation-iteration-count</span><span class="p">:</span> <span class="n">infinite</span><span class="p">;</span> <span class="nl">animation-fill-mode</span><span class="p">:</span> <span class="n">forwards</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>It’s better, but probably 1-2 properties away from being hard to read. Some developers used to argue that it also decreases the size of the CSS file, which is now a ‘commodity’. Again, by using a task runner we can eliminate all spaces and line breaks. This gives us the freedom of writing CSS however we like, with plenty of spaces and comments. (<a href="https://github.com/gruntjs/grunt-contrib-cssmin">grunt-cssmin</a> and <a href="https://github.com/chilijung/gulp-cssmin">gulp-cssmin</a>)</p> <p>I wonder if anybody is still following the order by line approach today. I wouldn’t be surprised if today it’s no more than a page in the history of web development.</p> <p>By the way, what’s the deal with the autoprefixer? It’s very helpful. If you are not using one, head over to <a href="https://github.com/nDmitry/grunt-autoprefixer">grunt autoprefixer</a> or <a href="https://github.com/sindresorhus/gulp-autoprefixer">gulp autoprefixer</a> and expect to get your mind blown. You’ll drastically reduce the size of your CSS source and increase it’s readability. If you are using a pre-processors you will also get rid of a huge chunk of mixins.</p> <h4 id="alphabetize">3. Alphabetize</h4> <p>The last but not the least method is alphabetising properties. Many developers like the consistency that it brings, that’s why it works well for bigger teams. Any other method you might choose is often adjusted to a developer’s mindset, but this one always stays the same. However, CSS properties have a strong relationship - for example absolute positioning with z-index, left or top. It’s hard to visualise these relationships when alphabetising.</p> <p>There’s more criticism: we certainly don’t write CSS alphabetically, therefore developers have to invest extra time to alphabetize declarations after they wrote them. What about for those using a pre-processor, how will we alphabetize mixins?</p> <p>Should we actually follow any rules while writing properties? In all honesty, I never felt the need to add rules, random works just fine. At the same time, I haven’t worked in a large team of front-enders. Maybe there is a point to alphabetize, but it certainly doesn’t feel wrong to randomly write the properties for me.</p> <h4 id="shorthand-bonus">Shorthand (bonus)</h4> <p>To wrap up the properties, here’s another interesting topic. Should we use shorthand or write it all down? The answer is: it depends. If you are talking about box-model properties, it sometimes makes sense to use shorthand. I use it in the following circumstances:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">section</span><span class="p">{</span> <span class="c">/* I use shorthand for the following cases */</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">15px</span> <span class="m">20px</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">10px</span> <span class="m">15px</span> <span class="m">20px</span> <span class="m">35px</span><span class="p">;</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">30px</span> <span class="nb">auto</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>The shorthand can force values to top, right, bottom and left. Sometimes we don’t want all properties to be set. This can result in all kinds of conflicts, for example:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">section</span> <span class="p">{</span> <span class="nl">margin-top</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span> <span class="p">}</span> <span class="k">@media</span> <span class="n">only</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span><span class="m">767px</span><span class="p">){</span> <span class="nt">section</span><span class="p">{</span> <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="m">20px</span> <span class="m">0</span> <span class="m">15px</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p>In this case, I actually want to maintain the original margin-top, but the shorthand forces me to either repeat myself or even worse - set it to a different value without realizing. In this case I prefer not to use shorthand:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">section</span> <span class="p">{</span> <span class="nl">margin-top</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span> <span class="p">}</span> <span class="k">@media</span> <span class="n">only</span> <span class="n">screen</span> <span class="n">and</span> <span class="p">(</span><span class="n">min-width</span><span class="p">:</span><span class="m">767px</span><span class="p">){</span> <span class="nt">section</span><span class="p">{</span> <span class="nl">margin-right</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span> <span class="nl">margin-left</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p>For some properties it makes sense to generally use shorthand, like border-radius. There is no simple answer to ‘should I use shorthand’. My opinion is we have to think about each property and decide on the spot. I might start with shorthand and change it later when I notice some redundant properties.</p> <h3 id="splitting-css-info-files">Splitting CSS info files</h3> <p>This is were the ‘pro’ territory begins. It’s not because it’s hard to do (it used to be harder), but you have to know what you’re doing. You need to have a clear idea of what CSS you’ll write and how it’s going to be connected. Call it ‘CSS architecture’ if you will.</p> <p>One of the common ways to get started is to use the SMACSS principles. The idea is to split the rules into 5 categories:</p> <ul> <li>Base</li> <li>Layout</li> <li>Module</li> <li>State</li> <li>Theme</li> </ul> <p>Of course, there’s more to SMACSS than this - but I won’t get into details. This approach will introduce a sort of modularity to CSS, which of course is what every developer dreams of. It’s the holy grail: flexibility, maintainability, extensibility, comprehensibility - it has it all. Sounds good, let’s all do it! Only… I don’t think it really works with CSS. It could work sure, but do you know any CSS architects which can design a modular stylesheet system? I believe that most modular systems work well because of the pre-analysis and thorough planning invested into them initially. I would like to think the same is possible with CSS, but do we ever do it?</p> <p>I’ll leave this one open. However, I have to say that this kind of structure has never worked for me. My team members weren’t happy either. It’s hard to find declarations and most importantly: it doesn’t align with the cascade.</p> <h3 id="the-cascade--specificity">The cascade / specificity</h3> <p>Oh boy, this is a big one.<br /> Let’s get the basics right first and then you’ll see why specificity can be a bitch.<br /> In a nutshell, CSS properties are applied based on the following model:</p> <p><code class="highlighter-rouge">inline style &gt; ID &gt; class / attributes / pseudo-class &gt; element / pseudo-element</code></p> <p>Simply put, here’s how CSS specificity is calculated:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code> <span class="o">*</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,0,0 */</span> <span class="nt">p</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,0,1 */</span> <span class="nt">p</span><span class="nd">:first-child</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,1,1 */</span> <span class="nt">p</span><span class="nd">:first-letter</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,0,2 */</span> <span class="nt">p</span> <span class="nt">span</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,0,2 */</span> <span class="nt">p</span> <span class="nt">span</span><span class="o">+</span><span class="nt">i</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,0,3 */</span> <span class="nt">p</span> <span class="nt">span</span> <span class="nt">i</span><span class="nc">.red</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,1,3 */</span> <span class="nt">p</span><span class="nc">.red.active</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,2,1 */</span> <span class="nf">#name</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,1,0,0 */</span> <span class="nt">style</span><span class="o">=</span><span class="s1">""</span> <span class="c">/* -&gt; specificity = 1,0,0,0 */</span> </code></pre> </div> <p>By the way, if you understand this, you should never need to use important. Even if you completely mess up, you can still use tricks to avoid it, as shown in this example:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="c">/* Avoid */</span> <span class="nt">p</span><span class="nc">.red</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="no">red</span><span class="cp">!important</span><span class="p">;</span> <span class="p">}</span> <span class="nt">p</span><span class="nc">.red</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="no">blue</span><span class="p">;</span> <span class="p">}</span> <span class="c">/* If you really have to, use a trick */</span> <span class="nt">p</span><span class="nc">.red.red</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="no">red</span><span class="p">;</span> <span class="p">}</span> <span class="c">/* -&gt; specificity = 0,0,2,1 */</span> <span class="nt">p</span><span class="nc">.red</span> <span class="p">{</span> <span class="nl">color</span><span class="p">:</span> <span class="no">blue</span><span class="p">;</span> <span class="p">}</span> <span class="c">/* -&gt; specificity = 0,0,1,1 */</span> <span class="c">/* the color of p.red will be red in both cases */</span> </code></pre> </div> <p>Finally, let’s focus on two of the declarations above.</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">p</span><span class="nd">:first-child</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,0,2 */</span> <span class="nt">p</span> <span class="nt">span</span> <span class="p">{}</span> <span class="c">/* -&gt; specificity = 0,0,0,2 */</span> </code></pre> </div> <p>The specificity is the same for both declarations. In this case, the selector that comes last in the file wins (‘p span’). You can imagine how splitting CSS into many files can be problematic.</p> <p>That’s the least of the problems though. Specificity can single-handedly ruin CSS for us. It’s a frequent problem with frameworks, content management systems, style guides and of course pre-processors. The real issue is that when CSS is specific, it forces us to keep writing more and more specific selectors. We then easily end up with redundant, complicated and disorganized stylesheets.</p> <blockquote> <p>Rule number one while writing CSS: be specific only when truly necessary.</p> </blockquote> <h3 id="element-tree-indentation">Element tree indentation</h3> <p>Specificity used to be a common problem with pre-processor users. The advertisement for both SASS and LESS that got a lot of developers to use them was: ‘reflect your HTML (or DOM) structure in your stylesheet’.</p> <p>Let’s see what that means. For the follwing HTML:</p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;header&gt;</span> <span class="nt">&lt;section</span> <span class="na">class=</span><span class="s">“navigation”</span><span class="nt">&gt;</span> <span class="nt">&lt;a</span> <span class="na">href=</span><span class="s">“home”</span> <span class="na">class=</span><span class="s">“logo”</span><span class="nt">&gt;</span>...<span class="nt">&lt;/a&gt;</span> <span class="nt">&lt;/section&gt;</span> <span class="nt">&lt;/header&gt;</span> </code></pre> </div> <p>Instead of this CSS:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nt">header</span><span class="p">{</span> <span class="nl">background-color</span><span class="p">:</span> <span class="no">blue</span><span class="p">;</span> <span class="p">}</span> <span class="nt">header</span> <span class="nc">.navigation</span> <span class="p">{</span> <span class="nl">width</span><span class="p">:</span> <span class="m">300px</span><span class="p">;</span> <span class="p">}</span> <span class="nt">header</span> <span class="nc">.navigation</span> <span class="nc">.logo</span> <span class="p">{</span> <span class="nl">max-height</span><span class="p">:</span> <span class="m">20px</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>We could write the following LESS/SASS code:</p> <div class="language-sass highlighter-rouge"><pre class="highlight"><code><span class="nt">header</span><span class="err">{</span> <span class="nl">background-color</span><span class="p">:</span> <span class="no">blue</span><span class="err">;</span> <span class="nc">.navigation</span> <span class="err">{</span> <span class="nl">width</span><span class="p">:</span> <span class="m">300px</span><span class="err">;</span> <span class="nc">.logo</span> <span class="err">{</span> <span class="nl">max-height</span><span class="p">:</span> <span class="m">20px</span><span class="err">;</span> <span class="err">}</span> <span class="err">}</span> <span class="err">}</span> </code></pre> </div> <p>If that doesn’t impress you, it should. It helps us easier implement something called OOCSS - you guessed it - object oriented CSS. It’s not object oriented in a true sense, but it encourages the development of components that are independent, self-contained and highly reusable (there’s also more to OOCSS, but I again won’t go into details).</p> <p>The downside of this is being specific all the time, which we should avoid. Be specific only when truly necessary, remember?<br /> There’s a way around this too.</p> <h3 id="block-element-modifier">Block, element, modifier</h3> <p>BEM is a naming convention that makes css selectors more informative and adds a certain amount of strictness. The pattern it introduces is the following:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nc">.navigation</span> <span class="p">{}</span> <span class="c">/* Block */</span> <span class="nc">.navigation__logo</span> <span class="p">{}</span> <span class="c">/* Element */</span> <span class="nc">.navigation--collapsed</span> <span class="p">{}</span> <span class="c">/* Modifier */</span> </code></pre> </div> <p>Specificity is now achieved by the naming conventions. What it lacks is the nesting of pre-processors, but nothing stops us from writing it like this:</p> <div class="language-css highlighter-rouge"><pre class="highlight"><code><span class="nc">.navigation</span> <span class="p">{}</span> <span class="nc">.navigation__logo</span> <span class="p">{}</span> <span class="nc">.navigation__logo__image</span> <span class="p">{}</span> <span class="nc">.navigation--collapsed</span> <span class="p">{}</span> </code></pre> </div> <p>Keep in mind that there are several variations of BEM conventions, but the core concept is the same for all of them. For some time now pre-processors introduced BEM support, which enables us to simulate the DOM structure &amp; use proper nesting - without having specificity troubles. Both LESS and SASS support a form of the following:</p> <div class="language-sass highlighter-rouge"><pre class="highlight"><code><span class="nc">.navigation</span> <span class="err">{</span> <span class="k">&amp;</span><span class="nt">__logo</span> <span class="err">{</span> <span class="k">&amp;</span><span class="nt">__image</span> <span class="err">{}</span> <span class="err">}</span> <span class="k">&amp;</span><span class="nt">--collapsed</span> <span class="err">{}</span> <span class="err">}</span> </code></pre> </div> <p>That’s neat. As long as we don’t mind the ‘ugly’ HTML that comes out of it, it’s a very reasonable solution. The ‘ugly’ problem is discussed in <a href="http://csswizardry.com/2013/01/mindbemding-getting-your-head-round-bem-syntax/">this article</a> and I think the author has some good points. We shouldn’t be dismissing this concept just because it looks odd.</p> <h2 id="my-conclusion-so-far">My conclusion so far</h2> <p>There’s more to cover, but I feel it was too optimistic to do it all in 1 article. A particularly interesting subject are design style guides, which I plan to write about in a future post. Don’t forget: write meaningful names for your selectors and pay attention to your HTML structure, many CSS problems start there.</p> <p>What else do you use? Do you write a table of contents? Do you have global classes like ‘.hidden’ or ‘.disabled’? How do you organize media queries? What about tools like <a href="https://github.com/SlexAxton/css-colorguard">colorguard</a>, <a href="https://github.com/Huddle/PhantomCSS">PhantomCSS</a> or <a href="https://github.com/katiefenn/parker">parker</a>, can they help us organize our CSS better?</p> <p>There’s more. Self contained components à la React or Polymer. Are they the future?</p> <p>Those are many interesting questions, but you scrolled this far to see a conclusion. Here it is: try out different methods until you and your team members are satisfied. There are many ways to organize stylesheets, but none of them are going to solve all of our problems. Me? I can’t go back from LESS, autoprefixer, and cssmin. I try to write my stylesheets in a OOCSS-way, which sometimes works. Lastly, I think BEM is neat and will definitely push it more in my future projects.</p> Pre-loading partial loader translations 2015-04-09T16:20:16+02:00 https://fadeit.dk/blog /2015/04/09/preload-angular-translate-partial-loader <h2 id="introduction">Introduction</h2> <p><a href="https://angular-translate.github.io/">Angular-translate</a> is the de-facto internationalisation module for AngularJS applications. Basically it works by replacing translation keys from the translation dictionary. The problem is that larger applications have a lot of content, thus a lot of translations so it doesn’t make sense to send the entire translations file to the client on initial page load. The good news is that there is a solution - <a href="https://github.com/angular-translate/angular-translate/blob/master/src/service/loader-partial.js">partial loader</a>. It works as advertised - we can define translation files that are required to load for that page, thus lowering the amount of data required to transfer. The downside to partial loading is that first browser must initialize angular, which in turn will start loading the translation partials. The issue becomes apparent on slower connections where translation keys are either shown or invisible until partial loader finishes fetching the files. It can be a factor when trying to make a first impression. What if we could pre-load some parts that are common throughout our application? It would help improve the user experience on most pages, but will not eliminate the use of partial translations.</p> <p>Behind the scenes the partial loader uses angular’s <code class="highlighter-rouge">$http</code> service to fetch a JSON file and store it in an array. Fortunately for us, angular has a built-in caching mechanism for <code class="highlighter-rouge">$http</code> service that we could leverage for our purpose. We are going to store the parts we want to pre-load in the $http cache, so when partial loader tries to fetch the files, it will hit the cache.<br />By default the caching mechanism for <code class="highlighter-rouge">$http</code> service is using <code class="highlighter-rouge">$cacheFactory</code>’s cache with <code class="highlighter-rouge">'$http'</code> key, although caching mechanism can be overridden. Cache key is the request URL and value is an array containing 4 items:</p> <ul> <li>response status - 200 for success</li> <li>response headers - we leave it blank</li> <li>data - JSON for our needs</li> <li>status text - ‘OK’ for fulfilled request</li> </ul> <p>Assuming the translation file we want to pre-load is available at http://example.com/assets/translations/da-dk/common.json, let’s test out the concept by creating a function that fakes cache entry for us:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'app'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'pascalprecht.translate'</span><span class="p">]);</span> <span class="nx">app</span><span class="p">.</span><span class="nx">config</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$translateProvider</span><span class="p">)</span> <span class="p">{</span> <span class="nx">$translateProvider</span><span class="p">.</span><span class="nx">useLoader</span><span class="p">(</span><span class="s1">'$translatePartialLoader'</span><span class="p">,</span> <span class="p">{</span> <span class="na">urlTemplate</span><span class="p">:</span> <span class="s1">'/assets/translations/{lang}/{part}.json'</span><span class="p">,</span> <span class="na">$http</span><span class="p">:</span> <span class="p">{</span> <span class="na">cache</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span> <span class="c1">//Enable caching for PartialLoader</span> <span class="p">});</span> <span class="p">});</span> <span class="nx">app</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="kd">function</span> <span class="nx">run</span> <span class="p">(</span><span class="nx">$cacheFactory</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">httpCache</span> <span class="o">=</span> <span class="nx">$cacheFactory</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">'$http'</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">putHttpCache</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">putHttpCache</span><span class="p">(</span><span class="nx">cacheKey</span><span class="p">,</span> <span class="nx">cacheValue</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">cacheObj</span> <span class="o">=</span> <span class="p">[</span><span class="mi">200</span><span class="p">,</span> <span class="nx">cacheValue</span><span class="p">,</span> <span class="p">{},</span> <span class="s2">"OK"</span><span class="p">];</span> <span class="nx">httpCache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="nx">cacheKey</span><span class="p">,</span> <span class="nx">cacheObj</span><span class="p">);</span> <span class="p">};</span> <span class="kd">var</span> <span class="nx">cacheValue</span> <span class="o">=</span> <span class="nx">angular</span><span class="p">.</span><span class="nx">toJson</span><span class="p">({</span><span class="na">TEST_KEY</span><span class="p">:</span> <span class="s2">"Cached key test"</span><span class="p">});</span> <span class="nx">putHttpCache</span><span class="p">(</span><span class="s1">'/assets/translations/da-dk/common.json'</span><span class="p">,</span> <span class="nx">cacheValue</span><span class="p">);</span> <span class="p">});</span> </code></pre> </div> <p>It works, however we have translation keys in code. Now we need to come up with a way to ship the desired JSON files at initial application load. An approach would be to keep translations in a .js file instead (an angular service, returning JSON string on call), however that would require us to migrate existing JSON files into Javascript objects and reduce translation maintainability. But wait, there is a Grunt plugin that already stores HTML templates in a similar way (<a href="https://github.com/karlgoldstein/grunt-html2js">html2js</a>). The only difference is that the plugin uses <code class="highlighter-rouge">$templateCache</code> instead. Since <code class="highlighter-rouge">html2js</code> is part of our build process already, what we can do is pass the JSON translations to the angular app using the existing Grunt build task. Once angular is bootstrapped, we’ll load translations from <code class="highlighter-rouge">$templateCache</code> and store them into the <code class="highlighter-rouge">$cacheFactory</code> cache as well. Here’s how the Grunt task for storing .json files in <code class="highlighter-rouge">$templateCache</code> looks like:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">html2js</span><span class="err">:</span> <span class="p">{</span> <span class="c1">//...template caching task omitted...</span> <span class="nl">translations</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">htmlmin</span><span class="p">:</span> <span class="p">{},</span> <span class="c1">//override minification settings</span> <span class="nx">base</span><span class="err">:</span> <span class="s1">'src'</span> <span class="p">},</span> <span class="nx">src</span><span class="err">:</span> <span class="p">[</span> <span class="s1">'src/assets/translations/**/*.json'</span> <span class="p">],</span> <span class="nx">dest</span><span class="err">:</span> <span class="s1">'&lt;%= build_dir %&gt;/templates-translations.js'</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p>Let’s now ‘teach’ the <code class="highlighter-rouge">putHttpCache</code> method to take translations from <code class="highlighter-rouge">$templateCache</code>:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">app</span><span class="p">.</span><span class="nx">run</span><span class="p">(</span><span class="kd">function</span> <span class="nx">run</span> <span class="p">(</span><span class="nx">$cacheFactory</span><span class="p">,</span> <span class="nx">$templateCache</span><span class="p">)</span> <span class="p">{</span> <span class="kd">var</span> <span class="nx">httpCache</span> <span class="o">=</span> <span class="nx">$cacheFactory</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="s1">'$http'</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">putHttpCache</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">putHttpCache</span><span class="p">(</span><span class="nx">cacheKey</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">cacheObj</span> <span class="o">=</span> <span class="p">[</span><span class="mi">200</span><span class="p">,</span> <span class="nx">$templateCache</span><span class="p">.</span><span class="nx">get</span><span class="p">(</span><span class="nx">cacheKey</span><span class="p">),</span> <span class="p">{},</span> <span class="s2">"OK"</span><span class="p">];</span> <span class="nx">httpCache</span><span class="p">.</span><span class="nx">put</span><span class="p">(</span><span class="s1">'/'</span> <span class="o">+</span> <span class="nx">cacheKey</span><span class="p">,</span> <span class="nx">cacheObj</span><span class="p">);</span> <span class="c1">//prepend '/' to make url's match</span> <span class="p">};</span> <span class="nx">putHttpCache</span><span class="p">(</span><span class="s1">'assets/translations/da-dk/common.json'</span><span class="p">);</span> <span class="p">});</span> </code></pre> </div> <h2 id="conclusion">Conclusion</h2> <p>Now we can take the best of both worlds - preload most frequently used translations and lazy-load translations for less visited pages. Our html2js build process works in similar manner - all templates ending with <code class="highlighter-rouge">*.tpl2js.html</code> are stored in <code class="highlighter-rouge">$templateCache</code>, whereas templates ending with <code class="highlighter-rouge">*.tpl.html</code> are only fetched on demand.</p> AngularJS SEO friendly translation URLs with ui-router 2015-03-30T18:10:55+02:00 https://fadeit.dk/blog /2015/03/30/angular-translate-ui-router-seo <h2 id="introduction">Introduction</h2> <p>In a <a href="https://fadeit.dk/blog/post/angularjs-seo-for-angular-translate">previous</a> post we explored how to introduce unique URLs per language using primarily NGINX and Grunt configuration. To recap, that approach is reasonable if you already have a working application and prefer not to re-factor every state and view that links to it. While it may be easier to implement in an existing application, it forces users to perform a refresh when switching between languages - though not a big issue (how often do users switch back-and-forth anyway), having a Single Page Application (SPA) that forces users to reload pages is sub-optimal. In this article we will explore possibilities of implementing SEO friendly URLs using ui-router.</p> <p>Nested states is the key to introducing language as a URL variable to all states. We start off by creating the ‘app’ state that every other state will be child of. The app state is abstract, so that the state by itself can not be activated. Next we want the route to only match the languages we intend to support (in this example ‘da’ and ‘en’) to reduce possible false-positive matches e.g example.com/<b>da</b>ta. As ui-router injects the content of the child states into ui-view, we define it as an inline template for the parent route. We also define ‘app.home’ state that will handle example.com/en and example.com/da pages:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">$stateProvider</span><span class="p">.</span><span class="nx">state</span><span class="p">(</span><span class="s1">'app'</span><span class="p">,</span> <span class="p">{</span> <span class="na">abstract</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">url</span><span class="p">:</span> <span class="s1">'/{lang:(?:da|en)}'</span><span class="p">,</span> <span class="na">template</span><span class="p">:</span> <span class="s1">'&lt;ui-view/&gt;'</span> <span class="p">});</span> <span class="nx">$stateProvider</span><span class="p">.</span><span class="nx">state</span><span class="p">(</span><span class="s1">'app.home'</span><span class="p">,</span> <span class="p">{</span> <span class="na">url</span><span class="p">:</span> <span class="s1">''</span><span class="p">,</span> <span class="na">templateUrl</span><span class="p">:</span> <span class="s1">'views/home-page.html'</span><span class="p">,</span> <span class="p">});</span> </code></pre> </div> <p>Now every state that we want to translate should be a child of app state:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">$stateProvider</span><span class="p">.</span><span class="nx">state</span><span class="p">(</span><span class="s1">'app.about'</span><span class="p">,</span> <span class="p">{</span> <span class="na">url</span><span class="p">:</span> <span class="s1">'/about'</span><span class="p">,</span> <span class="na">templateUrl</span><span class="p">:</span> <span class="s1">'views/about-page.html'</span><span class="p">,</span> <span class="p">});</span> </code></pre> </div> <p>Since the child states contain a language path variable, we need to alter the links pointing to the child states to pass in <code class="highlighter-rouge">lang</code> parameter. But before we do so, we need to set <code class="highlighter-rouge">activeLang</code> on <code class="highlighter-rouge">$rootScope</code> so we can read it from there when generating links with <code class="highlighter-rouge">ui-sref</code>. Due to the fact that <code class="highlighter-rouge">activeLang</code> can change at state change, we need to subscribe to <code class="highlighter-rouge">stateChangeSuccess</code> event. While at it, we can also generate links for the same page in other languages - ‘In English’ link at example.com/da/about should point to example.com/en/about. In this simple example we use only two languages, so we’ll be using only one variable <code class="highlighter-rouge">otherLangURL</code>:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">navigationController</span><span class="p">(</span><span class="nx">$scope</span><span class="p">,</span> <span class="nx">$rootScope</span><span class="p">,</span> <span class="nx">$stateParams</span><span class="p">,</span> <span class="nx">$translate</span><span class="p">){</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">$on</span><span class="p">(</span><span class="s1">'$stateChangeSuccess'</span><span class="p">,</span> <span class="kd">function</span> <span class="nx">rootStateChangeSuccess</span><span class="p">(</span><span class="nx">event</span><span class="p">,</span> <span class="nx">toState</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="nx">$stateParams</span><span class="p">.</span><span class="nx">lang</span> <span class="o">!==</span> <span class="kc">undefined</span><span class="p">){</span> <span class="kd">var</span> <span class="nx">otherLang</span> <span class="o">=</span> <span class="nx">$stateParams</span><span class="p">.</span><span class="nx">lang</span> <span class="o">===</span> <span class="s1">'da'</span> <span class="p">?</span> <span class="s1">'en'</span> <span class="p">:</span> <span class="s1">'da'</span><span class="p">;</span> <span class="nx">$rootScope</span><span class="p">.</span><span class="nx">activeLang</span> <span class="o">=</span> <span class="nx">$stateParams</span><span class="p">.</span><span class="nx">lang</span><span class="p">;</span> <span class="nx">$rootScope</span><span class="p">.</span><span class="nx">otherLangURL</span> <span class="o">=</span> <span class="nx">$location</span><span class="p">.</span><span class="nx">absUrl</span><span class="p">().</span><span class="nx">replace</span><span class="p">(</span><span class="s1">'/'</span> <span class="o">+</span> <span class="nx">$stateParams</span><span class="p">.</span><span class="nx">lang</span><span class="p">,</span> <span class="s1">'/'</span> <span class="o">+</span><span class="nx">otherLang</span><span class="p">);</span> <span class="nx">$translate</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">$stateParams</span><span class="p">.</span><span class="nx">lang</span><span class="p">);</span> <span class="p">}</span> <span class="p">});</span> <span class="p">}</span> </code></pre> </div> <p>It would be nice to use <code class="highlighter-rouge">ui-sref</code> to dynamically generate the links to same page in other languages, but unfortunately <code class="highlighter-rouge">ui-sref</code> does not support variables in state names, therefore we have to fall back to <code class="highlighter-rouge">ng-href</code> for generating the links. Fortunately path variable substitution is supported, therefore we can generate menu links using <code class="highlighter-rouge">activeLang</code> variable we stored on <code class="highlighter-rouge">$rootScope</code>:</p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;ul</span> <span class="na">class=</span><span class="s">"menu-navigation"</span><span class="nt">&gt;</span> ... <span class="nt">&lt;li</span> <span class="na">ui-sref-active=</span><span class="s">"activeMenu"</span><span class="nt">&gt;</span> <span class="nt">&lt;a</span> <span class="na">href</span> <span class="na">ui-sref=</span><span class="s">"app.about({lang: activeLang})"</span> <span class="na">translate</span><span class="nt">&gt;</span> NAV_ABOUT <span class="nt">&lt;/a&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;li&gt;</span> <span class="nt">&lt;a</span> <span class="na">ng-href=</span><span class="s">""</span> <span class="na">translate</span><span class="nt">&gt;</span> IN_LANGUAGE <span class="nt">&lt;/a&gt;</span> <span class="nt">&lt;/li&gt;</span> <span class="nt">&lt;/ul&gt;</span> </code></pre> </div> <h2 id="conclusion">Conclusion</h2> <p>Incorporating language in the application routes from very start will save time in the long run as re-factoring hundreds of <code class="highlighter-rouge">ui-sref</code> links will take it’s time. Ui-router fully supports nested sub-states, so there will not be any conflict if your application is already using nested states, just make sure that there is <code class="highlighter-rouge">ui-view</code> for each parent state to inject the content into. I was unable to make the lang parameter in the parent state optional, thus language will be enforced in every path (as opposed to <a href="https://fadeit.dk/blog/post/angularjs-seo-for-angular-translate">previous post</a> where we were able to use paths without language defined in them). Have a better approach? leave a comment!</p> AngularJS SEO friendly translations 2015-03-12T16:30:16+01:00 https://fadeit.dk/blog /2015/03/12/angularjs-seo-for-angular-translate <p><strong>Update</strong>: I have written a follow-up post solving the same problem using ui-router <a href="https://fadeit.dk/blog/post/angular-translate-ui-router-seo">here</a></p> <h2 id="introduction">Introduction</h2> <p>Instant translations, this is what <a href="http://angular-translate.github.io/">angular-translate</a> brings to the table. While it is significantly more convenient than waiting one more site load cycle for a different language to load (especially on sites that are slow to begin with), it doesn’t come without complications. If each translation language does not have a unique URL, search engines can not link search results to it, let alone crawl the site properly.</p> <p>Here’s what Google has to say on the matter:</p> <blockquote> <p><strong>Make sure each language version is easily discoverable</strong> <em>Keep the content for each language on separate URL’s. Don’t use cookies to show translated versions of the page. Consider cross-linking each language version of a page.</em> <a href="https://support.google.com/webmasters/answer/182192">https://support.google.com/webmasters/answer/182192</a></p> </blockquote> <h2 id="options">Options</h2> <p>Okay, perhaps it’s a nice practice to have language in the URL. Here are the options:</p> <ol> <li>Store the language in a GET parameter e.g: <strong>example.com/about?lang=en</strong></li> <li>Store the language in a path e.g: <strong>example.com/en/about</strong></li> <li>Store the language in a sub-domain e.g: <strong>en.example.com/about</strong></li> </ol> <p>While the first option would be the easiest to implement, it is also the ugliest. The other two are relatively similar in terms of appearance and it boils down to your personal preference. An important distincion to make is that if language is stored in a sub-domain, it excludes the possibility of handling the routing with angular as subdomain is out of application router scope. We decided to explore the second option - having the language in a URL path.</p> <p>The preferred approach would be to introduce a path variable on a router base path and have all children be relative to it or escape the language before routing is performed. While it is possible, it seems that the only way to achieve it is to nest all states in the following manner:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">angular</span><span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="s1">'app'</span><span class="p">).</span><span class="nx">config</span><span class="p">([</span> <span class="s1">'$stateProvider'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">$stateProvider</span><span class="p">)</span> <span class="p">{</span> <span class="nx">$stateProvider</span><span class="p">.</span><span class="nx">state</span><span class="p">(</span><span class="s1">'app'</span><span class="p">,</span> <span class="p">{</span> <span class="na">abstract</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">url</span><span class="p">:</span> <span class="s1">'/{lang:(?:da|en)}'</span><span class="p">,</span> <span class="na">template</span><span class="p">:</span> <span class="s1">'&lt;ui-view/&gt;'</span><span class="p">,</span> <span class="na">controller</span><span class="p">:</span> <span class="s1">'RootController'</span> <span class="p">});</span> <span class="nx">$stateProvider</span><span class="p">.</span><span class="nx">state</span><span class="p">(</span><span class="s1">'app.root'</span><span class="p">,</span> <span class="p">{</span> <span class="na">url</span><span class="p">:</span> <span class="s1">''</span><span class="p">,</span> <span class="na">templateUrl</span><span class="p">:</span> <span class="s1">'views/home.html'</span><span class="p">,</span> <span class="p">});</span> <span class="nx">$stateProvider</span><span class="p">.</span><span class="nx">state</span><span class="p">(</span><span class="s1">'app.about'</span><span class="p">,</span> <span class="p">{</span> <span class="na">url</span><span class="p">:</span> <span class="s1">''</span><span class="p">,</span> <span class="na">templateUrl</span><span class="p">:</span> <span class="s1">'views/about.html'</span><span class="p">,</span> <span class="p">});</span> <span class="p">}</span> <span class="p">]);</span> </code></pre> </div> <p>It is likely that by the time SEO becomes a priority, the application is already built and refactoring code does not sound appealing. To name a few, using ui-router has following downsides:</p> <ul> <li>Language slug becomes mandatory for each state, so example.com/about would become example.com/da/about</li> <li>All child state names need to be changed</li> <li>All links have to be refactored</li> </ul> <p>We’ll be taking a closer look at ui-router in a separate post soon, while this article focuses on finding an alternative configuration-based approach.</p> <h2 id="automation-to-the-rescue">Automation to the rescue!</h2> <p>Hosting Angular application is just serving static files, where each URL maps to a directory/file by default. Therefore, if we are to host the same application in a different directory for each language, we get SEO friendly URLs without a major overhaul of the code. If you are working on a big Angular project, the chances are that either Grunt or Gulp is in the mix. Grunt is our weapon of choice, so we need to add an extra copy task at the end of the build process that would copy all files into a subdirectory:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">copy</span><span class="err">:</span> <span class="p">{</span> <span class="nl">build_en_lang</span><span class="p">:</span> <span class="p">{</span> <span class="nl">files</span><span class="p">:</span> <span class="p">[</span> <span class="p">{</span> <span class="na">src</span><span class="p">:</span> <span class="p">[</span> <span class="s1">'**'</span><span class="p">,</span> <span class="s1">'!en/**'</span><span class="p">],</span> <span class="na">dest</span><span class="p">:</span> <span class="s1">'&lt;%= build_dir %&gt;/en'</span><span class="p">,</span> <span class="na">cwd</span><span class="p">:</span> <span class="s1">'&lt;%= build_dir %&gt;'</span><span class="p">,</span> <span class="na">expand</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}</span> <span class="p">]</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p><code class="highlighter-rouge">!en/**</code> tells Grunt not to copy itself every time that task is called.</p> <p>The directory structure should be following:</p> <ul> <li>/path/to/app/index.html (will default to Danish)</li> <li>/path/to/app/other/files</li> <li>/path/to/app/en/index.html</li> <li>/path/to/app/en/other/files</li> </ul> <p>Now we need to tell Angular to check for URL when determining language:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">absUrl</span> <span class="o">=</span> <span class="nx">$location</span><span class="p">.</span><span class="nx">absUrl</span><span class="p">();</span> <span class="k">if</span><span class="p">(</span><span class="nx">absUrl</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">'/en/'</span><span class="p">)</span> <span class="o">!==</span> <span class="o">-</span><span class="mi">1</span><span class="p">){</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">activeLang</span> <span class="o">=</span> <span class="s1">'en-us'</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span><span class="p">{</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">activeLang</span> <span class="o">=</span> <span class="s1">'da-dk'</span><span class="p">;</span> <span class="p">}</span> <span class="nx">$translate</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">$scope</span><span class="p">.</span><span class="nx">activeLang</span><span class="p">);</span> </code></pre> </div> <p><code class="highlighter-rouge">activeLang</code> is stored on scope because it will be later used to determine the class of language switching button</p> <p>Google also suggests cross-linking each language version of the page. If user navigates to a different page, we should also update the link that points to the same page in other languages. For that we use $stateChangeSuccess event:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="c1">//Construct url base</span> <span class="kd">var</span> <span class="nx">port</span> <span class="o">=</span> <span class="nx">$location</span><span class="p">.</span><span class="nx">port</span><span class="p">();</span> <span class="c1">//Port can be omitted on 80 or 443</span> <span class="nx">port</span> <span class="o">=</span> <span class="p">(</span><span class="nx">port</span> <span class="o">===</span> <span class="mi">80</span> <span class="o">||</span> <span class="nx">port</span> <span class="o">===</span> <span class="mi">443</span><span class="p">)</span> <span class="p">?</span> <span class="s1">''</span> <span class="p">:</span> <span class="s1">':'</span> <span class="o">+</span> <span class="nx">port</span><span class="p">;</span> <span class="kd">var</span> <span class="nx">urlBase</span> <span class="o">=</span> <span class="nx">$location</span><span class="p">.</span><span class="nx">protocol</span><span class="p">()</span> <span class="o">+</span> <span class="s1">'://'</span> <span class="o">+</span> <span class="nx">$location</span><span class="p">.</span><span class="nx">host</span><span class="p">()</span> <span class="o">+</span> <span class="nx">port</span><span class="p">;</span> <span class="nx">$rootScope</span><span class="p">.</span><span class="nx">$on</span><span class="p">(</span><span class="s1">'$stateChangeSuccess'</span><span class="p">,</span><span class="kd">function</span><span class="p">(){</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">enUrl</span> <span class="o">=</span> <span class="nx">urlBase</span> <span class="o">+</span> <span class="s1">'/en'</span> <span class="o">+</span> <span class="nx">$location</span><span class="p">.</span><span class="nx">url</span><span class="p">();</span> <span class="nx">$scope</span><span class="p">.</span><span class="nx">daUrl</span> <span class="o">=</span> <span class="nx">urlBase</span> <span class="o">+</span> <span class="nx">$location</span><span class="p">.</span><span class="nx">url</span><span class="p">();</span> <span class="p">});</span> </code></pre> </div> <p>The URLs need to be absolute, otherwise if this code was executed at example.com/en, it would calculate the ‘en’ URL to be at example.com/en/en and so on.</p> <p>Next we need to display our cross-links in the template using <code class="highlighter-rouge">ng-href</code> directive:</p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;a</span> <span class="na">ng-href=</span><span class="s">""</span> <span class="na">target=</span><span class="s">"_self"</span> <span class="na">ng-class=</span><span class="s">"{'active': activeLang === 'en-us'}"</span><span class="nt">&gt;</span> <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"assets/images/gb.svg"</span> <span class="na">alt=</span><span class="s">"Change language to English"</span><span class="nt">/&gt;</span> <span class="nt">&lt;/a&gt;</span> <span class="nt">&lt;a</span> <span class="na">ng-href=</span><span class="s">""</span> <span class="na">target=</span><span class="s">"_self"</span> <span class="na">ng-class=</span><span class="s">"{'active': activeLang === 'da-dk'}"</span><span class="nt">&gt;</span> <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"assets/images/dk.svg"</span> <span class="na">alt=</span><span class="s">"Change language to Danish"</span><span class="nt">/&gt;</span> <span class="nt">&lt;/a&gt;</span> </code></pre> </div> <p><code class="highlighter-rouge">target='_self'</code> is used to tell Angular not to load this link in ajax but to navigate to it the Angular way. We need the browser to fetch appropriate index.html page and bootstrap the application (since we have one application per language).</p> <p>The application is now SEO friendly, but we are not quite done yet. We are also using HTML5 mode to have pretty URLs. When using HTML5 mode, we must define base tag in the index file so Angular knows where the application path starts from:</p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span> <span class="nt">&lt;html</span> <span class="na">lang=</span><span class="s">"da"</span> <span class="na">ng-app=</span><span class="s">"example"</span> <span class="na">ng-controller=</span><span class="s">"AppCtrl"</span><span class="nt">&gt;</span> <span class="nt">&lt;head&gt;</span> <span class="nt">&lt;base</span> <span class="na">href=</span><span class="s">"/"</span><span class="nt">&gt;</span> <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"alternate"</span> <span class="na">hreflang=</span><span class="s">"en"</span> <span class="na">href=</span><span class="s">"http://example.com/en"</span><span class="nt">/&gt;</span> <span class="nt">&lt;link</span> <span class="na">rel=</span><span class="s">"alternate"</span> <span class="na">hreflang=</span><span class="s">"da"</span> <span class="na">href=</span><span class="s">"http://example.com"</span><span class="nt">/&gt;</span> ... </code></pre> </div> <p>In case <code class="highlighter-rouge">hreflang</code> caught your eye, you can read more about it <a href="https://support.google.com/webmasters/answer/182192">here</a>.</p> <p>However, now we also have a copy of the site running at /en, so the base tag needs to be appropriate.</p> <h2 id="grunt-to-the-rescue-again">Grunt to the rescue, again!</h2> <p>We need to update the base tag when making a copy of the site to match the path. Grunt’s copy task features a process option that is called for each file copied. The process option enables us to transform the contents of the file:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">copy</span><span class="err">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">processContent</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">contents</span><span class="p">,</span> <span class="nx">srcpath</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span><span class="p">(</span><span class="nx">srcpath</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="s1">'index.html'</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">0</span><span class="p">){</span> <span class="c1">//note spaces around in order not to match 'hreflang' tag</span> <span class="nx">contents</span> <span class="o">=</span> <span class="nx">contents</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s1">' lang="da" '</span><span class="p">,</span> <span class="s1">' lang="en" '</span><span class="p">);</span> <span class="nx">contents</span> <span class="o">=</span> <span class="nx">contents</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s1">'&lt;base href="/"&gt;'</span><span class="p">,</span> <span class="s1">'&lt;base href="/en/"&gt;'</span><span class="p">);</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">contents</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">//build_en_lang omitted</span> <span class="p">}</span> </code></pre> </div> <p>We are using older version of Grunt, so the option is called <code class="highlighter-rouge">processContent</code> instead of <code class="highlighter-rouge">process</code>.</p> <p>HTML5 mode also requires URL rewriting on the server side. If a request is made at example.com/about, the server should still return the index.html page and it is up to Angular router to figure out what to do with the ‘about’ path variable. In Nginx, this is typically achieved with try_files directive:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>location / <span class="o">{</span> try_files <span class="nv">$uri</span> <span class="nv">$uri</span>/ <span class="nv">$uri</span>/index.html /index.html; <span class="o">}</span> </code></pre> </div> <p>If a request is made at example.com/en/about, the configuration above will result in Nginx trying following files:</p> <ul> <li>en/about –&gt; not found</li> <li>en/about/ –&gt; not found</li> <li>en/about/index.html –&gt; not found</li> <li>index.html –&gt; found</li> </ul> <p>This results in the server returning the index.html file of the root rather than of ‘en’ directory. Now the router is trying to figure out which state to match the <code class="highlighter-rouge">en/about</code> path to, which it will of course fail to do accomplish. We need to define another location to load proper index.html, if $uri is not a match:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>location /en/ <span class="o">{</span> try_files <span class="nv">$uri</span> <span class="nv">$uri</span>/ <span class="nv">$uri</span>/index.html /en/index.html; <span class="o">}</span> </code></pre> </div> <p>Finally, SEO friendly multilingual application!<br />Now that we’ve established that there is no way around configuring web server, let’s make most of it! Since the only file that actually changed is index.html, there is no real need to copy other resources. We start out by changing the grunt copy task to only copy index file to same directory and rename it to index.en.html:</p> <div class="language-javascript highlighter-rouge"><pre class="highlight"><code><span class="nx">copy</span><span class="p">{</span> <span class="nl">build_en_lang</span><span class="p">:</span> <span class="p">{</span> <span class="nl">options</span><span class="p">:</span> <span class="p">{</span> <span class="nl">processContent</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">contents</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//note spaces around in order not to match 'hreflang' tag</span> <span class="nx">contents</span> <span class="o">=</span> <span class="nx">contents</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s1">' lang="da" '</span><span class="p">,</span> <span class="s1">' lang="en" '</span><span class="p">);</span> <span class="nx">contents</span> <span class="o">=</span> <span class="nx">contents</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s1">'&lt;base href="/"&gt;'</span><span class="p">,</span> <span class="s1">'&lt;base href="/en/"&gt;'</span><span class="p">);</span> <span class="k">return</span> <span class="nx">contents</span><span class="p">;</span> <span class="p">},</span> <span class="p">},</span> <span class="nx">files</span><span class="err">:</span> <span class="p">[</span> <span class="p">{</span> <span class="na">src</span><span class="p">:</span> <span class="p">[</span> <span class="s1">'index.html'</span><span class="p">],</span> <span class="na">dest</span><span class="p">:</span> <span class="s1">'&lt;%= build_dir %&gt;/'</span><span class="p">,</span> <span class="na">cwd</span><span class="p">:</span> <span class="s1">'&lt;%= build_dir %&gt;'</span><span class="p">,</span> <span class="na">expand</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="na">rename</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">dest</span><span class="p">,</span> <span class="nx">src</span><span class="p">){</span> <span class="k">return</span> <span class="nx">dest</span> <span class="o">+</span> <span class="nx">src</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="s1">'.html'</span><span class="p">,</span> <span class="s1">'.en.html'</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="p">]</span> <span class="p">}</span> <span class="p">}</span> </code></pre> </div> <p>Now Nginx ‘en’ location needs to serve index.en.html instead of index.html, otherwise it should ignore /en/ part of the URL when serving static files. We will use regular expression to match /en and capture everything that comes after into $1. Then we use captured group in try_files directive:</p> <div class="language-bash highlighter-rouge"><pre class="highlight"><code>location ~ ^/en/?<span class="o">(</span>.<span class="k">*</span><span class="o">)</span><span class="nv">$ </span><span class="o">{</span> index index.en.html; try_files /<span class="nv">$1</span> /<span class="nv">$1</span>/ /index.en.html; <span class="o">}</span> </code></pre> </div> <p>We have jumped through several hoops in order to get rid of the comfort angular-translate provides. Fortunately we can bring some of it back - authenticated users are not crawlers, so we can display ‘quick buttons’ for them while displaying SEO friendly cross-links to unauthenticated users:</p> <div class="language-html highlighter-rouge"><pre class="highlight"><code><span class="nt">&lt;div</span> <span class="na">ng-if=</span><span class="s">"!auth.isAuthenticated"</span><span class="nt">&gt;</span> <span class="nt">&lt;a</span> <span class="na">ng-href=</span><span class="s">""</span> <span class="na">target=</span><span class="s">"_self"</span> <span class="na">ng-class=</span><span class="s">"{'active': activeLang === 'en-us'}"</span><span class="nt">&gt;</span> <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"assets/images/gb.svg"</span> <span class="na">alt=</span><span class="s">"Change language to English"</span><span class="nt">/&gt;</span> <span class="nt">&lt;/a&gt;</span> ... <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;div</span> <span class="na">ng-if=</span><span class="s">"auth.isAuthenticated"</span><span class="nt">&gt;</span> <span class="nt">&lt;a</span> <span class="na">ng-href=</span><span class="s">"#"</span> <span class="na">ng-click=</span><span class="s">"changeLang('en-us')"</span> <span class="na">ng-class=</span><span class="s">"{'active': activeLang === 'en-us'}"</span><span class="nt">&gt;</span> <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">"assets/images/gb.svg"</span> <span class="na">alt=</span><span class="s">"Change language to English"</span><span class="nt">/&gt;</span> <span class="nt">&lt;/a&gt;</span> ... <span class="nt">&lt;/div&gt;</span> </code></pre> </div> <h2 id="conclusion">Conclusion</h2> <p>With this approach we have tried to solve as much as possible with configuration, so we’ve managed not to entangle language awareness with every state in the application. While ui-router configuration would be preferred, it does not come without limitations: ui-router can not be used for sub-domains and ui-router enforces using language in every url rather than having it as optional parameter.</p> <p>At the end of day there is no good way to to play by SEO rules, so hopefully AngularJS 2.0 will change this.</p>