<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Reactive web component example with subclassing</title>
<template id="template">
<button id="decrement">-</button>
<span id="visibleValue"></span>
<button id="increment">+</button>
</template>
<script type="module">
import AttributeMarshallingMixin from 'https://cdn.rawgit.com/elix/elix/master/src/AttributeMarshallingMixin.js';
import { merge } from 'https://cdn.rawgit.com/elix/elix/master/src/updates.js';
import RenderUpdatesMixin from 'https://cdn.rawgit.com/elix/elix/master/src/RenderUpdatesMixin.js';
import ReactiveMixin from 'https://cdn.rawgit.com/elix/elix/master/src/ReactiveMixin.js';
import ShadowTemplateMixin from 'https://cdn.rawgit.com/elix/elix/master/src/ShadowTemplateMixin.js';
import symbols from 'https://cdn.rawgit.com/elix/elix/master/src/symbols.js';
// Create a native web component with reactive behavior.
const Base =
AttributeMarshallingMixin(
ReactiveMixin(
RenderUpdatesMixin(
ShadowTemplateMixin(
HTMLElement
))));
class IncrementDecrement extends Base {
componentDidMount() {
if (super.componentDidMount) { super.componentDidMount(); }
this.$.decrement.addEventListener('click', () => {
this.value--;
});
this.$.increment.addEventListener('click', () => {
this.value++;
});
}
// This property becomes the value of this.state at constructor time.
get defaultState() {
return Object.assign({}, super.defaultState, {
value: 0
});
}
// Udpates represent property/attribute assignments to apply to the
// component host and elements in its shadow subtree during rendering of
// the current state. This is part of a rendering pipeline:
//
// events -> methods/properties -> state -> render
//
get updates() {
return {
style: {
color: this.state.value < 0 ? 'red' : null
},
$: {
visibleValue: {
textContent: this.state.value
}
}
}
}
// Provide a public property that gets/sets state.
get value() {
return this.state.value;
}
set value(value) {
this.setState({
value: parseInt(value)
});
}
get [symbols.template]() {
return template;
}
}
customElements.define('increment-decrement', IncrementDecrement);
class CustomIncrementDecrement extends IncrementDecrement {
get updates() {
const base = super.updates;
const baseColor = base.style && base.style.color;
// Pick a color if the base class didn't specify one.
const color = baseColor || (this.state.value > 0 ? 'dodgerblue' : null);
const buttonStyle = {
background: '#444',
border: 'none',
'border-radius': 0
};
const decrementDisabled = this.state.value <= -5;
const incrementDisabled = this.state.value >= 5;
// Merge updates on top of those defined by the base class. This lets us
// preserve some of the base rendering, while adding our own styling and
// some unique behavior.
return merge(super.updates, {
style: {
background: 'lightgray',
color,
'font-family': 'Helvetica, Arial, sans-serif',
'font-weight': 'bold'
},
$: {
decrement: {
attributes: {
disabled: decrementDisabled
},
style: merge(buttonStyle, {
color: decrementDisabled ? 'gray' : 'white'
})
},
increment: {
attributes: {
disabled: incrementDisabled
},
style: merge(buttonStyle, {
color: incrementDisabled ? 'gray' : 'white'
})
}
}
});
}
get value() {
return super.value;
}
set value(value) {
// Enforce a bound of -5 to 5 on the value.
const parsed = parseInt(value);
const bounded = Math.max(Math.min(parsed, 5), -5);
super.value = bounded;
}
}
customElements.define('custom-increment-decrement', CustomIncrementDecrement);
</script>
</head>
<body>
<p>
This shows a plain increment/decrement component and a subclass with styling
and custom behavior.
</p>
<increment-decrement></increment-decrement>
<custom-increment-decrement></custom-increment-decrement>
</body>
</html>
Output
300px
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |