-
Notifications
You must be signed in to change notification settings - Fork 1
Creating a simple component
TWC does not add any funky logic to Polymer, so concepts are pretty much the same. All it requires additionally, is typescript installed (globally or locally). The following tutorial will guide you through creating a simple component in Polymer V2, which lets you select an online state (online or offline) and reflect the change on a host attribute. There are a lot of ways you can take, but here we will take a single simple approach.
- Code editor
- Node.js
- Bower
- Polymer CLI
- TWC
TL;DR
Nothing comes out of the box (at least in most cases), so first you will need to get familiar with some general development terms and tools. The most important things are a source code editor and a terminal/command line. Next in a row is having Node.js and Bower installed globally. These are package repositories with software that you will need. Next you need you install polymer-cli and twc (both globally).
npm init
tsc --init
npm install typescript twc --save-dev
Please note. To have types properly handled, add
node_modules/twc/types/polymer.decorators.d.tstoincludesection in yourtsconfig.json!
TL;DR
Let us begin with installing core stuff. First fire up a terminal within a folder you want to develop in, this will be the root of your project. Now run npm init (answer all the questions) and tsc --init. This will create two additional files: package.json and tsconfig.json. The first one is a configuration file for npm repository (for twc and TypeScript), the second one hold TypeScript project configuration.
Now let's install developer dependencies: npm install typescript twc --save-dev. This will install typescript and twc locally (for this project only) and add them as development dependencies. You can now see that package.json file has changed and now has devDependencies inside. Now you are ready to create a component!
polymer init --name polymer-2-element
Please note. In this tutorial, component will be named online-state
TL;DR
Next thing you need to do is creating a project using polymer-cli. To do that, go to your terminal and run the following: polymer init and select polymer-2-element as a starter template.
Now you have a valid Polymer 2 sample component! To see how it looks, run polymer serve and in the web browser open this link http://127.0.0.1:8081/components/COMPONENT-NAME (replace COMPONENT-NAME with the name of your component).
Lets analyze the sample Polymer component:
<link rel="import" href="../polymer/polymer-element.html">
<dom-module id="online-state">
<template>
<style>
:host {
display: block;
}
</style>
<h2>Hello [[prop1]]!</h2>
</template>
<script>
/**
* `online-state`
* Lets you select an online state (online or offline) and reflect the change on a host attribute.
*
* @customElement
* @polymer
* @demo demo/index.html
*/
class OnlineState extends Polymer.Element {
static get is() { return 'online-state'; }
static get properties() {
return {
prop1: {
type: String,
value: 'online-state'
}
};
}
}
window.customElements.define(OnlineState.is, OnlineState);
</script>
</dom-module>What we have here is an import and a <dom-module> declaration.
Please note! The path is generated to work with Polymer CLI and looks like it in an own folder within bower_components.
Within the <dom-module> we have a <template> and <script> tags. First one holds a template and styles, while the second one holds the actual component declaration. Declaration itself consists of a JSDoc comment, and a class with a tag name (string returned from static get is) and properties.
Lets use this information to scaffold a sample typescript class to be our base. Change component file extension from .html to .ts and replace the contents to this:
import { CustomElement } from "twc/polymer";
import "bower:polymer/polymer-element.html";
/**
* `online-state`
* Lets you select an online state (online or offline) and reflect the change on a host attribute.
*
* @customElement
* @polymer
* @demo demo/index.html
*/
@CustomElement()
class OnlineState extends Polymer.Element {
prop1: string = "online-state";
template() {
return `
<style>
:host {
display: block;
}
</style>
<h2>Hello [[prop1]]!</h2>
`;
}
}The above code contains the same details. It imports polymer/polymer-element.html, contains the exactly same JSDoc comment, a class with the same name and heritage, prop1 string property with default value, style and a template. It looks different though, so lets get through it.
It now skips the relative path (which sometimes is troublesome) and tells "import polymer/polymer-element.html from bower repository".
Instead of declaring an object with properties config, we use a TypeScript properties declaration. It only requires to list properties within a class body, add a type and optionally assign a default value. As easy as that!
This is almost identical to original code. The only difference is that instead of declaring it within <template> tags, we return it from a template() function.
The only thing added is a @CustomElement() decorator imported from twc/polymer package. This registers the component as a Custom Element and removes the need to register it manually using customElements.define().
So what next? First of all we need a template with 3 things:
- container reflecting current status
- select with options
- button to disable the select (because we can!)
This is how your template() method should look like:
template() {
return `
<style>
:host {
display: block;
}
</style>
<strong>You are [[status]]</strong>
<select value="{{currentIndex::change}}" disabled="[[selectDisabled]]">
<template is="dom-repeat" items="{{statusList}}">
<option value="[[index]]">[[item]]</option>
</template>
</select>
<button type="button" on-click="toggleEdit">Toggle disable</button>`;
}This is what normal Polymer template looks like, and this is how it looks with twc, no magic here. Now more interesting part - lets make it work!
From the template we can see that we need a status, currentIndex, selectDisabled and statusList properties and a toggleEdit() method. To explain a bit:
-
currentIndex- a number taking an index of current a option -
selectDisabled- a boolean indicating whether select should or should not be disabled -
statusList- an array of available statuses -
status- a computed property, taking a current index and list of available statuses to return a status -
toggleEdit()- a method flippingselectDisabledproperty
Here is how it looks like:
class OnlineStatus extends Polymer.Element {
readonly statusList = [ "online", "offline" ];
currentIndex = 0;
selectDisabled = false;
@compute((statusList, currentIndex) => statusList[currentIndex]) @attr() @notify() status: string;
toggleEdit() {
this.selectDisabled = !this.selectDisabled;
}
template() {
return `
<style>
:host {
display: block;
}
</style>
<strong>You are [[status]]</strong>
<select value="{{currentIndex::change}}" disabled="[[selectDisabled]]">
<template is="dom-repeat" items="{{statusList}}">
<option value="[[index]]">[[item]]</option>
</template>
</select>
<button type="button" on-click="toggleEdit">Toggle disable</button>`;
}As you can see, the statusList is a read only property and has an initial value of an array. You might know that non-primitive default values should be wrapped with a function, but you don't need to worry about that here, twc will do that for you.
currentIndex and selectDisabled have assigned primitive values, but as with statusList, there is no type declaration. How so? Twc analyses the assigned value and fetches the type out of them, if not provided explicitly.
Next thing to notice is status property. It has an explicit type of a string and 3 decorators:
-
@attr()setsreflectToAttributeto true -
@notify()setsnotifyto true -
@compute()creates a resolver method and sets the property to be a computed property
A bit more on @compute(). We provided it with an array function: (statusList, currentIndex) => statusList[currentIndex] and did nothing more. It works pretty simple: argument names are properties it relies on, and the function itself is created as a class method and assigned to the prototype. The name given to the method is then set to computed config of the property and arguments are passed to it.
Once you are with the code, head to the terminal and run twc. It works almost identical to tsc, but generates .html file instead of .js. Just as tsc it reads configuration from tsconfig.json file (like rootDir, outDir, target, etc.), so for more details head over here.
With the following config:
{
"compilerOptions": {
"target": "es6",
"experimentalDecorators": true
},
"include": [
"node_modules/twc/types/polymer.decorators.d.ts"
],
"files": [
"online-status.ts"
]
}
running the twc should create online-status.html file at your projects root, which should look roughly like this:
<link rel="import" href="../polymer/polymer-element.html">
<!--
`online-status`
Lets you select an online state (online or offline) and reflect the change on a host attribute.
@customElement
@polymer
@demo demo/index.html
-->
<dom-module id="online-status">
<template>
<style>
:host {
display: block;
}
</style>
<strong>You are [[status]]</strong>
<select value="{{currentIndex::change}}" disabled="[[selectDisabled]]">
<template is="dom-repeat" items="{{statusList}}">
<option value="[[index]]">[[item]]</option>
</template>
</select>
<button type="button" on-click="toggleEdit">Toggle disable</button>
</template>
<script>
class OnlineStatus extends Polymer.Element {
static get is() {
return "online-status";
}
static get properties() {
return {
statusList: {
type: Array,
value: function() {
return ["online", "offline"];
},
readOnly: true
},
currentIndex: {
type: Number,
value: 0,
observer: "statusChanged"
},
status: {
type: String,
reflectToAttribute: true,
notify: true,
computed: "_statusComputed(statusList, currentIndex)"
},
selectDisabled: {
type: Boolean,
value: false
}
};
}
_statusComputed(statusList, currentIndex) {
return statusList[currentIndex];
}
statusChanged(newStatus, oldStatus) {
console.log(`currentIndex has changed ${oldStatus} => ${newStatus}`);
}
toggleEdit() {
this.selectDisabled = !this.selectDisabled;
}
}
customElements.define(OnlineStatus.is, OnlineStatus);
</script>
</dom-module>
This is it! This tutorial should explain basic concepts of twc. Further documentation will come with time (How To guides and detailed info of how it works internally, as well as API docs), so please be patient :).