元のネタはこちらのページ
https://www.polymer-project.org/0.5/articles/spa.html
後半に動的にページロードするサンプルが出ます。
原理はcore-ajaxからリクエストを送ってdivタグのidが'article-content'から下部を動的に表示する内容ですが、
サンプルのソースが巣のhtmlで書いているためいざ使う時に他のFWとの絡みがややこしい。
ので、element化してみました。
<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とかも適宜に修正してください
<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で使うたとすると
@(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>
こんな感じですかね。
(上記の2ファイルがpublic/elementsの配下にある場合)