Polymer &
Web Components

Getting Started With Polymer

Pete Johanson / @petejohanson

Applications Existing Frameworks Web Components (Polymer?) Web Platform

No Panacea

Panacea

Considerations

  • Progressive Enhancement Challenges
  • Server Side Rendering?
  • Browser Support

Existing Approaches


<head>
  <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
  <script src="//code.jquery.com/jquery-1.10.2.js"></script>
  <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
  <script>
  $(function() {
    $( "#menu" ).menu({ disabled: true });
  });
  </script>
</head>
<body>

<ul id="menu">
	<li>Item 1</li>
	<li>Item 2</li>
</ul>
						

Polymer


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

<body>
	<paper-menu selected="1">
		<paper-item>Item 1</paper-item>
		<paper-item>Item 2</paper-item>
	</paper-menu>
</body>
						

Features

  • Declared Properties
  • Local/Light DOM
  • Data Binding
  • Events
  • Scoped Styles and Custom CSS Properties

Declared Properties


Polymer({
	is: 'my-gravatar',
	properties: {
		email: String,
		size: {
			type: String,
			value: ''
		},
		/* ... */
	}
});
						

Computed Properties


Polymer({
	is: 'my-gravatar',

	properties: {
		email: String,
		size: String,
		imgsrc: {
			type: String,
			computed: 'computeImageSource(email, size)'
		}
	},

	computeImageSource: function(email, size) {
		return ...;
	}
});
						

Change Notification

Needed for two-way data binding


Polymer({
	is: 'my-chooser',

	properties: {
		choice: {
			type: String,
			notify: true,
		}
	},
});
						

Local (Shadow) DOM


<dom-module id="my-gravatar">
	<template>
		<img src="{{imgsrc}}">
	</template>
	...
</dom-module>
						

Automatic Node Finding


<dom-module id="my-gravatar">
	<template>
		<img id="gravatar">
	</template>
	<script>
		Polymer({
			is: 'my-gravatar',
			ready: function() {
				this.$.gravatar.src = '//gravatar.com/avatar/abcdef';
			}
		});
	</script>

</dom-module>
						

DOM Manipulation

Local DOM


var toLocal = document.createElement('div');
var beforeNode = Polymer.dom(this.root).childNodes[0];
Polymer.dom(this.root).insertBefore(toLocal, beforeNode);
						

Light DOM


Polymer.dom(this).appendChild(document.createElement('div'));
var allSpans = Polymer.dom(this).querySelectorAll('span');
							

Light DOM


<dom-module id="my-strongbad">
	<template>
		<strong><content></content></strong>
	</template>
	...
</dom-module>
						

<my-strongbad>Deleted!</my-strongbad>
						

Data Binding


<dom-module id="my-gravatar">
	<template>
		<input type="text" value={{email::input}}></input>
		<input type="text" value={{size::input}}></input>
		<img src="{{imgsrc}}">
	</template>
	...
</dom-module>
						

One-Way vs Two-Way Bindings


<template>
	<my-gravatar email="[[email]]"></my-gravatar>
</template>
						

<template>
	<my-chooser choice="{{choice}}"></my-chooser>
</template>

One-Way Binding

Host-To-Child


<template>
	<my-gravatar email="[[email]]"></my-gravatar>
	<input type="text" value="{{email::input}}">
</template>
<script>
	Polymer({
		is: 'my-element',
		properties: {
			email: String,
		},
	});
</script>
						

Two-Way Binding

Bi-directional between child and host


<template>
	<my-chooser choice="{{type}}"></my-chooser>
</template>
<script>
	Polymer({
		is: 'my-element',
		properties: {
			type: String,
		},
	});
</script>

Events

  • Declarative event listeners
  • Annotated event listeners
  • Custom Event Firing

Declarative Event Listeners


Polymer({
	is: 'x-custom',

	listeners: {
		'tap': 'regularTap',
		'special.tap': 'specialTap'
	},

	regularTap: function(e) {
		alert("Thank you for tapping");
	},
	specialTap: function(e) {
		alert("It was special tapping");
	}
});
						

Annotated Event Listeners


<button on-click="buttonClick">Click Me</button>
						

Event Firing


<dom-module id="x-custom">
	<template>
		<button on-click="handleClick">Kick Me</button>
	</template>

	<script>

		Polymer({
			is: 'x-custom',

			handleClick: function(e, detail) {
				this.fire('kick', {kicked: true});
			}
		});

	</script>
</dom-module>
						

Styling

Scoped Styles


<template>
	<style>
		:host { /* Selector to style the host DOM element */
			display: block;
		}

		.content-wrapper > ::content .warning { /* Light DOM */
			color: red;
		}
	</style>

	<div class="content-wrapper"><content></content></div>
</template>
						

Cross Scope Styles

"Theming"


<template>
	<style>
		:host { /* Selector to style the host DOM element */
			display: block;
		}

		.content-wrapper > ::content .warning { /* Light DOM */
			color: var(--my-element-warning-color, red);
		}
	</style>

	<div class="content-wrapper"><content></content></div>
</template>
						

CSS Mixins


<template>
	<style>
		:host { /* Selector to style the host DOM element */
			display: block;
			@apply(--my-element-theme);
		}
	</style>
</template>
						

<style>
	:host {
		--my-element-theme {
			background-color: green;
		}
	}
</style>
						

Element Catalog

Polymer Starter Kit

  • Best Practices Baked In
  • Build
  • Offline Support
  • Testing

$ wget https://github.com/PolymerElements/polymer-starter-kit/releases/download/v1.0.3/polymer-starter-kit-1.0.3.zip
$ unzip polymer-starter-kit-1.0.3.zip
$ cd polymer-starter-kit-1.0.3
$ npm install && bower install
$ gulp serve
						

Slides

http://petejohanson.github.io/nerdsummit-2015-polymer