<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Nyasoft</title>
    <link>http://nya.io/</link>
    <atom:link href="/rss2.xml" rel="self" type="application/rss+xml"/>
    
    <description>Nyasoft is a blog about web and android development technology</description>
    <pubDate>Mon, 24 Apr 2017 10:26:17 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title>Angular Prerender in 5 minutes</title>
      <link>http://nya.io/uncategorized/Angular-Prerender-in-5-minutes/</link>
      <guid>http://nya.io/uncategorized/Angular-Prerender-in-5-minutes/</guid>
      <pubDate>Mon, 24 Apr 2017 09:33:24 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;Angular has a prerender support once called &lt;a href=&quot;https://github.com/angular/universeral&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Angular Unive
        
      
      </description>
      
      <content:encoded><![CDATA[<p>Angular has a prerender support once called <a href="https://github.com/angular/universeral" target="_blank" rel="noopener">Angular Universal</a> which recently merged into the official@angular/platform-server package. It’s official guide for universal is still working in progress. It requires a lot of configuration if you have readthe document. Most important is you need to setup and AOT configuration and use @angular/compiler-cli to compile your source code.</p><p>But if you want to know how to use this feature in a briefly way without touching too much concept like express. Read this article. I’ll show youan extreme concise example to setup a very basic prerender application.</p><p>Here we go.</p><h2 id="Step-1"><a href="#Step-1" class="headerlink" title="Step 1"></a>Step 1</h2><p>Setup a basic angular project. In this example, we have only one component and module</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── package.json</span><br><span class="line">├── src</span><br><span class="line">│   ├── app</span><br><span class="line">│   │   ├── app.component.ts</span><br><span class="line">│   │   ├── app.module.ts</span><br><span class="line">│   │   └── app.server.ts</span><br><span class="line">│   ├── index.html</span><br><span class="line">│   ├── main.browser.aot.ts</span><br><span class="line">│   ├── main.browser.ts</span><br><span class="line">│   └── main.server.ts</span><br><span class="line">├── tsconfig.json</span><br><span class="line">├── tsconfig.aot.json</span><br></pre></td></tr></table></figure><p>app.component.ts is a very basic component</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">import &#123;Component&#125; from &apos;@angular/core&apos;;</span><br><span class="line"></span><br><span class="line">@Component(&#123;</span><br><span class="line">    selector: &apos;app&apos;,</span><br><span class="line">    template: &apos;&lt;div&gt;It works&lt;/div&gt;&apos;</span><br><span class="line">&#125;)</span><br><span class="line">export class App &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Then I make an AppModule with BrowserModule imported. Call withServerTransition will make sure prerendered app can transit into browser module.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">import &#123;NgModule&#125; from &apos;@angular/core&apos;;</span><br><span class="line">import &#123;App&#125; from &apos;./app.component&apos;;</span><br><span class="line">import &#123;BrowserModule&#125; from &apos;@angular/platform-browser&apos;;</span><br><span class="line"></span><br><span class="line">@NgModule(&#123;</span><br><span class="line">    declarations: [App],</span><br><span class="line">    imports: [BrowserModule.withServerTransition(&#123;</span><br><span class="line">        appId: &apos;iroha&apos;</span><br><span class="line">    &#125;)],</span><br><span class="line">    bootstrap: [App]</span><br><span class="line">&#125;)</span><br><span class="line">export class AppModule &#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="Step-2"><a href="#Step-2" class="headerlink" title="Step 2"></a>Step 2</h2><p>To make sure server can bootstrap this app, A server module is also needed. The important part in AppServerModule is that it imports a module called ServerModule from @angular/platform-server. This module has some server replacement for the browser provider in BrowserModule.</p><p>After having these files in hand. use ngc (Angular Compiler) to compile these files, but before continue, let’s dig into the tsconfig.aot.json fileto see something important for compiling with ngc.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  &quot;compilerOptions&quot;: &#123;</span><br><span class="line">    &quot;module&quot;: &quot;es2015&quot;,</span><br><span class="line">    &quot;target&quot;: &quot;es5&quot;,</span><br><span class="line">    &quot;moduleResolution&quot;: &quot;node&quot;,</span><br><span class="line">    &quot;emitDecoratorMetadata&quot;: true,</span><br><span class="line">    &quot;experimentalDecorators&quot;: true,</span><br><span class="line">    &quot;allowSyntheticDefaultImports&quot;: true,</span><br><span class="line">    &quot;sourceMap&quot;: true,</span><br><span class="line">    &quot;noEmit&quot;: true,</span><br><span class="line">    &quot;noEmitHelpers&quot;: true,</span><br><span class="line">    &quot;strictNullChecks&quot;: false,</span><br><span class="line">    &quot;lib&quot;: [</span><br><span class="line">      &quot;es2015&quot;,</span><br><span class="line">      &quot;dom&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;typeRoots&quot;: [</span><br><span class="line">      &quot;node_modules/@types&quot;</span><br><span class="line">    ],</span><br><span class="line">    &quot;types&quot;: [</span><br><span class="line">      &quot;hammerjs&quot;,</span><br><span class="line">      &quot;node&quot;</span><br><span class="line">    ]</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;exclude&quot;: [</span><br><span class="line">    &quot;node_modules&quot;,</span><br><span class="line">    &quot;dist&quot;,</span><br><span class="line">    &quot;src/**/*.spec.ts&quot;</span><br><span class="line">  ],</span><br><span class="line">  &quot;awesomeTypescriptLoaderOptions&quot;: &#123;</span><br><span class="line">    &quot;forkChecker&quot;: true,</span><br><span class="line">    &quot;useWebpackText&quot;: true</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;angularCompilerOptions&quot;: &#123;</span><br><span class="line">    &quot;genDir&quot;: &quot;./compiled&quot;,</span><br><span class="line">    &quot;skipMetadataEmit&quot;: true</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;compileOnSave&quot;: false,</span><br><span class="line">  &quot;buildOnSave&quot;: false</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>To make AOT compiled code, you should use <code>es2015</code> as module resolution, this will be convenient for import ngfactory and treeshaking. alsotheir are a little options which related to angular compile. <code>angularCompilerOptions.genDir</code> will setup the output path for ngc, all your compiledmodule will be output in that path. <code>angularCompilerOptions.skipMetadataEmit</code>  property prevents the compiler from generating metadata files with the compiled application. Metadata files are not necessary when targeting TypeScript files, so there is no reason to include them.</p><p>Then, use <code>$(npm bin)/ngc -p tsconfig.aot.json</code> to generate AOT files. You’ll get compiled directory in your project root. like this.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── compiled</span><br><span class="line">│   └── src</span><br><span class="line">│       └── app</span><br><span class="line">│           ├── app.component.ngfactory.ts</span><br><span class="line">│           ├── app.component.ngsummary.json</span><br><span class="line">│           ├── app.module.ngfactory.ts</span><br><span class="line">│           ├── app.module.ngsummary.json</span><br><span class="line">│           ├── app.server.ngfactory.ts</span><br><span class="line">│           └── app.server.ngsummary.json</span><br></pre></td></tr></table></figure><h2 id="Step-3"><a href="#Step-3" class="headerlink" title="Step 3"></a>Step 3</h2><p>Make some entry files for JIT browser, AOT browser and server render.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">src/main.browser.ts,</span><br><span class="line">src/main.browser.aot.ts,</span><br><span class="line">src/main.server.ts</span><br></pre></td></tr></table></figure><p>They are almost the same. but will a little different.</p><p>src/main.browser.ts</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">import &apos;core-js/es7/reflect&apos;;</span><br><span class="line">import &apos;zone.js/dist/zone&apos;;</span><br><span class="line">import &apos;ts-helpers&apos;;</span><br><span class="line"></span><br><span class="line">import &#123;platformBrowserDynamic&#125; from &apos;@angular/platform-browser-dynamic&apos;;</span><br><span class="line">import &#123;AppModule&#125; from &apos;./app/app.module&apos;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">platformBrowserDynamic().bootstrapModule(AppModule);</span><br></pre></td></tr></table></figure><p>src/main.browser.aot.ts</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">import &apos;core-js/es7/reflect&apos;;</span><br><span class="line">import &apos;zone.js/dist/zone&apos;;</span><br><span class="line">import &apos;ts-helpers&apos;;</span><br><span class="line"></span><br><span class="line">import &#123;platformBrowser&#125; from &apos;@angular/platform-browser&apos;;</span><br><span class="line">import &#123;AppModuleNgFactory&#125; from &apos;../compiled/src/app/app.module.ngfactory&apos;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);</span><br></pre></td></tr></table></figure><p>You may have noticed, the only difference between main.browser.ts and main.browser.aot.ts is In JIT, it use the AppModule directly and bootstrapthat module using platformBrowserDynamic.bootstrapModule(), while in AOT, it use platformBrowser.bootstrapModuleFactory() to bootstrap an ngfactoryfrom compiled file.</p><p>that’s the different. AOT compiled component and in file bootstrap, it won’t load JIT compiler, this will make the final shipped code smaller.</p><p>finally, let’ look into the main.server.ts</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">import &apos;core-js/es7/reflect&apos;;</span><br><span class="line">import &apos;zone.js/dist/zone-node&apos;;</span><br><span class="line">import &apos;ts-helpers&apos;;</span><br><span class="line"></span><br><span class="line">import &#123;renderModuleFactory&#125; from &apos;@angular/platform-server&apos;;</span><br><span class="line">import &#123;AppServerModuleNgFactory&#125; from &apos;../compiled/src/app/app.server.ngfactory&apos;;</span><br><span class="line">import * as fs from &apos;fs&apos;;</span><br><span class="line">import * as path from &apos;path&apos;;</span><br><span class="line"></span><br><span class="line">let template = fs.readFileSync(path.join(__dirname, &apos;./index.html&apos;));</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    renderModuleFactory(AppServerModuleNgFactory, &#123;</span><br><span class="line">        document: template.toString(),</span><br><span class="line">        url: &apos;/&apos;</span><br><span class="line">    &#125;)</span><br><span class="line">        .then((doc: string) =&gt; &#123;</span><br><span class="line">            console.log(doc);</span><br><span class="line">        &#125;);</span><br></pre></td></tr></table></figure><p>This file looks different from the previous two. it import zone-node instead of zone, because zone-node can be run in nodejs, zone cannot.</p><p>Because what we need is a string of the final html page. a factory called renderModuleFactory from @angular/platform-server is used here tobootstrap our AppServerModuleNgFactory which is imported from compiled code.</p><p>At last, you can run ts-node to get the string of rendered app.</p><p>Is that over. exactly. No, Prerender also involve a transition step when app has loaded into browser and your browser module has taken over allthe state from server prerendered page. But this is the mainly about webpack configuration, what you need is build a bundle and shipped with thegenerated html document from renderModuleFactory. and use preboot.js to make the state transition.</p><p>By reading this sort article, you now setup a up a very basic project without touch any unnecessary configuration. but to make a useful application,you still need to learn how to tame webpack and build a bundle for browser. I may write something about this in the future.</p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/Angular-Prerender-in-5-minutes/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Make A List View In Angular</title>
      <link>http://nya.io/uncategorized/make-a-list-view-in-angular/</link>
      <guid>http://nya.io/uncategorized/make-a-list-view-in-angular/</guid>
      <pubDate>Tue, 11 Apr 2017 06:35:43 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;Recently, I’m working on an solution to show vast amount of card in a list and scroll smoothly on this list. Further more, the list shoul
        
      
      </description>
      
      <content:encoded><![CDATA[<p>Recently, I’m working on an solution to show vast amount of card in a list and scroll smoothly on this list. Further more, the list shouldshow a timeline meter on the side to allow user navigate to any position quickly just drag or click the timeline. This feature pose a challengeto develop a new kind of UI component which able to show any amount of card in screen without impact the performance.</p><p>A tradition way to show large amount of data in a list is so called <em>infinite list</em> or <em>lazy loading</em> . A list loads data at 20-30 items per page until user reachesbottom of the list. then bumps the next page of data and appends to current list. This solution actually contributes to the initial loadingperformance but did not too much to the overall scrolling performance. Using this solution, user cannot quickly scroll to the last position of data.even worse, when you have loaded 2000-3000 items, your tab crashes.</p><h2 id="Find-Another-Approach"><a href="#Find-Another-Approach" class="headerlink" title="Find Another Approach."></a>Find Another Approach.</h2><p>The reason why <em>lazy loading</em> or traditional <em>infinite list</em> doesn’t work is obvious. As long as you continue added items into your list, thoseDOM elements will exists until eats up all of your memory. So this solution is not fit for our requirements.</p><p>If you ever have some experience on Android or iOS development. You may find the solution which has developed for a long time. Yes, that’sListView (or RecyclerView, GridView) in Android and UITableView in iOS. They both use a similar idea which keep a limited amount of item on the fly and trash the view which is out of current viewport. To reduce the performance expense on create new views, they also recycle the trashed views and reuse them on demand. So this idea is what I choose for solving this problem.</p><h3 id="First-How-does-this-solution-work"><a href="#First-How-does-this-solution-work" class="headerlink" title="First, How does this solution work?"></a>First, How does this solution work?</h3><p>The core of this solution is only maintaining a limited number of views in the list and keep remove and added necessary views into list when userscrolling the list. To make this possible we need to know the height of each view before performing a layout to place those views. In Android,the framework using a measurement procedure, but at web, we don’t have that mechanism, the dimension of an DOM element is control by css alongwith the element parent. So measurement on DOM is hard to do, we don’t want to discuss this topic at this article. We use a predefined heightfor each row. By doing this, we simplify the calculation of layout.</p><p>Another requirement to make this solution possible is we need to know the total number of items of our list. This is usually not much hard to do.To make things simple, we also make a little change, we have all data loaded once, so we don’t need to write load on demand.</p><p>Once we prepared data and container finish its layout, we will get <strong>container height</strong> , <strong>number of rows</strong>, <strong>row height</strong> . these information is pretty enough for our list view to render a initial state.</p><p><img src="/img/infinite-list-1.jpg" alt="Initial State"></p><p>As you can see, our initial state only contains very limited number of elements. This is what we expect, another information we got fromthis picture is we have a scrollbar which indicates we have very long content to scroll. we use a straight forward method to calculate the totalheight of scroll content by simply multiply row height and item count. Here is the main DOM structure to make this possible.</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- infinite-list has a height of 100% viewport height this height will be used to determine how many views to render --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"infinite-list"</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- infinite-list-holder has a height of all --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"infinite-list-holder"</span> <span class="attr">style</span>=<span class="string">"height: 140000px;"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">list-item-example</span>&gt;</span><span class="tag">&lt;/<span class="name">list-item-example</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p><code>infinite-list</code> is the container of visible views, it use a <code>overflow-y: auto</code> css to make the <code>infinite-list-holder</code> scrollable.When this structure is established, browser will render a scrollbar for <code>infinite-list</code>.</p><p>When user scroll down or up, the <code>infinite-list-holder</code> will scroll and together with its view content. meanwhile we listen to thescroll event to asynchronously calculate how far we have scrolled from initial position.</p><p>THE key part of the solution can keep a low memory usage is keep calculating every view position relative to viewport, add views which are about to visible in the viewport and remove views which is already out of viewport.</p><p><img src="/img/infinite-list-2.jpg" alt="When content scrolling, remove and added views"></p><p>This is really very intuitive solution. keep added and remove views as long as there are the right views in our viewport, user will feellike our list is filled with all of the data items. OK, this is simple. But how do we know which view is out of viewport, which view isabout to add in viewport and how to place them in the correct position.</p><h3 id="Do-the-layout"><a href="#Do-the-layout" class="headerlink" title="Do the layout"></a>Do the layout</h3><p>we already have some basic information about our container <code>infinite-list</code>, we know its width and height. we also get height of every row and total number of data. when we do layout at scrolling, we need one more information to know how much have we scrolled. This is not hard to obtain. In all browser, we can use <code>element.scrollTop</code> property to know the relative distance between top of the scrolling content and  top of the wrapper content. (More detail, you can learn from <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop" target="_blank" rel="noopener">MDN</a>)</p><p>Calculate the index of top most view relative to our viewport, remove any views before that index.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">let firstIndex = Math.floor(scrollTop / rowHeight);</span><br></pre></td></tr></table></figure><p>Calculate the index of bottom most view relative to our viewport, remove any views after that index.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">// we need this offset to complement the height of viewport. because a view may just cross the border of list-container</span><br><span class="line">let firstViewOffset = scrollTop - firstIndex * rowHeight;</span><br><span class="line">// containerHeight is equal to viewport height as we assumed</span><br><span class="line">let lastIndex = Math.ceil((containerHeight + firstViewOffset) / rowHeight) + firstIndex;</span><br></pre></td></tr></table></figure><p>Once we get the index range, we can remove view not inside this range and added views according this range. To added view at correct position,we need calculate its top y coordinate relative the list-holder.This calculation is more easy. We can calculate a view y position base on row height and data item index:</p><p>A view top y position relative to infinite-list-holder is <code>rowHeight * index</code></p><p>The method we use to place our views on <code>infinite-list-holder</code> is using css3 translate3d and let each view’s position property be absolute.By using this method we get the hardware acceleration and not trigger any re-layout out side the <code>infinite-list-holder</code>. This is another important performance trick.</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">viewElement.style.transform = <span class="string">`translate3d(0, <span class="subst">$&#123;y&#125;</span>px, 0)`</span>;</span><br><span class="line">viewElement.style.webkitTransform = <span class="string">`translate3d(0, <span class="subst">$&#123;y&#125;</span>px, 0)`</span>;</span><br><span class="line">viewElement.style.width = <span class="string">`<span class="subst">$&#123;containerWidth&#125;</span>px`</span>;</span><br><span class="line">viewElement.style.height = <span class="string">`<span class="subst">$&#123;rowHeight&#125;</span>px`</span>;</span><br><span class="line">viewElement.style.position = <span class="string">'absolute'</span>;</span><br></pre></td></tr></table></figure><p>Once styles is applied. insert view into <code>infinite-list-holder</code> at proper position, browser will render the view for us.</p><p>This process will repeat whenever we need to do a layout operation. At the user aspect, the view will really scroll in the list.</p><h3 id="Optimization"><a href="#Optimization" class="headerlink" title="Optimization."></a>Optimization.</h3><p>So far so good, We have give a smooth scrolling experience to user using our real <strong>infinite-list</strong> .but currently we only create a verysimple example which using a simple structure for item view. What if we have a very complex view to render. we need to rapidly remove andadd DOM element to our list-holder, the view element may have many object to initialize. many listener to add. Creating new object especiallyDOM element object is expensive. Those views in our list are very the same structure, the only different is there content. so we decide todo some optimization to reuse the views.</p><p>To reuse view, we need modified our solution at two phase, remove and add phase. At remove phase, we don’t directly destroy that trashed view,we detach that view and move it into a recycle bin. we can create a class called <code>RecycleBin</code> to manage those recycled views. At add phase, we first try to retrieve view from RecycleBin, if we find the same view with the same index we expect, we can directly reattach it back to list-holder, because its content is the same. But if we don’t have that type of view, we can also pop a view from scrap views, and replace its binding data with correct data at certain index. If you use a MVVM framework, the framework will do the rest things. only when we don’t have scraped views from <code>RecycleBin</code>, we create a new view with data of certain index.</p><p><img src="/img/infinite-list-3.jpg" alt="RecycleBin"></p><p>Our modified version reduce the dom create and destroy, also reduce the new memory allocation and GC operation (though this is really dependon JS engine). A drawback of this modification is we increase a little more memory usage. But if you can write some simple strategy to controlthe total number of scrap views. No need to worry about run out of memory.</p><p>So far we have briefly explained how our solution work. But we haven’t explained how to implement in Angular.</p><h2 id="The-Angular-Implementation"><a href="#The-Angular-Implementation" class="headerlink" title="The Angular Implementation"></a>The Angular Implementation</h2><p><em>we only cover the Angular 4 and above, if you want to find an AngularJS (1.x) implementation. You can read the code from <a href="https://github.com/ngFancy/ng-gridview" target="_blank" rel="noopener">this repository</a> to see how toimplement this approach in Angular 1.5, although this is grid view which is a little complicated.</em></p><p>In Angular, things are not much different. We still use the theory we have explained before plus a little angular feature.</p><p>In Angular, there are component and directive ( include structural directive ) which can manipulate DOM as we need. Recall our feature requirement,Our list, in fact, has repeat structure which presents data in same form repeatedly. The idea flash in your mind first must be ngFor directive which is a structural directive can clone its content repeatedly. The difference between ngFor and our directive is ngFor render all DOM in the collection we don’t. So we use a similar directive called <code>infiniteFor</code> which has a very same usage.</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">any</span> *<span class="attr">infiniteFor</span>=<span class="string">"let row of collection"</span>&gt;</span></span><br><span class="line">    &#123;&#123;row&#125;&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">any</span>&gt;</span></span><br></pre></td></tr></table></figure><p>By using these semantic we borrowed from ngFor, we can create a local variable binding to its template from the iterable collectionIn Angular we don’t have the concept <code>scope</code> but it is a very similar concept which using an object to store your local variable and bind to the template.We won’t use too much words to describe how ngFor implement. if you don’t understand and have interest in this, you can read official document: <a href="https://angular.io/docs/ts/latest/guide/structural-directives.html#!#ngFor" target="_blank" rel="noopener">Structural Directive</a> and <a href="https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html" target="_blank" rel="noopener">ngFor API Guide</a></p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Directive</span>(&#123;</span><br><span class="line">    selector: <span class="string">'[infiniteFor][infiniteForOf]'</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">class</span> InfiniteForOf&lt;T&gt; <span class="keyword">implements</span> OnChanges, DoCheck &#123;</span><br><span class="line">    <span class="keyword">private</span> _differ: IterableDiffer&lt;T&gt;;</span><br><span class="line">    <span class="keyword">private</span> _trackByFn: TrackByFunction&lt;T&gt;;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// This is a major difference from ngFor directive, we actually need store the whole collection to a data structure (we use array)</span></span><br><span class="line">    <span class="keyword">private</span> _collection: <span class="built_in">any</span>[];</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Input</span>() infiniteForOf: NgIterable&lt;T&gt;;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Input</span>()</span><br><span class="line">    <span class="keyword">set</span> infiniteForTrackBy(fn: TrackByFunction&lt;T&gt;) &#123;</span><br><span class="line">        <span class="keyword">this</span>._trackByFn = fn;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">get</span> infiniteForTrackBy(): TrackByFunction&lt;T&gt; &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>._trackByFn;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Input</span>()</span><br><span class="line">    <span class="keyword">set</span> infiniteForTemplate(value: TemplateRef&lt;InfiniteRow&gt;) &#123;</span><br><span class="line">        <span class="keyword">if</span> (value) &#123;</span><br><span class="line">            <span class="keyword">this</span>._template = value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">constructor</span>(<span class="params"><span class="keyword">private</span> _infiniteList: InfiniteList,</span></span><br><span class="line"><span class="params">                <span class="keyword">private</span> _differs: IterableDiffers,</span></span><br><span class="line"><span class="params">                <span class="keyword">private</span> _template: TemplateRef&lt;InfiniteRow&gt;,</span></span><br><span class="line"><span class="params">                <span class="keyword">private</span> _viewContainerRef: ViewContainerRef</span>) &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ngOnChanges(changes: SimpleChanges): <span class="built_in">void</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="string">'infiniteForOf'</span> <span class="keyword">in</span> changes) &#123;</span><br><span class="line">            <span class="comment">// React on infiniteForOf only once all inputs have been initialized</span></span><br><span class="line">            <span class="keyword">const</span> value = changes[<span class="string">'infiniteForOf'</span>].currentValue;</span><br><span class="line">            <span class="keyword">if</span> (!<span class="keyword">this</span>._differ &amp;&amp; value) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">this</span>._differ = <span class="keyword">this</span>._differs.find(value).create(<span class="keyword">this</span>._trackByFn);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (e) &#123;</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`Cannot find a differ supporting object '<span class="subst">$&#123;value&#125;</span>' of type '<span class="subst">$&#123;getTypeNameForDebugging(value)&#125;</span>'. NgFor only supports binding to Iterables such as Arrays.`</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ngDoCheck(): <span class="built_in">void</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">this</span>._differ) &#123;</span><br><span class="line">            <span class="keyword">const</span> changes = <span class="keyword">this</span>._differ.diff(<span class="keyword">this</span>.infiniteForOf);</span><br><span class="line">            <span class="keyword">if</span> (changes) &#123;</span><br><span class="line">                <span class="keyword">this</span>.applyChanges(changes);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> applyChanges(changes: IterableChanges&lt;T&gt;) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">this</span>._collection) &#123;</span><br><span class="line">            <span class="keyword">this</span>._collection = [];</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">let</span> isMeasurementRequired = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">        changes.forEachOperation(<span class="function">(<span class="params">item: IterableChangeRecord&lt;<span class="built_in">any</span>&gt;, adjustedPreviousIndex: <span class="built_in">number</span>, currentIndex: <span class="built_in">number</span></span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (item.previousIndex == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// new item</span></span><br><span class="line">                isMeasurementRequired = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">this</span>._collection.splice(currentIndex, <span class="number">0</span>, item.item);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (currentIndex == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// remove item</span></span><br><span class="line">                isMeasurementRequired = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">this</span>._collection.splice(adjustedPreviousIndex, <span class="number">1</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// move item</span></span><br><span class="line">                <span class="keyword">this</span>._collection.splice(currentIndex, <span class="number">0</span>, <span class="keyword">this</span>._collection.splice(adjustedPreviousIndex, <span class="number">1</span>)[<span class="number">0</span>]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        changes.forEachIdentityChange(<span class="function">(<span class="params">record: <span class="built_in">any</span></span>) =&gt;</span> &#123;</span><br><span class="line">            <span class="keyword">this</span>._collection[record.currentIndex] = record.item;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (isMeasurementRequired) &#123;</span><br><span class="line">            <span class="keyword">this</span>.requestMeasure();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">this</span>.requestLayout();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Instead of directly react on data changes, we need to store the whole data set into a collection because we may use it later,But IterableDiffer is still a useful tool to manipulate our data collection without need to learn the actual data structure of source data.when any changes of data happen, we mark a <code>isMeasurementRequired</code> to determine whether we need to recalculate the list-holder height.This is needed when we change the size of collection. Whenever a change happen, a layout is requested.</p><p>Consider when we should do the layout. By learn the feature of our component, we can find three timing to do layout:</p><ul><li>After a measurement. (data set size changes, container dimension changes)</li><li>scroll event happens.</li><li>data changes</li></ul><p>The actual layout is the core process. we will break it into several parts.</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> layout() &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>._isInLayout || !<span class="keyword">this</span>._collection || <span class="keyword">this</span>._collection.length === <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">this</span>._isInLayout = <span class="literal">true</span>;</span><br><span class="line">    <span class="keyword">let</span> &#123;width, height&#125; = <span class="keyword">this</span>._infiniteList.measure();</span><br><span class="line">    <span class="keyword">this</span>._containerWidth = width;</span><br><span class="line">    <span class="keyword">this</span>._containerHeight = height;</span><br><span class="line">    <span class="keyword">this</span>.findPositionInRange();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="keyword">this</span>._viewContainerRef.length; i++) &#123;</span><br><span class="line">        <span class="keyword">let</span> child = &lt;EmbeddedViewRef&lt;InfiniteRow&gt;&gt; <span class="keyword">this</span>._viewContainerRef.get(i);</span><br><span class="line">        <span class="keyword">this</span>._viewContainerRef.detach(i);</span><br><span class="line">        <span class="keyword">this</span>._recycler.recycleView(child.context.index, child);</span><br><span class="line">        i--;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">this</span>.insertViews();</span><br><span class="line">    <span class="keyword">this</span>._recycler.pruneScrapViews();</span><br><span class="line">    <span class="keyword">this</span>._isInLayout = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">this</span>._invalidate = <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>we first check the prerequisites, we should not do layout when:</p><ul><li>A layout is performing.</li><li>Collection is empty.</li></ul><p>Then update containerHeight because we need this in <code>findPositionInRange</code>,After we do findPositionInRange( which is actually calculate the first and last index need to be in list-holder). we detach our existed viewsin <code>list-holder</code>. and re insert needed views back into it. after all job is done, we also prune the recycle bin to make sure it will not bloat too big.</p><p>How we implement findPositionInRange has been explained in the previous section. the start index and end index is exactly follow those formula to calculate.So the next important thing is adding views back to list-holder. the <code>getView</code> method first try to get views from recycle bin, if not find, create a new EmbeddedView with <code>InfiniteRow</code> object, this object is a data structure provide the data and some information like index, count. it has important property <code>$implicit</code> which is used by angular core to binding our data to view.</p><p>Once we get the view, <code>dispatchLayout</code> method will do the rest job to place views (applyStyle and insert).</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> insertViews() &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="keyword">this</span>._firstItemPosition; i &lt;= <span class="keyword">this</span>._lastItemPosition; i++) &#123;</span><br><span class="line">        <span class="keyword">let</span> view = <span class="keyword">this</span>.getView(i);</span><br><span class="line">        <span class="keyword">this</span>.dispatchLayout(i, view, <span class="literal">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> getView(position: <span class="built_in">number</span>): ViewRef &#123;</span><br><span class="line">    <span class="keyword">let</span> view = <span class="keyword">this</span>._recycler.getView(position);</span><br><span class="line">    <span class="keyword">let</span> item = <span class="keyword">this</span>._collection[position];</span><br><span class="line">    <span class="keyword">let</span> count = <span class="keyword">this</span>._collection.length;</span><br><span class="line">    <span class="keyword">if</span> (!view) &#123;</span><br><span class="line">        view = <span class="keyword">this</span>._template.createEmbeddedView(<span class="keyword">new</span> InfiniteRow(item, position, count));</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        (view <span class="keyword">as</span> EmbeddedViewRef&lt;InfiniteRow&gt;).context.$implicit = item;</span><br><span class="line">        (view <span class="keyword">as</span> EmbeddedViewRef&lt;InfiniteRow&gt;).context.index = position;</span><br><span class="line">        (view <span class="keyword">as</span> EmbeddedViewRef&lt;InfiniteRow&gt;).context.count = count;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> view;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> applyStyles(viewElement: HTMLElement, y: <span class="built_in">number</span>) &#123;</span><br><span class="line">    viewElement.style.transform = <span class="string">`translate3d(0, <span class="subst">$&#123;y&#125;</span>px, 0)`</span>;</span><br><span class="line">    viewElement.style.webkitTransform = <span class="string">`translate3d(0, <span class="subst">$&#123;y&#125;</span>px, 0)`</span>;</span><br><span class="line">    viewElement.style.width = <span class="string">`<span class="subst">$&#123;this._containerWidth&#125;</span>px`</span>;</span><br><span class="line">    viewElement.style.height = <span class="string">`<span class="subst">$&#123;this._infiniteList.rowHeight&#125;</span>px`</span>;</span><br><span class="line">    viewElement.style.position = <span class="string">'absolute'</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> dispatchLayout(position: <span class="built_in">number</span>, view: ViewRef, addBefore: <span class="built_in">boolean</span>) &#123;</span><br><span class="line">    <span class="keyword">let</span> startPosY = position * <span class="keyword">this</span>._infiniteList.rowHeight;</span><br><span class="line">    <span class="keyword">this</span>.applyStyles((view <span class="keyword">as</span> EmbeddedViewRef&lt;InfiniteRow&gt;).rootNodes[<span class="number">0</span>], startPosY);</span><br><span class="line">    <span class="keyword">if</span> (addBefore) &#123;</span><br><span class="line">        <span class="keyword">this</span>._viewContainerRef.insert(view, <span class="number">0</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">this</span>._viewContainerRef.insert(view);</span><br><span class="line">    &#125;</span><br><span class="line">    view.reattach();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>We have nearly all done. But because we separate the component into two pieces. we haven’t implement the container component <code>InfiniteList</code>.It is responsible for listen to scroll event, resize event and set the <code>height</code> to list-holder, it has a simple dom structure:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- infinite-list.html --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"infinite-list"</span> #<span class="attr">listContainer</span></span></span><br><span class="line"><span class="tag">     [<span class="attr">ngClass</span>]=<span class="string">"scrollbarStyle"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"infinite-list-holder"</span> [<span class="attr">style.height</span>]=<span class="string">"holderHeightInPx"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">ng-content</span>&gt;</span><span class="tag">&lt;/<span class="name">ng-content</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>By using <code>ng-content</code> we can put all element generated by <code>InfiniteForOf</code> directive. The important thing behind the html is a css definition.We must make infinite-list <code>overflow-y: auto</code> and give it a limit height (if you container element have a height, you can use 100%)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">.infinite-list &#123;</span><br><span class="line">  overflow-y: auto;</span><br><span class="line">  overflow-x: hidden;</span><br><span class="line">  position: relative;</span><br><span class="line">  contain: layout;          // this is performance trick, you can lookup this css property in MDN</span><br><span class="line">  -webkit-overflow-scrolling: touch; // make a touch scroll has resilient.</span><br><span class="line">  .infinite-list-holder &#123;</span><br><span class="line">    width: 100%;</span><br><span class="line">    position: relative;     // relative is also a very important property. because of all its children will be position: absolute;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>In the InfiniteList class we heavily use the Observable from RxJS, if you are not familiar rxjs, you can find some material from the internet. There some important notice when implement this component.</p><ul><li>Do all DOM event binding in <code>AfterViewInit</code> lifecycle hook. and besides, you should use setTimeout() to trigger a immediate measurement. Becausethis operation will change _containerWidth and _containerHeight in one tick which will cause an error in zone.js. so we need to schedule it to next tick.</li><li>scrollPosition subject is a BehaviorSubject, it has a convenience that a initial value will be emitted without need the scroll event. This is helpful toinitialize the layout of <code>InfiniteForOf</code>.</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br></pre></td><td class="code"><pre><span class="line">@Component(&#123;</span><br><span class="line">    selector: &apos;infinite-list&apos;,</span><br><span class="line">    templateUrl: &apos;infinite-list.html&apos;,</span><br><span class="line">    styleUrls: [&apos;infinite-list.less&apos;]</span><br><span class="line">&#125;)</span><br><span class="line">export class InfiniteList implements AfterViewInit, OnDestroy &#123;</span><br><span class="line">    private _holderHeight: number;</span><br><span class="line">    private _containerWidth: number;</span><br><span class="line">    private _containerHeight: number;</span><br><span class="line"></span><br><span class="line">    private _subscription: Subscription = new Subscription();</span><br><span class="line"></span><br><span class="line">    private _scrollPosition: BehaviorSubject&lt;number&gt; = new BehaviorSubject(0);</span><br><span class="line">    private _sizeChange: BehaviorSubject&lt;number[]&gt; = new BehaviorSubject([0, 0]);</span><br><span class="line"></span><br><span class="line">    @ViewChild(&apos;listContainer&apos;) listContainer: ElementRef;</span><br><span class="line"></span><br><span class="line">    set holderHeight(height: number) &#123;</span><br><span class="line">        if (height) &#123;</span><br><span class="line">            this._holderHeight = height;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    get holderHeight(): number &#123;</span><br><span class="line">        return this._holderHeight;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    get holderHeightInPx(): string &#123;</span><br><span class="line">        if (this.holderHeight) &#123;</span><br><span class="line">            return this.holderHeight + &apos;px&apos;;</span><br><span class="line">        &#125;</span><br><span class="line">        return &apos;100%&apos;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * current scroll position.</span><br><span class="line">     * @type &#123;number&#125;</span><br><span class="line">     */</span><br><span class="line">    get scrollPosition(): Observable&lt;number&gt; &#123;</span><br><span class="line">        return this._scrollPosition.asObservable();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    /**</span><br><span class="line">     * list container width and height.</span><br><span class="line">     */</span><br><span class="line">    get sizeChange(): Observable&lt;number[]&gt; &#123;</span><br><span class="line">        return this._sizeChange.asObservable();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Input() rowHeight: number;</span><br><span class="line"></span><br><span class="line">    ngAfterViewInit(): void &#123;</span><br><span class="line">        if (window) &#123;</span><br><span class="line">            this._subscription.add(Observable.fromEvent(window, &apos;resize&apos;)</span><br><span class="line">                .subscribe(() =&gt; &#123;</span><br><span class="line">                    let &#123;width, height&#125; = this.measure();</span><br><span class="line">                    this._sizeChange.next([width, height]);</span><br><span class="line">                &#125;));</span><br><span class="line">        &#125;</span><br><span class="line">        this._subscription.add(Observable.fromEvent(this.listContainer.nativeElement, &apos;scroll&apos;)</span><br><span class="line">            .map(() =&gt; &#123;</span><br><span class="line">                return this.listContainer.nativeElement.scrollTop;</span><br><span class="line">            &#125;)</span><br><span class="line">            .subscribe((scrollY: number) =&gt; &#123;</span><br><span class="line">                this._scrollPosition.next(scrollY);</span><br><span class="line">            &#125;));</span><br><span class="line"></span><br><span class="line">        setTimeout(() =&gt; &#123;</span><br><span class="line">            let &#123;width, height&#125; = this.measure();</span><br><span class="line">            this._sizeChange.next([width, height]);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ngOnDestroy(): void &#123;</span><br><span class="line">        this._subscription.unsubscribe();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    measure():&#123;width: number, height: number&#125; &#123;</span><br><span class="line">        if (this.listContainer &amp;&amp; this.listContainer.nativeElement) &#123;</span><br><span class="line">            let rect = this.listContainer.nativeElement.getBoundingClientRect();</span><br><span class="line">            this._containerWidth = rect.width - this.scrollbarWidth;</span><br><span class="line">            this._containerHeight = rect.height;</span><br><span class="line">            return &#123;width: this._containerWidth, height: this._containerHeight&#125;;</span><br><span class="line">        &#125;</span><br><span class="line">        return &#123;width: 0, height: 0&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>By now, we have all done. let’s use our component and directive together to make a list with infinite smooth scroll.</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"demo-container"</span> *<span class="attr">ngIf</span>=<span class="string">"collection"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">infinite-list</span> [<span class="attr">rowHeight</span>]=<span class="string">"140"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">list-item-example</span> *<span class="attr">infiniteFor</span>=<span class="string">"let row of collection; let i = index"</span> [<span class="attr">item</span>]=<span class="string">"row"</span> [<span class="attr">index</span>]=<span class="string">"i"</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;/<span class="name">list-item-example</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">infinite-list</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure><p>In this case, we give the view height 140 to InfiniteList component and pass row local variable to ListItemExample component, the actual list item willbe rendered by <code>ListItemExample</code> component.</p><p>The demo can be found at <a href="https://github.com/lordfriend/Deneb-UI/tree/master/demo-app/app/infinite-list" target="_blank" rel="noopener">GitHub</a>.</p><p>The real component has some additional feature which can tell the list item component current scroll state. This will be helpful to make some decision when user is performing a fast scroll and avoid unnecessary resource loading.</p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/make-a-list-view-in-angular/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Use Angular $http Interceptors</title>
      <link>http://nya.io/uncategorized/Use-Angular-Http-Interceptors/</link>
      <guid>http://nya.io/uncategorized/Use-Angular-Http-Interceptors/</guid>
      <pubDate>Fri, 08 May 2015 08:20:11 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;When working with HTTP API of an backend, you need to handle various response from those APIs and make your own reaction on either succes
        
      
      </description>
      
      <content:encoded><![CDATA[<p>When working with HTTP API of an backend, you need to handle various response from those APIs and make your own reaction on either success or failure response.But those APIs are not always designed RESTfully, It may not indicate an error status using the status code of HTTP. Instead, it may use an special field in theresponse body to indicate a status of an response. This implicate an burden on the developer who will not be able to use the facility provided by theframework like <code>$http</code>. Normally, <code>$http()</code> would return an promise object which can be chained with <code>then</code> method to handle both success or failure response.But when an HTTP request doesn’t using HTTP status code to indicate its result, your error handlers will never be called.In this case, you may need to check the response entity carefully to see whether its special field indicates its failure status.</p><p>I’v also encountered this issue when joining a new team. And I think I can’t change the API definition. So the only way to save time is do some tricky.And this issue can be easily solved by using $http interceptors.</p><p>Assume that I have an API which return an object like this.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="string">"status"</span>: <span class="number">0</span>, <span class="comment">// 0 indicates an successful request, otherwise the request is a failure one.</span></span><br><span class="line">    <span class="string">"data"</span>: &#123;</span><br><span class="line">    &#125;,           <span class="comment">// this is what the real data resides in.</span></span><br><span class="line">    <span class="string">"message"</span>: <span class="literal">null</span> <span class="comment">// If status isn't 0, this message will contain the error information.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Well, this API definition is very straightforward though it is not follow the RESTful style. What I need do is to intercept every response made from this APIand check the value of <code>status</code>. If it is not 0, change the statusCode of the response object to &gt;=400. Besides, that I also want to make an global interceptorto achieve this so I can get rid of writing same check code everywhere.</p><p>Let’s start to see what $http interceptors can help.</p><p>According to <a href="https://docs.angularjs.org/api/ng/service/$http#interceptors" target="_blank" rel="noopener">AngularJS Document</a> Interceptors are factory which can modify request and response.You need define some interceptors and push them into <code>$httpProvider.interceptors</code> array in your <code>config</code> function.</p><p>Let’s define an interceptor to check the status and reject the response if status code is not 0.Assume we have an API pattern is <code>/api/:someTarget</code> (:someTarget is an parameter which can be changed by need).</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> restInterceptor = angular.module(<span class="string">'RestInterceptor'</span>, []);</span><br><span class="line"></span><br><span class="line">restInterceptor.factory(<span class="string">'RestInterceptor'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">$q</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="comment">// response function will be invoked when request responds with successful status.</span></span><br><span class="line">        <span class="comment">// This is usually means the status code is 200~299 or an redirect response</span></span><br><span class="line">        response: <span class="function"><span class="keyword">function</span>(<span class="params">response</span>) </span>&#123;</span><br><span class="line">            <span class="keyword">var</span> apiPattern = <span class="regexp">/^(?:\/api\/)\S+/</span>;</span><br><span class="line">            <span class="comment">// we need filter the config object to ensure only check our API response.</span></span><br><span class="line">            <span class="keyword">if</span>(apiPattern.test(response.config.url) &amp;&amp; <span class="keyword">typeof</span> response.data.status !== <span class="string">'undefined'</span> &amp;&amp; response.data.status !== <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">//here we modify the http status code to 400 indicates this should be treated as an error.</span></span><br><span class="line">                response.status = <span class="number">400</span>;</span><br><span class="line">                <span class="comment">// reject this response will let the end point caller's error handler be called. also, you can</span></span><br><span class="line">                <span class="comment">// chain an responseError interceptor to handle this error.</span></span><br><span class="line">                <span class="keyword">return</span> $q.reject(response);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> response;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>This code snippet create an interceptor to check the normal response whose HTTP status code is usually 200, but modify an response if its data.status doesn’tequal 0. and modify the HTTP status code to 400. so we can treat the API as an RESTful API and All the facility provided by angular or three party dependencycan be used directly without redundant check code.</p><p>The rest job is to make the interceptor work, we need to push this factory into <code>$httpProvider.interceptors</code>. <strong>Note that the invoke order of <code>response</code> and <code>responseError</code>interceptors are in reverse registration order. This is not clearly documented in official document.</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> app = angular.module(<span class="string">'myApp'</span>, [<span class="string">'RestInterceptor'</span>]);</span><br><span class="line">app.config(<span class="function"><span class="keyword">function</span>(<span class="params">$httpProvider, RestInterceptor</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// if you have other interceptors to handle error response, make sure to push "RestInterceptor" at last.</span></span><br><span class="line">    $httpProvider.interceptors.push(<span class="string">'OtherErrorHandlerInterceptor'</span>);</span><br><span class="line">    $httpProvider.interceptors.push(<span class="string">'RestInterceptor'</span>);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><p>Now you can write your code without need to check whether you got an error in your success handler.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> app = angular.module(<span class="string">'myApp'</span>);</span><br><span class="line">app.controller(<span class="string">'SomeController'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">$scope, $http</span>)</span>&#123;</span><br><span class="line">    $http.get(<span class="string">'/api/first-item'</span>)</span><br><span class="line">        .success(<span class="function"><span class="keyword">function</span>(<span class="params">data, status, headers, config</span>) </span>&#123;</span><br><span class="line">            <span class="comment">// write your code for successful request</span></span><br><span class="line">            <span class="comment">// result object will be always valid.</span></span><br><span class="line">            <span class="built_in">console</span>.log(data);</span><br><span class="line">        &#125;)</span><br><span class="line">        .error(<span class="function"><span class="keyword">function</span>(<span class="params">data, status, headers, config</span>) </span>&#123;</span><br><span class="line">            <span class="built_in">console</span>.log(status); <span class="comment">// will print 400 when data.data.status != 0</span></span><br><span class="line">        &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>Next time, I will use interceptor to write a notification module which will automatically make a toast notification to user when an API requesthas failed. And will combine with the RestInterceptor I just have wrote.</p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/Use-Angular-Http-Interceptors/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Beware Of Using value Attribute On list element</title>
      <link>http://nya.io/AngularJS/Beware-Of-Using-value-Attribute-On-list-element/</link>
      <guid>http://nya.io/AngularJS/Beware-Of-Using-value-Attribute-On-list-element/</guid>
      <pubDate>Mon, 20 Apr 2015 04:05:49 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;In AngularJs development, we use custom attribute in our directive is nontrivial things. you can even use some attributes which may alrea
        
      
      </description>
      
      <content:encoded><![CDATA[<p>In AngularJs development, we use custom attribute in our directive is nontrivial things. you can even use some attributes which may already be definedin W3C standards of that element. well, this doesn’t matter at most circumstance. But it is not recommended to override the meaning of those attribute originally defined by W3C. I have paid for this.</p><p>I have a directive <a href="http://nya.io/nya-bootstrap-select/">nya-bootstrap-select</a> which using a <code>value</code> attribute on list element. I give this attributea new meaning that its value will be the option value for predefined options. It works well in AngularJS apps until I tried to add some e2e test onit using <a href="http://angular.github.io/protractor/" target="_blank" rel="noopener">Protractor</a>.</p><p>In Protractor elements are selected by the webdriver api and I hasn’t look into the implementation of selenium, but I can sure it may take the W3C standardand make its own implementation to retrieve the value of <code>value</code> attribute.</p><p>As the W3C standards says:</p><blockquote><p>This integer attributes indicates the current ordinal value of the item in the list as defined by the <code>&lt;ol&gt;</code> element. The only allowed value for this attribute is a number, even if the list is displayed with Roman numerals or letters. List items that follow this one continue numbering from the value set. The value attribute has no meaning for unordered lists (<code>&lt;ul&gt;</code>) or for menus (<code>&lt;menu&gt;</code>).</p></blockquote><p>The list element has a <code>value</code> attribute which <strong>only accepts integer value</strong>. Although I can use a String value in my AngularJS app but this is not warrantedby standard and can varies between implementations. Just as the selenium implementation, the WebElement.getAttribute(‘value’) on my directive alwaysreturn <code>0</code>. This is not my expected.</p><p>In conclusion, It is recommended for those who want to use some predefined attribute on their own directive to look up the standard whether its behavior has already been defined toavoid conflicts and variants between implementations.</p>]]></content:encoded>
      
      <comments>http://nya.io/AngularJS/Beware-Of-Using-value-Attribute-On-list-element/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Busting The Cache</title>
      <link>http://nya.io/uncategorized/Busting-The-Cache/</link>
      <guid>http://nya.io/uncategorized/Busting-The-Cache/</guid>
      <pubDate>Sun, 05 Apr 2015 02:58:36 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;Last week, I completed my new hexo theme, but newly added page were broken because the stylesheet were not updated with the page. This wa
        
      
      </description>
      
      <content:encoded><![CDATA[<p>Last week, I completed my new hexo theme, but newly added page were broken because the stylesheet were not updated with the page. This was caused by the cache either on browser side or on the CDN of github pages side.So I decided to solve this issue by using a method to busting the cache whenever I have updated the assets files.</p><p>If anyone have ever used yeoman to generate an angular project. You may find a grunt task in the generated <em>Gruntfile</em> called <a href="https://github.com/yeoman/grunt-usemin" target="_blank" rel="noopener">usemin</a>.This task combined with several other task can concatenate your stylesheet andjavascript files then minimize those files, at last a file revision task is executed internally. The key of busting cache is the file revision task which will calculate your files’ hash value and use the value torename your file, this will totally solve the cache issue. whenever your file changes, the hash value changes and you get a different filename of assets.</p><p>Back to the hexo theme project, I found it is hard to use the grunt task directly in my project because those task will modify the html or template file to update the <code>script</code> and <code>link</code> tag in order torevision the assets file name. But hexo template file should only be modifed by programmer and generate html file by hexo. So I think it’s time to write a similar procedure by myself using hexo api.</p><p>Hexo provide two api <a href="http://hexo.io/api/helper.html" target="_blank" rel="noopener">helper</a> and <a href="http://hexo.io/api/generator.html" target="_blank" rel="noopener">generator</a> to generate tags in template and generate files with specified path. I will use this two api to keep the reference in template update and generate the new file with hash prefix file name.Because I can’t ensure the execution order of these two api, the concat and minified operation are abandoned.First let’s write a file hash method. This method accepts two arguments: file absolute path and hash digit length. because we don’t need the entire hash string to name asset file. the return value will be the new file name prefixed with a hash string</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>),</span><br><span class="line">  path = <span class="built_in">require</span>(<span class="string">'path'</span>),</span><br><span class="line">  crypto = <span class="built_in">require</span>(<span class="string">'crypto'</span>);</span><br><span class="line"><span class="comment">// modules above will be shared in the following two code blocks.</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">filehash</span>(<span class="params">filepath, digitlength</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> hash = crypto.createHash(<span class="string">'md5'</span>).update(fs.readFileSync(filepath)).digest(<span class="string">'hex'</span>);</span><br><span class="line">  <span class="keyword">var</span> prefix = hash.slice(<span class="number">0</span>, digitlength);</span><br><span class="line">  <span class="keyword">return</span> prefix + <span class="string">'.'</span> + path.basename(filepath);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>This method will be called by <code>helper</code> and <code>generator</code> to get the assets files’ hash value file name.</p><p>Now let’s begin from the <code>generator</code>. this api will let me to provide an array which tell hexo to write some files with specified path. I can copy the assets file with hashed file name.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> themeSourcePath = path.resolve(__dirname, <span class="string">'../source'</span>); <span class="comment">// we need the theme source path to find the assets folder</span></span><br><span class="line"><span class="keyword">var</span> hashLength = <span class="number">8</span>;</span><br><span class="line"><span class="comment">// this is define of the generator.</span></span><br><span class="line">hexo.extend.generator.register(<span class="string">'filerev'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">locals</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// find all directories in theme source directory.</span></span><br><span class="line">  <span class="keyword">var</span> directories = fs.readdirSync(themeSourcePath);</span><br><span class="line">  <span class="keyword">var</span> outputData = [];</span><br><span class="line">  directories.filter(<span class="function"><span class="keyword">function</span>(<span class="params">dir</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> dir === <span class="string">'css'</span> || dir === <span class="string">'js'</span>;</span><br><span class="line">  &#125;).forEach(<span class="function"><span class="keyword">function</span>(<span class="params">dir</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> files = fs.readdirSync(path.join(themeSourcePath, dir));</span><br><span class="line">    outputData = outputData.concat(files.map(<span class="function"><span class="keyword">function</span>(<span class="params">file</span>) </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> &#123;</span><br><span class="line">        path: dir + <span class="string">'/'</span> + filehash(path.join(themeSourcePath, dir, file), hashLength),  <span class="comment">// call filehash method we write before.</span></span><br><span class="line">        data: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">          <span class="keyword">return</span> fs.createReadStream(path.join(themeSourcePath, dir, file));</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;));</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> outputData;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>This generator will generate a set of new file with the hash prefix and its original folder structure in <code>public</code> folder when you run <code>hexo generate</code>.</p><p>With the hash prefixed file generated properly, we have to update the reference in <code>script</code> and <code>link</code> tag. here we using <code>helper</code> to define an <em>ejs</em> helper called <code>usemin</code> to generate our <code>script</code> and <code>link</code> tag.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">hexo.extend.helper.register(<span class="string">'usemin'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">input</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> filepath = path.join(themeSourcePath, input);</span><br><span class="line">  <span class="keyword">var</span> dir = input.split(<span class="string">'/'</span>);</span><br><span class="line">  <span class="keyword">var</span> ext = path.extname(input);</span><br><span class="line">  <span class="keyword">var</span> newFilename = filehash(filepath, hashLength);</span><br><span class="line">  <span class="keyword">var</span> newPath = path.resolve(<span class="string">'/'</span>, path.join(dir.slice(<span class="number">0</span>, dir.length - <span class="number">1</span>).join(<span class="string">'/'</span>), newFilename));</span><br><span class="line">  <span class="keyword">return</span> ext === <span class="string">'.js'</span> ? <span class="string">'&lt;script type="text/javascript" src="'</span> + newPath + <span class="string">'"&gt;&lt;/script&gt;'</span> : <span class="string">'&lt;link rel="stylesheet" href="'</span> + newPath + <span class="string">'"&gt;'</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>Put this scripts file in scripts folder under my theme directory, the script will be loaded by hexo automatically. Now there are only one step to get the goal, modify template using the <code>usemin</code> helper</p><p>In head.ejs</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- other tags... --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">%-</span> <span class="attr">usemin</span>('<span class="attr">css</span>/<span class="attr">styles.css</span>') %&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">%-</span> <span class="attr">css</span>('<span class="attr">fancybox</span>/<span class="attr">source</span>/<span class="attr">jquery.fancybox.css</span>') %&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br></pre></td></tr></table></figure><p>In scripts.ejs</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- other tags... --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">%-</span> <span class="attr">usemin</span>('<span class="attr">js</span>/<span class="attr">jquery.js</span>') %&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">%-</span> <span class="attr">js</span>('<span class="attr">fancybox</span>/<span class="attr">source</span>/<span class="attr">jquery.fancybox.js</span>') %&gt;</span> <span class="comment">&lt;!-- we can't process this file, so use the js helper --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">%-</span> <span class="attr">usemin</span>('<span class="attr">js</span>/<span class="attr">caption.js</span>') %&gt;</span></span><br></pre></td></tr></table></figure><p>When I run <code>hexo generate</code>, the index.html will be generated by hexo which contains head.ejs and scripts.ejs part</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- omit other tags... --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"/css/2bb849c6.styles.css"</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">"stylesheet"</span> <span class="attr">href</span>=<span class="string">"/fancybox/source/jquery.fancybox.css"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- omit other tags... --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">src</span>=<span class="string">"/js/cf26f8f0.jquery.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"/fancybox/source/jquery.fancybox.js"</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">"text/javascript"</span> <span class="attr">src</span>=<span class="string">"/js/33415e49.caption.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/Busting-The-Cache/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Add About Page</title>
      <link>http://nya.io/uncategorized/Add-About-Page/</link>
      <guid>http://nya.io/uncategorized/Add-About-Page/</guid>
      <pubDate>Tue, 31 Mar 2015 13:41:48 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;Finally, this new blog theme has completed. A new about page is available, you can see the description and authors from about page.&lt;/p&gt;

        
      
      </description>
      
      <content:encoded><![CDATA[<p>Finally, this new blog theme has completed. A new about page is available, you can see the description and authors from about page.</p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/Add-About-Page/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Fix Theme bug in Safari</title>
      <link>http://nya.io/uncategorized/Fix-Theme-bug-in-Safari/</link>
      <guid>http://nya.io/uncategorized/Fix-Theme-bug-in-Safari/</guid>
      <pubDate>Tue, 31 Mar 2015 02:44:23 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;I found my new theme behaves strange in iOS. After an inspect, I found this was caused by missing the vendor prefix of some css property.
        
      
      </description>
      
      <content:encoded><![CDATA[<p>I found my new theme behaves strange in iOS. After an inspect, I found this was caused by missing the vendor prefix of some css property.</p><p>From caniuse.com I know that the 3d transform property although is supported by Safari and iOS, but it is still needed a vendor prefix to make it work. After add the <code>-webkit-</code> prefix. the bugon iOS disappeared. For convenience, I using a grunt task <code>grunt-autoprefixer</code> to do this automatically.</p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/Fix-Theme-bug-in-Safari/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Add Archives Layout</title>
      <link>http://nya.io/uncategorized/Add-archives-layout/</link>
      <guid>http://nya.io/uncategorized/Add-archives-layout/</guid>
      <pubDate>Mon, 23 Mar 2015 11:51:25 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;Archives page is not very useful, but as a hexo theme, I think this is an important feature. So this page was redesigned and coded as you
        
      
      </description>
      
      <content:encoded><![CDATA[<p>Archives page is not very useful, but as a hexo theme, I think this is an important feature. So this page was redesigned and coded as you could see. Enjoy.</p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/Add-archives-layout/#disqus_thread</comments>
    </item>
    
    <item>
      <title>New Hexo Theme</title>
      <link>http://nya.io/uncategorized/New-Hexo-Theme/</link>
      <guid>http://nya.io/uncategorized/New-Hexo-Theme/</guid>
      <pubDate>Sun, 08 Feb 2015 08:33:40 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;For long time, I have to use the default theme &lt;code&gt;landscape&lt;/code&gt;, because I always want to make a very magnificence theme which may 
        
      
      </description>
      
      <content:encoded><![CDATA[<p>For long time, I have to use the default theme <code>landscape</code>, because I always want to make a very magnificence theme which may require far more out of my toolbox and ability.Until now, I decide to make this blog a pure programming and technology oriented. So I decided to develop an brief and clear theme. That is the theme <code>timeline</code>.</p><p>This theme is made from ground. Almost everything, I have to learn from hexo official site, where its document is not very detailed.To save time, I made a decision that I would use the technology which I already handled of. So this theme is designed with Adobe Edge Reflow CC, made with less, ejs and a little jquery. Also, I use grunt to build a early preview version to check the design and some css styles. In fact, it’s not a good practice to develop a hexo theme.</p><p>It is worth to talk about the interesting things when developing this theme, Some of these may have someone to not make a mistake.</p><p>This theme is design with Adobe Edge Reflow CC, this means I’ll to make a responsive design at very first. so I’ll have to develop with both desktop and mobile screen. Luckly, Chrome has a emulator can provide different device screen size to preview the result. But when I previewed with that, All of my fonts became very big (almost scaled 3 times bigger) and the content were out of screen on mobile devices. Debug this wasted a lot of time until I found I missed a meta tag to declare the <code>viewport</code> and <code>initial-scale</code> factor.</p><p>Then I add this meta tag then everything looks good.<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"width=device-width, initial-scale=1.0"</span>/&gt;</span></span><br></pre></td></tr></table></figure></p><p>To make the navigation bar more accessible on mobile devices. I want to make a drawer navigator which may be very similar to the drawer of Android app.At first, I made this very jquery, write some code and debug, this was time waste. Then I found a method called <a href="http://css-tricks.com/the-checkbox-hack/" target="_blank" rel="noopener">checkbox hack</a> which is far more easy to create a navigator drawer and with no javascript need! The mystery is you can make a invisible checkbox (whether make it opacity=0 or move it out of screen) and then attach one or more labels with it, these labels are clickable and styleable. Another key technology is <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_selectors" target="_blank" rel="noopener">general sibling selectors</a> which make it possible to use the :checked pseudo class to define a different styles for the drawer and backdrop element based on the checkbox state.</p><p>it is almost like this:<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"checkbox"</span> <span class="attr">id</span>=<span class="string">"drawer-toggle"</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"drawer-toggle"</span> <span class="attr">class</span>=<span class="string">"drawer-label"</span>&gt;</span>Drawer Button<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"drawer"</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"drawer-toggle"</span> <span class="attr">class</span>=<span class="string">"backdrop"</span>&gt;</span><span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br></pre></td></tr></table></figure></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">#drawer-toggle &#123;</span><br><span class="line">    opacity: 0;</span><br><span class="line">    position: absolute;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">.drawer-label &#123;</span><br><span class="line">    // you can make any styles what you want your drawer toggle button to be.</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">.drawer &#123;</span><br><span class="line">    transition: transform 0.2s ease-out;</span><br><span class="line">    transform: translate3d(-100px, 0, 0); // we move the drawer out of screen when initialized.</span><br><span class="line">    position: fixed;</span><br><span class="line">    top: 0;</span><br><span class="line">    left: 0;</span><br><span class="line">    width: 100px;</span><br><span class="line">    height: 100%;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">.backdrop &#123;</span><br><span class="line">    display: none;</span><br><span class="line">    position: fixed;</span><br><span class="line">    top: 0;</span><br><span class="line">    left: 0;</span><br><span class="line">    width: 100%;</span><br><span class="line">    height: 100%;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">// then came the interesting part.</span><br><span class="line"></span><br><span class="line">#drawer-toggle:checked ~ .drawer &#123;</span><br><span class="line">    transform: translate3d(0, 0, 0);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">#drawer-toggle:checked ~ .backdrop &#123;</span><br><span class="line">    display:block;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>This technology has very good browser compatibility. even with IE9 but If you want to be compatible with the older browser, You may consider a javascript version. But I don’t care.</p><p>After I almost complete the styles and layout development and began to convert html to ejs template. I finally found a project called hexo-theme-unit-test. Which is actually a sample blog with every type of post. What I need is copy may theme into its themes folder. and modify the _config.yml.  But, when I run the command <code>hexo server</code> it didn’t work! After tried serveral times, I found this unit-test project lack some dependency declarations in its package.json.  This would result in the ejs template not being rendered. after installed the dependencies. it works well. If you want to develop a theme, you should work with this project,   no need to config a grunt task. and write a pure html file.</p><p>This hexo theme is still in development, some pages and functions are not available, the styles need to be tweaked, I’ll continue work with this.</p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/New-Hexo-Theme/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Make Action Bar Overlay work with ListView or GridView</title>
      <link>http://nya.io/uncategorized/Make-Action-Bar-Overlay-work-with-ListView-or-GridView/</link>
      <guid>http://nya.io/uncategorized/Make-Action-Bar-Overlay-work-with-ListView-or-GridView/</guid>
      <pubDate>Wed, 31 Dec 2014 06:45:20 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;ActionBar pattern is widely used since Honeycomb released. As the action bar actually take space of your limited screen, you may need mak
        
      
      </description>
      
      <content:encoded><![CDATA[<p>ActionBar pattern is widely used since Honeycomb released. As the action bar actually take space of your limited screen, you may need make the action bar semi-transparent to save some space.ActionBar API provide an method to overlay your action bar on your screen content. as the <a href="http://developer.android.com/training/basics/actionbar/overlaying.html" target="_blank" rel="noopener">training material</a> mentioned:</p><p>Just add a style <code>windowActionBarOverlay</code> and set it true. The snippet is copied from android developer.<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">resources</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- the theme applied to the application or activity --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">style</span> <span class="attr">name</span>=<span class="string">"CustomActionBarTheme"</span></span></span><br><span class="line"><span class="tag">           <span class="attr">parent</span>=<span class="string">"@android:style/Theme.AppCompat"</span>&gt;</span><span class="undefined"></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">item</span> <span class="attr">name</span>=<span class="string">"android:windowActionBarOverlay"</span>&gt;</span>true<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span></span><br><span class="line"><span class="undefined"></span></span><br><span class="line"><span class="xml">        <span class="comment">&lt;!-- Support library compatibility --&gt;</span></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">item</span> <span class="attr">name</span>=<span class="string">"windowActionBarOverlay"</span>&gt;</span>true<span class="tag">&lt;/<span class="name">item</span>&gt;</span></span></span><br><span class="line"><span class="undefined">    </span><span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">resources</span>&gt;</span></span><br></pre></td></tr></table></figure></p><p>But the training material from android developer site isn’t use an ListView or GridView with ActionBar overlay. When you just follow its instruction to set paddingTop on your ListView or GridView.You may not get expected effect. the scrolling content is just cut from the air. like the left side show on the figure.</p><p><img src="/img/cliptopadding.gif" alt="clipToPadding and scrollBarStyle Protip"></p><p>To achieve the right effect like the right side. Android Developers Official G+ page give a tip on G+ <a href="https://plus.google.com/+AndroidDevelopers/posts/LpAA7q4jw9M" target="_blank" rel="noopener">https://plus.google.com/+AndroidDevelopers/posts/LpAA7q4jw9M</a></p><p>Add an attribute <code>android:clipToPadding</code> to your ListView or GridView and set it false. then you can set your paddingTop on your ListView or GridView as your ActionBar height.</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">ListView</span></span></span><br><span class="line"><span class="tag">    …</span></span><br><span class="line"><span class="tag">    <span class="attr">android:clipToPadding</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag">    <span class="attr">android:scrollbarStyle</span>=<span class="string">"outsideOverlay"</span> /&gt;</span></span><br></pre></td></tr></table></figure>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/Make-Action-Bar-Overlay-work-with-ListView-or-GridView/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Remove Verbose Log In Production</title>
      <link>http://nya.io/uncategorized/Remove-Verbose-Log-In-Production/</link>
      <guid>http://nya.io/uncategorized/Remove-Verbose-Log-In-Production/</guid>
      <pubDate>Sat, 25 Oct 2014 07:45:12 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;&lt;code&gt;android.util.Log&lt;/code&gt; is a very useful class to help developer debug their application. But when in production release. Any verbo
        
      
      </description>
      
      <content:encoded><![CDATA[<p><code>android.util.Log</code> is a very useful class to help developer debug their application. But when in production release. Any verbose log (<code>Log.v</code>) should be removed properly to prevent annoying user or leak some information.</p><p>You can simply commented those Log.v out or Using a ProGuard to remove any verbose log statements directly from the bytecode of your compiled JAR executable. This method is described in Christopher’s answer in the <a href="http://stackoverflow.com/a/2019563/1696590" target="_blank" rel="noopener">StackOverflow post</a></p><p>The easiest way is to run your compiled JAR through ProGuard before deployment, with a config like:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">-assumenosideeffects class android.util.Log &#123;</span><br><span class="line">    public static int v(...);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>That will – aside from all the other ProGuard optimisations – remove any verbose log statements directly from the bytecode.</p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/Remove-Verbose-Log-In-Production/#disqus_thread</comments>
    </item>
    
    <item>
      <title>自己改的Brackets用的Monokai主题，参考Webstorm配色</title>
      <link>http://nya.io/uncategorized/monokai-webstorm-for-brackets/</link>
      <guid>http://nya.io/uncategorized/monokai-webstorm-for-brackets/</guid>
      <pubDate>Wed, 28 May 2014 17:12:00 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;因为之前被人推荐过Brackets，自己也试用了一下，总体感觉还是不错的。虽然跟现在正在试用Webstorm比起来要弱一些，不过对于不太严重依赖IDE的人来说，作为前端开发编辑器来说还是很出色的。&lt;/p&gt;
&lt;p&gt;由于Brackets基于webkit写成的，所以可以按照开发w
        
      
      </description>
      
      <content:encoded><![CDATA[<p>因为之前被人推荐过Brackets，自己也试用了一下，总体感觉还是不错的。虽然跟现在正在试用Webstorm比起来要弱一些，不过对于不太严重依赖IDE的人来说，作为前端开发编辑器来说还是很出色的。</p><p>由于Brackets基于webkit写成的，所以可以按照开发web的方法来编写插件。而Themes插件则利用CodeMirror来编写主题。不过里面自带的Monokai过于鲜亮，加上Brackets在Mac下有字体渲染bug（后面会解释如何修复这个问题），所以我决定按照Webstorm的monokai主题配色方案来修改一个Brackets的monokai主题。</p><p>不过修改起来才发现，Brackets的功能还是略弱，比如不能按照语言来定义颜色，不能区分很具体的变量用法（比如未使用的局部变量），在修改配色的时候首先照顾javascript，然后才是css，所以css最后效果和正常的monokai差距较大，是个遗憾之处。</p><p>总的来说修改之后的monokai主题有以下优点：</p><ul><li>和默认的monokai-dark-soda相比，对比度降低了一些，不那么刺眼了，虽然还是优点略亮。</li><li>此外还解决了mac下字体显示的问题</li></ul><p>###效果预览：####</p><p><strong>JS代码效果：</strong></p><p><img src="/img/monokai-webstorm-js.png" alt="JS代码效果"></p><p><strong>HTML代码效果：</strong></p><p><img src="/img/monokai-webstorm-html.png" alt="HTML代码效果"></p><p><strong>CSS代码效果：</strong></p><p><img src="/img/monokai-webstorm-css.png" alt="CSS代码效果"></p><p>###下载地址：###<a href="https://gist.github.com/lordfriend/858e369f8e9f7cd9861c" target="_blank" rel="noopener">https://gist.github.com/lordfriend/858e369f8e9f7cd9861c</a></p><p><strong>欢迎试用与反馈bug</strong></p><p>###使用方法：###</p><ul><li>安装Brackets的Themes扩展：<a href="https://github.com/MiguelCastillo/Brackets-Themes" target="_blank" rel="noopener">https://github.com/MiguelCastillo/Brackets-Themes</a></li><li>然后打开扩展的文件夹（Help菜单-&gt;show Extensions Folder)，进入user/themes/theme，然后将下载的monokai-webstorm.css放到里面，重启Brackets，在Themes菜单里选择monokai-webstorm主题即可。</li></ul><p>###如何修复mac下字体显示问题###</p><p>mac下字体显示问题是因为brackets自己加了一个 <code>-webkit-font-smoothing: antialiased</code> 属性导致的。这个属性本来是为了让windows下字体显示更平滑，不过 <code>antialiased</code> 这个值在mac下却导致了字体显示变成点阵一样的效果。解决办法就是使用 <code>subpixel-antialiased</code> 值来代替默认值。</p><p>如果你要定义自己的主题。那么在 <code>.CodeMirror</code>里加上这样一句就可以了：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">.CodeMirror &#123;</span><br><span class="line">    -webkit-font-smoothing: subpixel-antialiased;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/monokai-webstorm-for-brackets/#disqus_thread</comments>
    </item>
    
    <item>
      <title>nya-bootstrap-select也许应该完全重写了</title>
      <link>http://nya.io/AngularJS/nya-bootstrap-select-may-need-a-rewrite/</link>
      <guid>http://nya.io/AngularJS/nya-bootstrap-select-may-need-a-rewrite/</guid>
      <pubDate>Thu, 08 May 2014 15:18:14 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;就在不久前，我还对这个插件很乐观，不过在解决了最近的两个bug之后，我觉得同angularjs自带的directive作斗争是一件不愉快的事情。&lt;/p&gt;
&lt;p&gt;由于nya-bootstrap-select依赖bootstra-select这个jQuery插件，而该插件需要使
        
      
      </description>
      
      <content:encoded><![CDATA[<p>就在不久前，我还对这个插件很乐观，不过在解决了最近的两个bug之后，我觉得同angularjs自带的directive作斗争是一件不愉快的事情。</p><p>由于nya-bootstrap-select依赖bootstra-select这个jQuery插件，而该插件需要使用select标签来定义下拉选框。但Angular内部已经把select标签作为了一个directive，并且定义了一些列特殊行为。这些行为导致了目前各种冲突，其中一个问题就是，当我们使用ng-options来生成选项的时候。select directive会先生成一个?值的option作为默认选项，一开始为了保持美观和一致性，我删除了这个选项，但我发现angular内部似乎会错误的引用位置，导致当select的值由无变成确定值时，删除后的第一个实际option被删除。这带来了严重的问题，最后不得已，我只能保留那个？的option，但这样很难看。看起来避免使用select标签才是根本解决之道。</p>]]></content:encoded>
      
      <comments>http://nya.io/AngularJS/nya-bootstrap-select-may-need-a-rewrite/#disqus_thread</comments>
    </item>
    
    <item>
      <title>在Node.js中使用promise摆脱回调金字塔</title>
      <link>http://nya.io/Node-js/promise-in-nodejs-get-rid-of-callback-hell/</link>
      <guid>http://nya.io/Node-js/promise-in-nodejs-get-rid-of-callback-hell/</guid>
      <pubDate>Tue, 06 May 2014 14:42:00 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;在开始谈论正题之前，我们先来看看下面一段代码：
&lt;figure class=&quot;highlight javascript&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;span class=&quot;line&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span clas
        
      
      </description>
      
      <content:encoded><![CDATA[<p>在开始谈论正题之前，我们先来看看下面一段代码：<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">step1(<span class="function"><span class="keyword">function</span> (<span class="params">value1</span>) </span>&#123;</span><br><span class="line">    step2(value1, <span class="function"><span class="keyword">function</span>(<span class="params">value2</span>) </span>&#123;</span><br><span class="line">        step3(value2, <span class="function"><span class="keyword">function</span>(<span class="params">value3</span>) </span>&#123;</span><br><span class="line">            step4(value3, <span class="function"><span class="keyword">function</span>(<span class="params">value4</span>) </span>&#123;</span><br><span class="line">                <span class="comment">// Do something with value4</span></span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>是不是感觉很恐怖，随着嵌套的回调函数增加，结尾会有大量的花括号和圆括号 }); 出现。</p><p>###Promise###在javascript中实现异步最简单的方式是Callback。遗憾的是，这种编程方式牺牲了控制流，同时你也不能throw new Error()并在外部捕获异常。Promise的出现解决了这两个需求，又保持了javascript异步的优势，不同于Fiber这种多线程的实现方式，Promise只是一种编程方式的变化。而无须在底层改变。</p><p>CommonJS的规范提到了多种Promise，我们只介绍其中一种的实现q (<a href="https://github.com/kriskowal/q" target="_blank" rel="noopener">https://github.com/kriskowal/q</a>)</p><p>我们在这里不讲解抽象的Promise规范，这多半是实现者应该关心的，我们直接从示例入手，如果你有兴趣，可以参见<a href="http://promises-aplus.github.io/promises-spec/" target="_blank" rel="noopener">Promise/A+</a>。</p><p>q的核心是一个promise对象的then方法，他接受两个回调方法，一个promise被定义之后有3种状态，pending（过渡状态），fullfilled（完成状态），rejected(错误状态)。一个promise只能是这三种状态种的一种，而无法是他们的混合状态。</p><ul><li>pending状态可以理解为promise还没有获得确定值，就相当于一个任务还没有完成。</li><li>fullfilled状态可以理解为完成并返回结果。这时then(onFullfilled, onRejected)的onFullfilled方法会被调用。</li><li>rejected状态可以理解为错误，并结束。返回错误。这时then(onFullfilled, onRejected)的onRejected方法会被调用。</li></ul><p>了解了核心思想后，我们来看一个例子，在这个例子中我们先读取一个json文本文件，然后将其解析成javascript对象，最后这个对象进行修改再保存回去。按照传统的callback写法，有如下代码：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">fs.readFile(<span class="string">'example.json'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">err, data</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(err) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(err):</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">var</span> obj = <span class="built_in">JSON</span>.parse(data);</span><br><span class="line">      obj.prop = <span class="string">'something new'</span>;</span><br><span class="line">      fs.writeFile(<span class="string">'example.json'</span>, <span class="built_in">JSON</span>.stringify(obj), <span class="function"><span class="keyword">function</span>(<span class="params">error</span>)</span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(err) &#123;</span><br><span class="line">          <span class="built_in">console</span>.log(error);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">          <span class="built_in">console</span>.log(<span class="string">'success'</span>);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125; <span class="keyword">catch</span>(e) &#123;</span><br><span class="line">      <span class="built_in">console</span>.log(e);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>在这个例子中，控制流被切割成多个部分（每次异步都要处理一次错误），并且 <strong>JSON.parse</strong> 的错误必须在内部捕获，但却不能跑到外部。因为在异步回调中无法抛出异常。现在当我们使用promise的时候，假设我们有个能够返回一个 <em>promise</em> 对象的 <strong>readFile</strong> 和 <strong>writeFile</strong> 方法。那么上面的代码就可以变成如下形式：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> promise = readFile();</span><br><span class="line">promise</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>&#123;</span><br><span class="line">  <span class="comment">// we don't need to catch error. in other words. we can throw error in this callback.</span></span><br><span class="line">  <span class="keyword">var</span> obj = <span class="built_in">JSON</span>.parse(data);</span><br><span class="line">  obj.prop = <span class="string">'something new'</span>;</span><br><span class="line">  <span class="comment">// return a promise. so we can chain the then() method.</span></span><br><span class="line">  <span class="keyword">return</span> writeFile(<span class="built_in">JSON</span>.stringify(obj));</span><br><span class="line">  &#125;)</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'success'</span>);</span><br><span class="line">  &#125;, <span class="function"><span class="keyword">function</span>(<span class="params">err</span>)</span>&#123;</span><br><span class="line">    <span class="comment">// all error will fall down here.</span></span><br><span class="line">    <span class="built_in">console</span>.log(err);</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><p>上面的例子中，我们首先从 <strong>readFile()</strong> 方法里获得了一个返回的 <em>promise</em> 对象，然后使用这个对象的 <strong>then()</strong> 方法。在这里，我们只传入了一个 <strong>onFullfilled</strong> 回调方法，根据Promise/A+的文档。<strong>then()</strong> 一定会返回一个 <em>promise</em> 对象，所以我们又连接了一个 <strong>then()</strong> ，由于这个 <strong>then()</strong> 是最后一个，所以我们需要在这里提供一个 <strong>onRejected()</strong> 回调方法来处理所有的错误。在第一个 <strong>onFullfilled()</strong> 回调方法中，我们返回了一个 <em>promise</em> ，这个 <em>promise</em> 的处理结果将会在下一个 <strong>then()</strong> 的 <strong>onFullfilled()</strong> 方法中取得。</p><p>这段代码执行的时候，当任意位置抛出异常的时候，最后一个then的 <strong>onRejected</strong> 回调会被执行。否则一切按照从上至下的顺序执行，整个控制流都十分简洁明了。</p><p>因为 <strong>then()</strong> 方法必须返回一个promise，实际上我们也可以结合同步方法返回一个已经fullfilled的promise比如下面这个例子<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> promise = readFile();</span><br><span class="line">promise</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">JSON</span>.parse(data);</span><br><span class="line">  &#125;)</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">obj</span>)</span>&#123;</span><br><span class="line">    obj.prop = <span class="string">'something new'</span>;</span><br><span class="line">    <span class="built_in">console</span>.log(obj);</span><br><span class="line">  &#125;,<span class="function"><span class="keyword">function</span>(<span class="params">err</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(err);</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure></p><p>上面例子中第二个then会在第一个then返回之后被执行，因为第一个then返回的时候，由于JSON.parse是同步方法，所以返回了一个值，这个值会被包装成一个fullfilled的promise.</p><p>###制作promise的API###</p><p>上面例子中我们知道了如何使用promise提供的核心方法 <strong>then()</strong> 。但是对于平时使用的fs等异步的库我们要怎么才能利用promise呢。在q的文档中介绍了q-io库，里面将常用的io方法都用promise的模式包装了一遍，在实际使用中，你可以使用那个库的方法。不过我们在这里简单的对fs进行包装，让其支持promise，这样以后遇到任何异步方法，你都可以将其转化。</p><p>首先定义改一个readFile方法，返回promise，这里利用了 <strong>Q</strong> 的 <strong>defer()</strong> 方法，创建一个 <em>deferred</em> 对象。这个对象有连个关键的方法 <strong>resolve</strong> 和 <strong>reject()</strong> 。当resolve(value)执行之后，promise变成fullfilled状态，fullfilled的值就是value当 <strong>reject(reason)</strong> 执行之后，promise变成了rejected状态，reason会被传递到onRejected()方法。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> Q = <span class="built_in">require</span>(<span class="string">'q'</span>);</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">readFile</span>(<span class="params">callback</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> deferred = Q.defer();</span><br><span class="line">  fs.readFile(<span class="string">'example.json'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">err, data</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(err)&#123;</span><br><span class="line">      deferred.reject(err);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      deferred.resolve(data);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> deferred.promise.nodeify(callback);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>注意到这里面我们依然提供了一个callback，用于提供一些需要callback的场合的兼容性，我们利用 <em>promise</em> 对象的nodeify方法来调用这个callback，这个callback可以为undefined。</p><p>另外一点需要注意的是，一个promise状态改变之后，不能再次改变，所以，你只能调用一次reject或resolve。</p><p>有了这个API，我们便可以像前面例子里那样，使用promise来执行读取文件的操作了。其他异步回调转化成返回promise的异步方法基本上都可以参照这个模式来做。</p><p>###一次处理多个promise的###</p><p>如果你有几个异步方法，他们都返回promise，并且当这些方法都处理完之后，你才能进行下一步，Q提供了一个all()方法来帮助你消化多个promise。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Q.all([</span><br><span class="line">  readFile(<span class="string">'file1.json'</span>),</span><br><span class="line">  readFile(<span class="string">'file2.json'</span>)</span><br><span class="line">  ])</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">dataArray</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; dataArray.length; i++)&#123;</span><br><span class="line">      <span class="built_in">console</span>.log(dataArray[i]);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;, <span class="function"><span class="keyword">function</span>(<span class="params">err</span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(err);</span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure><p>在这里例子里，我们将一个promise数组传给 <strong>all()</strong> all返回一个promise，当数组里面的所有promise都为fullfilled状态时，我们的then()方法才会被调用。这时fullfilled值是一个数组，每个元素对应前面promise的fullfilled值。当任意一个promise变成rejected状态的时候，all的promise会立即reject而不等其他的完成。</p><p>###利用promise改写你的项目###</p><p>最佳的理解方法便是事件，你可以把一些nodejs的基本异步方法包装成promise，这样你就可以在整个程序的多个地方使用这些方法。并且让你的程序的异步代码看起来更整洁，更容易理解。阅读 <a href="http://documentup.com/kriskowal/q/" target="_blank" rel="noopener">Q的文档</a> 了解更多的API和方法。并在程序中使用这些方法，使你的代码更优美，逻辑更健壮。阅读 <a href="http://promises-aplus.github.io/promises-spec/" target="_blank" rel="noopener">Promise/A+</a> 。了解promise原理。</p>]]></content:encoded>
      
      <comments>http://nya.io/Node-js/promise-in-nodejs-get-rid-of-callback-hell/#disqus_thread</comments>
    </item>
    
    <item>
      <title>开始使用nya-bootstrap-select</title>
      <link>http://nya.io/uncategorized/getting-started-with-nya-bootstrap-select/</link>
      <guid>http://nya.io/uncategorized/getting-started-with-nya-bootstrap-select/</guid>
      <pubDate>Sat, 12 Apr 2014 13:51:47 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;好吧，这个post是个广告，如果你是一名web前端开发者，使用AngularJS，bootstrap和jquery。那么我在这里向你推荐我最近发布的一个开源项目：&lt;a href=&quot;http://nya.io/nya-bootstrap-select/&quot;&gt;&lt;strong&gt;ny
        
      
      </description>
      
      <content:encoded><![CDATA[<p>好吧，这个post是个广告，如果你是一名web前端开发者，使用AngularJS，bootstrap和jquery。那么我在这里向你推荐我最近发布的一个开源项目：<a href="http://nya.io/nya-bootstrap-select/"><strong>nya-bootstrap-select</strong></a></p><p>nya-bootstrap-select 是一个对<a href="http://silviomoreto.github.io/bootstrap-select/" target="_blank" rel="noopener">bootstrap-select</a>的包装，让这个功能强大的jquery插件可以用angularjs特有的风格在angularjs程序中使用。</p><p>依赖：    AngularJS 1.0+(这个是当然的） bootstrap-select 1.3+  bootstrap css （支持2.x和3.x）</p><p>安装：推荐使用<a href="http://bower.io" target="_blank" rel="noopener">bower</a>来安装，可以帮助你自动处理依赖关系。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bower install nya-bootstrap-select</span><br></pre></td></tr></table></figure><p>将src/nya-bootstrap-select.js加入到你的页面里</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;script src=&quot;bower_components/nya-bootstrap-select/src/nya-bootstrap-select.js&quot;&gt;&lt;/script&gt;</span><br></pre></td></tr></table></figure><p>将 nya.bootstrap.select作为angular app的依赖</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">angular.module(&apos;yourApp&apos;, [&apos;nya.bootstrap.select&apos;]);</span><br></pre></td></tr></table></figure><p>现在可以开始使用了。本文就只列举一个例子，更多例子请参考文档 <a href="http://nya.io/nya-bootstrap-select/">http://nya.io/nya-bootstrap-select/</a></p><p>在select标签里，加入class <code>nya-selectpicker</code> 也可以作为属性加入。 然后将你的scope变量绑定到ng-model上，这样你可以获取到选择的值。假设你的$scope.myOptions就是选项的条件<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;select class=&quot;nya-selectpicker&quot; data-container=&quot;body&quot; ng-model=&quot;myModel&quot; ng-options=&quot;c.value for c in myOptions&quot;&gt;</span><br><span class="line"></span><br><span class="line">&lt;/select&gt;</span><br></pre></td></tr></table></figure></p>]]></content:encoded>
      
      <comments>http://nya.io/uncategorized/getting-started-with-nya-bootstrap-select/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Angular 与 jQuery 加载顺序的问题</title>
      <link>http://nya.io/AngularJS/angular-jquery-load-order/</link>
      <guid>http://nya.io/AngularJS/angular-jquery-load-order/</guid>
      <pubDate>Sat, 08 Feb 2014 13:50:03 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;最近打算把一个自己写的angular directive开源，于是花了点时间写demo，但是在demo里曾经没有什么问题的插件不能正常的工作，令人匪夷所思。
发现问题之后，困扰了一段时间，把所有能怀疑的对象都检查了一遍，依然没发现问题，最后在无意间对比过去使用这个direc
        
      
      </description>
      
      <content:encoded><![CDATA[<p>最近打算把一个自己写的angular directive开源，于是花了点时间写demo，但是在demo里曾经没有什么问题的插件不能正常的工作，令人匪夷所思。发现问题之后，困扰了一段时间，把所有能怀疑的对象都检查了一遍，依然没发现问题，最后在无意间对比过去使用这个directive的项目代码和demo代码发现二者的angular.js与jquery.js加载顺序不同，于是调换了一下顺序，果然问题就解决了。瞬间有种要掀桌的感觉(╯°Д°)╯︵ ┻━┻。不过这个问题是为什么呢，百思不得其解，在Google了一番之后，在Google Group上找到了类似的<a href="https://groups.google.com/forum/#!topic/angular/6A3Skwm59Z4" target="_blank" rel="noopener">问题</a>。在原po的提示下，通过研究angular源码发现，原来angular会在加载到内存之后，查找 <code>window.jQuery</code> 如果存在，就对其进行扩展，并将jQLite绑定到jQuery上，以后创建出来的jQLite对象实际上也是jQuery对象。如果没找到，就使用angular自带的jQLite。以下是部分代码片段<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bindJQuery</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// bind to jQuery if present;</span></span><br><span class="line">  jQuery = <span class="built_in">window</span>.jQuery;</span><br><span class="line">  <span class="comment">// reset to jQuery or default to us.</span></span><br><span class="line">  <span class="keyword">if</span> (jQuery) &#123;</span><br><span class="line">    jqLite = jQuery;</span><br><span class="line">    extend(jQuery.fn, &#123;</span><br><span class="line">      scope: JQLitePrototype.scope,</span><br><span class="line">      isolateScope: JQLitePrototype.isolateScope,</span><br><span class="line">      controller: JQLitePrototype.controller,</span><br><span class="line">      injector: JQLitePrototype.injector,</span><br><span class="line">      inheritedData: JQLitePrototype.inheritedData</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// Method signature:</span></span><br><span class="line">    <span class="comment">//     jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments)</span></span><br><span class="line">    jqLitePatchJQueryRemove(<span class="string">'remove'</span>, <span class="literal">true</span>, <span class="literal">true</span>, <span class="literal">false</span>);</span><br><span class="line">    jqLitePatchJQueryRemove(<span class="string">'empty'</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">false</span>);</span><br><span class="line">    jqLitePatchJQueryRemove(<span class="string">'html'</span>, <span class="literal">false</span>, <span class="literal">false</span>, <span class="literal">true</span>);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    jqLite = JQLite;</span><br><span class="line">  &#125;</span><br><span class="line">  angular.element = jqLite;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>Angular在加载到内存之后便会执行这段代码。以后所有的angular.element()创建或包装的对象都是jqLite对象。如果弄错了加载顺序，那么使用$()创建出来的对象与angular的jqLite对象便存在兼容性问题，导致一些奇怪的现象。</p><p>###正确的做法###如果你要在项目中同时使用jQuery与AngularJS那么，一定要让jQuery在angularjs之前加载。比如下面这样写才能保证程序不出现奇怪的问题：</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"bower_components/jquery/jquery.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"bower_components/angular/angular.js"</span>&gt;</span><span class="undefined"></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure>]]></content:encoded>
      
      <comments>http://nya.io/AngularJS/angular-jquery-load-order/#disqus_thread</comments>
    </item>
    
    <item>
      <title>用Grunt与livereload构建实时预览的开发环境</title>
      <link>http://nya.io/Node-js/grunt-livereload-for-realtime-preview/</link>
      <guid>http://nya.io/Node-js/grunt-livereload-for-realtime-preview/</guid>
      <pubDate>Thu, 06 Feb 2014 05:40:42 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;自从用了&lt;a href=&quot;http://yeoman.io&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;yeoman&lt;/a&gt;来开发angular.js之后，就喜欢上这个工具了，顺带也了解了整个workflow所用的所有工具，yeoman给我最深的印象就是
        
      
      </description>
      
      <content:encoded><![CDATA[<p>自从用了<a href="http://yeoman.io" target="_blank" rel="noopener">yeoman</a>来开发angular.js之后，就喜欢上这个工具了，顺带也了解了整个workflow所用的所有工具，yeoman给我最深的印象就是它可以在开发的时候启动一个localhost的server来预览你的前端项目，并且能够实时反应你对文件的修改。想想把，你不在需要依赖昂贵又不准确的IDE进行WYSIWYG开发，也不需要自己动手去按F5不停的刷新页面，还要担心缓存问题。现在只要在命令行运行 <code>grunt serve</code> 就可以做到了这么好的功能当然不是yeoman特有的功能，只要你会用Grunt就可以做到。如果你还不熟悉Gruntjs那么，它的<a href="http://gruntjs.com/getting-started" target="_blank" rel="noopener">官方页面</a>可以帮你快速熟悉常用配置（是的，你不需要知道怎么写grunt plugin)</p><p>###我们需要哪些Grunt插件？###</p><p>过去这个任务需要connect-livereload, grunt-contrib-connect, grunt-contrib-watch来配合完成. 感谢connect和grunt插件的开发者，我们现在只需要2个插件就可以做到这一切了：</p><ul><li><strong>grunt-contrib-connect</strong>, 用来充当一个静态文件服务器，本身集成了livereload功能，因此不再需要connect-livereload中间件</li><li><strong>grunt-contrib-watch</strong>, 用来监视文件的改变，然后执行一些任务，同时保持 <strong>grunt-contrib-connect</strong> 的服务器一直开启</li></ul><p>为了不用不厌其烦的写grunt.loadNpmTask()，我们使用 <strong>load-grunt-tasks</strong> 来帮助我们自动加载这些插件，为了能看到grunt任务执行时间，我们加入了 <strong>time-grunt</strong> 插件，这几个插件对于本文的目的来说都不是必须的</p><p>下面是配置好的package.json</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="attr">"name"</span>: <span class="string">"grunt-livereload-demo"</span>,</span><br><span class="line">  <span class="attr">"version"</span>: <span class="string">"0.0.1"</span>,</span><br><span class="line">  <span class="attr">"devDependencies"</span>: &#123;</span><br><span class="line">    <span class="attr">"grunt"</span>: <span class="string">"~0.4.2"</span>,</span><br><span class="line">    <span class="attr">"grunt-contrib-connect"</span>: <span class="string">"~0.6.0"</span>,</span><br><span class="line">    <span class="attr">"grunt-contrib-watch"</span>: <span class="string">"~0.5.3"</span>,</span><br><span class="line">    <span class="attr">"load-grunt-tasks"</span>: <span class="string">"~0.3.0"</span>,</span><br><span class="line">    <span class="attr">"time-grunt"</span>: <span class="string">"~0.2.9"</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来运行 <code>npm install</code> 来安装就可以了。</p><p>###配置Gruntfile###</p><p>现在假设你已经知道Gruntfile的结构。那么让我们开始先搭建一个可以serve静态文件的服务器</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span>(<span class="params">grunt</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Load all grunt tasks automatically</span></span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'load-grunt-tasks'</span>)(grunt);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Time how long grunt task take. Can help when optimizing build times</span></span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'time-grunt'</span>)(grunt);</span><br><span class="line"></span><br><span class="line">  <span class="comment">//Configure grunt</span></span><br><span class="line">  grunt.initConfig(&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// The actual grunt server settings</span></span><br><span class="line">    connect: &#123;</span><br><span class="line">      options: &#123;</span><br><span class="line">        port: <span class="number">9000</span>,</span><br><span class="line">        hostname: <span class="string">'localhost'</span>, <span class="comment">// Change this to '0.0.0.0' to access the server from outside.</span></span><br><span class="line">        keepalive: <span class="literal">true</span> <span class="comment">// keep the server alive. so the grunt task won't stop</span></span><br><span class="line">      &#125;,</span><br><span class="line">      all: &#123;</span><br><span class="line">        options: &#123;</span><br><span class="line">          open: <span class="literal">true</span>,</span><br><span class="line">          base: [</span><br><span class="line">            <span class="string">'examples'</span> <span class="comment">// This is the base file folder. we suppose our index.html is located in this folder</span></span><br><span class="line">                       <span class="comment">// replace with the directory you want the files served from</span></span><br><span class="line">          ]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Creates the 'serve' task</span></span><br><span class="line">  grunt.registerTask(<span class="string">'serve'</span>, [</span><br><span class="line">    <span class="string">'connect:all'</span></span><br><span class="line">  ]);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>我们用grunt-contrib-connect创建了一个静态服务器，并且能自动打开浏览器。我们把配置好的任务加入到自己创建的serve任务里，别在意这里只有一个子任务，我们接下来会加入其他任务到这个serve任务里。现在你可以在命令行里运行 <code>grunt serve</code> （从Gruntfile所在的目录）。grunt会自动打开浏览器并访问 <a href="http://localhost:9000" target="_blank" rel="noopener">http://localhost:9000</a> 并保持服务器一直运行下去（任务不会结束）。</p><p>###Watch和livereload###</p><p>虽然我们已经配置好了一个静态文件服务器。但是 <strong>grunt-contrib-connect</strong> 并不会帮助我们监视文件变化并自动加载，我们需要 <strong>grunt-contrib-watch</strong> 来完成这项工作，并触发 <strong>grunt-contrib-connect</strong> 里面的livereload功能。所以我们要修改一下Gruntfile</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span>(<span class="params">grunt</span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Load all grunt tasks automatically</span></span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'load-grunt-tasks'</span>)(grunt);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Time how long grunt task take. Can help when optimizing build times</span></span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'time-grunt'</span>)(grunt);</span><br><span class="line"></span><br><span class="line">  <span class="comment">//Configure grunt</span></span><br><span class="line">  grunt.initConfig(&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// The actual grunt server settings</span></span><br><span class="line">    connect: &#123;</span><br><span class="line">      options: &#123;</span><br><span class="line">        port: <span class="number">9000</span>,</span><br><span class="line">        <span class="comment">// Change this to '0.0.0.0' to access the server from outside.</span></span><br><span class="line">        hostname: <span class="string">'localhost'</span>,</span><br><span class="line">        livereload: <span class="number">35729</span> <span class="comment">// This does not perform live reloading. this port is used by watch task to trigger a live reloading action.</span></span><br><span class="line">      &#125;,</span><br><span class="line">      all: &#123;</span><br><span class="line">        options: &#123;</span><br><span class="line">          open: <span class="literal">true</span>,</span><br><span class="line">          base: [</span><br><span class="line">            <span class="string">'examples'</span></span><br><span class="line">          ]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    <span class="comment">//Watch files for changes, and run tasks base on the changed files.</span></span><br><span class="line">    watch: &#123;</span><br><span class="line"></span><br><span class="line">      livereload: &#123;</span><br><span class="line">        options: &#123;</span><br><span class="line">          livereload: <span class="string">'&lt;%= connect.options.livereload %&gt;'</span> <span class="comment">// this port must be same with the connect livereload port</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="comment">// Watch whatever files you needed.</span></span><br><span class="line">        files: [</span><br><span class="line">          <span class="string">'examples/*.html'</span>,</span><br><span class="line">          <span class="string">'examples/styles/&#123;,*/&#125;*.css'</span>,</span><br><span class="line">          <span class="string">'examples/scripts/(,*/&#125;*.js'</span>,</span><br><span class="line">          <span class="string">'examples/images/&#123;,*/&#125;*.&#123;png,jpg,jpeg,gif,webp,svg&#125;'</span></span><br><span class="line">        ]</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="comment">// Creates the 'serve' task</span></span><br><span class="line">  grunt.registerTask(<span class="string">'serve'</span>, [</span><br><span class="line">    <span class="string">'connect:all'</span>,</span><br><span class="line">    <span class="string">'watch'</span></span><br><span class="line">  ]);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>我们新修改的配置把connect任务的keepalive选项去掉了，换成livereload。因为watch会帮我们让grunt task挂起而不停止，所以不再需要keepalive。这里我们将livereload的端口号设置为35729。你也可以设置成其他的端口，不过必须要在watch的livereload选项里设置成相同的端口。新加入了一个watch任务，这里我们配置了livereload的端口。配置了需要监视的文件。当这些文件发生改变时，watch任务就会触发livereload。让浏览器刷新页面。最后，我们把这两个任务按照先后顺序组合成serve任务，注意watch必须在后面，因为watch之后的任务永远不会被执行，同时我们也需要watch帮我们保持服务器一直运行。</p><p>接下来运行 <code>grunt serve</code> 就可以看到一个跟刚才一样的页面在浏览器窗口中打开了。不同的是，这次你修改任何监视范围内的文件，都会实时的反映在浏览器上，可以说做到的了实时预览。当你需要结束服务器的时候，使用Ctrl+C。watch并不止能做这些，它还可以让less自动编译，自动运行jshint检查js文件的语法。connect还可以和grunt-connect-proxy结合来制作本地代理访问其他域名的api而不用处理跨域问题。当你觉得某种开发方式不够酷的时候，想想grunt吧。如果你没有找到合适的插件，就自己编写一个。Have a good day!</p>]]></content:encoded>
      
      <comments>http://nya.io/Node-js/grunt-livereload-for-realtime-preview/#disqus_thread</comments>
    </item>
    
    <item>
      <title>Hello nya.io</title>
      <link>http://nya.io/miscellaneous/hello-nyaio/</link>
      <guid>http://nya.io/miscellaneous/hello-nyaio/</guid>
      <pubDate>Sat, 28 Dec 2013 13:20:11 GMT</pubDate>
      <description>
      
        
        
          &lt;p&gt;似乎很早就有搭建blog来记录一些技术上的心得这样的想法，不过碍于自己的行动力方面有些不足加上租用主机搭建wordpress又有些麻烦，而Octopress是ruby写的又不想学ruby，就一直都没有付诸行动。不过最近对NodeJS的兴趣和发现了Hexo这样的好工具，让我有
        
      
      </description>
      
      <content:encoded><![CDATA[<p>似乎很早就有搭建blog来记录一些技术上的心得这样的想法，不过碍于自己的行动力方面有些不足加上租用主机搭建wordpress又有些麻烦，而Octopress是ruby写的又不想学ruby，就一直都没有付诸行动。不过最近对NodeJS的兴趣和发现了Hexo这样的好工具，让我有了想要尝试blog的想法，于是这个blog就这样诞生了。</p><p>说起来，这个域名起的也很随便，一开始只是想要一个又短又好记的域名，没想到这个域名可以满足全部的要求，只是现在都没想好站名。索性就叫nyanyanya好了。域名注册过程要感谢<a href="https://plus.google.com/+PhoenixNemo/about" target="_blank" rel="noopener">Phenix Nemo</a>的帮助，他提供了域名注册的代理服务和域名挂靠的服务。另外hexo这个好用的工具也是他推荐的。没有他的帮助，这个blog也不会这么快的搭建起来。</p><p>最近我在为公司制作一个统计系统的前端页面，用到了Yeoman，AngularJS，Bootstrap，NodeJS，expressjs这些工具和技术，所以近期会尝试写一些关于这方面的文章吧。鉴于本人水平有限，如果有什么错误或者不够清晰的地方，希望能够批评指正。</p>]]></content:encoded>
      
      <comments>http://nya.io/miscellaneous/hello-nyaio/#disqus_thread</comments>
    </item>
    
  </channel>
</rss>
