Polymerのsingle page app tutorialで動的ページロードをelement化してみました

元のネタはこちらのページ
https://www.polymer-project.org/0.5/articles/spa.html

後半に動的にページロードするサンプルが出ます。
原理はcore-ajaxからリクエストを送ってdivタグのidが'article-content'から下部を動的に表示する内容ですが、
サンプルのソースが巣のhtmlで書いているためいざ使う時に他のFWとの絡みがややこしい。
ので、element化してみました。


```html:main-menu.html
<polymer-element name="main-menu" >

<template>

  <!-- Route controller. -->
  <!-- 
  <flatiron-director route="{{route}}" autoHash></flatiron-director>
  -->
  
  <!-- Keyboard nav controller. -->
  <core-a11y-keys id="keys" target="{{parentElement}}"
                  keys="up down left right space space+shift"
                  on-keys-pressed="{{keyHandler}}"></core-a11y-keys>

  <!-- Dynamic content controller -->
  <core-ajax id="ajax" url="{{selectedPage.page.url}}" handleAs="document"
             on-core-response="{{onResponse}}"></core-ajax>

  <core-scaffold id="scaffold">

    <nav>
      <core-toolbar>
        <span>Polymer Tutorial SPA</span>
      </core-toolbar>
      <core-menu id="menu" valueattr="hash"
                 selected="{{route}}"
                 selectedModel="{{selectedPage}}"
                 on-core-select="{{menuItemSelected}}" on-click="{{ajaxLoad}}">
        <template repeat="{{page, i in pages}}">
          <paper-item hash="{{page.hash}}" noink>
            <core-icon icon="label{{route != page.hash ? '-outline' : ''}}"></core-icon>
            <a href="{{page.url}}">{{page.name}}</a>
          </paper-item>
        </template>
      </core-menu>
    </nav>

    <core-toolbar tool flex>
      <div flex>{{selectedPage.page.name}}</div>
      <core-icon-button icon="refresh"></core-icon-button>
      <core-icon-button icon="add"></core-icon-button>
    </core-toolbar>

    <div layout horizontal center-center fit>
      <core-animated-pages id="apages" selected="{{route}}" valueattr="hash"
                           transitions="slide-from-right">
        <template repeat="{{page, i in pages}}">
          <section hash="{{page.hash}}" layout vertical center-center>
            <div style="max-width:100%;">Loading...</div>
          </section>
        </template>
      </core-animated-pages>
    </div>

  </core-scaffold>
  
</template>

<script>
Polymer({
	DEFAULT_ROUTE : 'one',
	pages : [
      {name: 'Shadow DOM 101', hash: 'one', url: '//www.html5rocks.com/en/tutorials/webcomponents/shadowdom/'},
      {name: 'Shadow DOM 201', hash: 'two', url: '//www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/'},
      {name: 'Shadow DOM 301', hash: 'three', url: '//www.html5rocks.com/en/tutorials/webcomponents/shadowdom-301/'},
      {name: 'Custom Elements', hash: 'four', url: '//www.html5rocks.com/en/tutorials/webcomponents/customelements/'}
	],

	created : function() {
		  this.cache = {};
	},
	
    domReady: function() {
		this.ajax = this.shadowRoot.querySelector('#ajax');
		this.scaffold = this.shadowRoot.querySelector('#scaffold');
		this.apages = this.shadowRoot.querySelector('#apages');
		this.keys = this.shadowRoot.querySelector('#keys');
		
		// Allow selecting pages by num keypad. Dynamically add
		// [1, template.pages.length] to key mappings.
		var keysToAdd = Array.apply(null, this.pages).map(function(x, i) {
		  return i + 1;
		}).reduce(function(x, y) {
		  return x + ' ' + y;
		});
		this.keys.keys += ' ' + keysToAdd;
		
		this.route = this.route || this.DEFAULT_ROUTE; // Select initial route.
    },
	
    
    keyHandler : function(e, detail, sender) {
    	  // Select page by num key.
    	  var num = parseInt(detail.key);
    	  if (!isNaN(num) && num <= this.pages.length) {
    	    this.apages.selectIndex(num - 1);
    	    return;
    	  }

    	  switch (detail.key) {
    	    case 'left':
    	    case 'up':
    	      this.apages.selectPrevious();
    	      break;
    	    case 'right':
    	    case 'down':
    	      this.apages.selectNext();
    	      break;
    	    case 'space':
    	      detail.shift ? this.apages.selectPrevious() : this.apages.selectNext();
    	      break;
    	  }
  	},

	menuItemSelected : function(e, detail, sender) {
  	  if (detail.isSelected) {

  	    // Need to wait one rAF so <core-ajax> has it's URL set.
  	    this.async(function() {
  	      if (!this.cache[this.ajax.url]) {
  	        this.ajax.go();
  	      }

  	      this.scaffold.closeDrawer();
  	    });

  	  }
  	},
	       
	ajaxLoad : function(e, detail, sender) {
		e.preventDefault(); // prevent link navigation.
	},

	onResponse : function(e, detail, sender) {
		  var article = detail.response.querySelector('#article-content');

		  article.querySelector('.byline').remove();

		  // Fix up image paths to not be local.
		  [].forEach.call(article.querySelectorAll('img'), function(img) {
		    img.setAttribute('src', img.src);
		  });

		  var html = article.innerHTML;

		  this.cache[this.ajax.url] = html; // Primitive caching by URL.

		  this.injectBoundHTML(html, this.apages.selectedItem.firstElementChild);
	}
	
       
});

</script>
</polymer-element>

```

