Skip to content

Commit

Permalink
✨ Allow updating the title of the page using amp-bind (#14564)
Browse files Browse the repository at this point in the history
* Allow updating the title of the page using amp-bind

* Fix comments from @choumx and @honeybadgerdontcare
  • Loading branch information
josh313 authored and William Chou committed Apr 12, 2018
1 parent 97847f2 commit 14a9c4b
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 4 deletions.
35 changes: 35 additions & 0 deletions examples/bind/title.amp.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!doctype html>
<html >
<head>
<meta charset="utf-8">
<title [text]="'amp-bind: Title (' + myState.count + ')'">amp-bind: Title</title>
<link rel="canonical" href="amps.html">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Questrial" rel="stylesheet" type="text/css">
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<script async src="https://cdn.ampproject.org/v0.js"></script>
<script async custom-element="amp-bind" src="https://cdn.ampproject.org/v0/amp-bind-0.1.js"></script>

<style amp-custom>
button {
border: solid 1px black;
}
</style>
</head>

<body>
<h2>Title changing example</h2>
<p>After clicking the button below, a counter will be appended to this page's title in the browser and the number will increment on each subsequent button push.<p>
<div>
<button on="tap:AMP.setState({myState: {count: myState.count + 1}})">Click me</button>
</div>

<amp-state id="myState">
<script type="application/json">
{
"count": 0
}
</script>
</amp-state>
</body>
</html>
17 changes: 13 additions & 4 deletions extensions/amp-bind/0.1/bind-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ import {ChunkPriority, chunk} from '../../../src/chunk';
import {Services} from '../../../src/services';
import {deepMerge, dict} from '../../../src/utils/object';
import {dev, user} from '../../../src/log';
import {elementByTag, iterateCursor, waitForBodyPromise} from '../../../src/dom';
import {filterSplice} from '../../../src/utils/array';
import {getMode} from '../../../src/mode';
import {installServiceInEmbedScope} from '../../../src/service';
import {invokeWebWorker} from '../../../src/web-worker/amp-worker';
import {isArray, isObject, toArray} from '../../../src/types';
import {isFiniteNumber} from '../../../src/types';
import {iterateCursor, waitForBodyPromise} from '../../../src/dom';
import {map} from '../../../src/utils/object';
import {parseJson, recursiveEquals} from '../../../src/json';
import {reportError} from '../../../src/error';
Expand Down Expand Up @@ -161,7 +161,8 @@ export class Bind {
*/
this.initializePromise_ =
this.viewer_.whenFirstVisible().then(() => bodyPromise).then(body => {
return this.initialize_(body);
return this.initialize_(
body, elementByTag(ampdoc.getHeadNode(), 'title'));
});

/** @private {Promise} */
Expand Down Expand Up @@ -282,14 +283,19 @@ export class Bind {
/**
* Scans the ampdoc for bindings and creates the expression evaluator.
* @param {!Node} rootNode
* @param {Node} titleNode
* @return {!Promise}
* @private
*/
initialize_(rootNode) {
initialize_(rootNode, titleNode) {
dev().fine(TAG, 'Scanning DOM for bindings and macros...');
const nodes = [rootNode];
if (titleNode) {
nodes.push(titleNode);
}
let promise = Promise.all([
this.addMacros_(),
this.addBindingsForNodes_([rootNode])]
this.addBindingsForNodes_(nodes)]
).then(() => {
// Listen for DOM updates (e.g. template render) to rescan for bindings.
rootNode.addEventListener(AmpEvents.DOM_UPDATE, this.boundOnDomUpdate_);
Expand Down Expand Up @@ -868,6 +874,9 @@ export class Bind {
switch (property) {
case 'text':
element.textContent = String(newValue);
if (tag === 'TITLE') {
this.localWin_.document.title = String(newValue);
}
// Setting `textContent` on TEXTAREA element only works if user
// has not interacted with the element, therefore `value` also needs
// to be set (but `value` is not an attribute on TEXTAREA)
Expand Down
10 changes: 10 additions & 0 deletions extensions/amp-bind/0.1/test/integration/test-bind-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,16 @@ describe.configure().ifNewChrome().run('Bind', function() {
});
});

it('should update document title for <title> elements', () => {
const element = createElement(
env, container, '[text]="\'a\' + \'b\' + \'c\'"', 'title');
element.value = 'foo';
return onBindReadyAndSetState(env, bind, {}).then(() => {
expect(element.value).to.equal('abc');
expect(env.win.document.title).to.equal('abc');
});
});

it('should support binding to CSS classes with strings', () => {
const element = createElement(env, container, '[class]="[\'abc\']"');
expect(toArray(element.classList)).to.deep.equal([]);
Expand Down
1 change: 1 addition & 0 deletions validator/validator-main.protoascii
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ tags: {
tags: {
tag_name: "TITLE"
spec_name: "title"
attrs: { name: "[text]" }
}
# 4.2.3 the base element
tags: {
Expand Down

0 comments on commit 14a9c4b

Please sign in to comment.