ざっくり解説
shadowRootからオブジェクトを読めるタイミングはcreatedではなく、domReadyからであるので初期化もそこから行っています。
なので本当はcreatedは要らない
domReadyにまとめちゃってもかまいません。

公式polymerの記事はよく出来ているけど、サンプルコードで一つ悪いのが
templateの配列要素とcore-animated-pagesのidが同じ名前の"pages"であって紛らわしいですね。
今回はcore-animated-pagesのidをapagesに変えました

flatiron-directorは使うつもりがないのでコメントアウト。
もし使う場合はimportをいれてください。
polymer-elementのnameとかpagesのurlデータとか、titleとかも適宜に修正してください


```html:import.html
<script src="../bower_components/webcomponentsjs/webcomponents.min.js" type="text/javascript"></script>
<link rel="import" href="../bower_components/polymer/polymer.html">

<link rel="import" href="../bower_components/core-a11y-keys/core-a11y-keys.html">
<link rel="import" href="../bower_components/core-ajax/core-ajax.html">
<link rel="import" href="../bower_components/core-animated-pages/core-animated-pages.html">
<link rel="import" href="../bower_components/core-animated-pages/transitions/slide-from-right.html">

<link rel="import" href="../bower_components/core-drawer-panel/core-drawer-panel.html">
<link rel="import" href="../bower_components/core-icon/core-icon.html">
<link rel="import" href="../bower_components/core-icon-button/core-icon-button.html">
<link rel="import" href="../bower_components/core-item/core-item.html">
<link rel="import" href="../bower_components/core-menu/core-menu.html">
<link rel="import" href="../bower_components/core-scaffold/core-scaffold.html">
<link rel="import" href="../bower_components/core-toolbar/core-toolbar.html">

<link rel="import" href="../bower_components/font-roboto/roboto.html">

<link rel="import" href="../bower_components/paper-item/paper-item.html">

```

いざ使う場合は、、たとえばplay frameworkで使うたとすると

```html:index.scala.html
@(title: String)(content: Html)

<!DOCTYPE html>

<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
        <title>@title</title>
        <link rel="import" href="@routes.Assets.at("elements/import.html")">
        <link rel="import" href="@routes.Assets.at("elements/main-menu.html")">
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")" shim-shadowdom>
    </head>

<body unresolved fullbleed>
	<main-menu>main</main-menu>
</body>
</html>

```

こんな感じですかね。
(上記の２ファイルがpublic/elementsの配下にある場合）