polymer 2.0 two way binding - polymer

I am experimenting with Polymer 2.0 and have a simple class. It has an input text box that does not respond when changing its content. What is needed to reflect changes to text box to reflect as two way binding?
The code for the class follows.
<!-- Styles MUST be inside template -->
<style>
</style>
<div>
<input type="text" id="greeting" name="greeting" value="{{greeting}}"/>
<slot></slot>
{{greeting}}
</div>
// Extend Polymer.Element base class
class Polymer2Class extends Polymer.Element {
static get is() { return 'polymer-2-class' }
static get config() {
return {
properties: {
greeting: {
type: String,
value: "Hello",
notify: true
//observer: 'greetingChanged'
}
},
observers: [
'greetingChanged(greeting)'
]
}
}
constructor() {
super();
console.log('created');
}
connectedCallback() {
super.connectedCallback();
console.log('attached');
}
ready() {
this.addEventListener('click', (e)=>this.handleClick(e));
this._ensureAttribute('tabIndex', 0);
super.ready();
console.log('ready');
}
greetingChanged(greeting) {
if(greeting === undefined) {
console.log("greetingChanged: undefined");
} else {
console.log("greetingChanged: " + greeting);
}
}
handleClick(e) {
console.log("hamdleClick: " + e.type);
}
// Register custom element definition using standard platform API
customElements.define(Polymer2Class.is, Polymer2Class);

I assume you're expecting greeting to appear below the <input> exactly as you type it. You'll need to use two-way native binding on the <input>'s input event:
<input type="text" value="{{greeting::input}}">
codepen

Related

how to bind the values using object in polymer 3.x?

I'm trying to bind the values using an object reference like this:
this.set('state', tab1Data);
but values are not binding with respective input fields.
We are using the polymer 3.x version
It depends very much on where you bind your data and how you bind it.
As you provide not so much information on the concrete context, I would guess, that you bind your data, outside the element ths calls this.set('state', ...) .
Then your property needs to have the notify-flag been set to true, otherwise changes won't be propagated outside.
The following code shows this behavior:
<html>
<head>
<script src="https://unpkg.com/#webcomponents/webcomponentsjs#latest/webcomponents-loader.js"></script>
</head>
<body>
<script type="module">
import {PolymerElement, html} from 'https://unpkg.com/#polymer/polymer/polymer-element.js?module';
import 'https://unpkg.com/#polymer/paper-button/paper-button.js?module';
class MyElement extends PolymerElement {
static get properties() {
return {
someData: {
type: Object,
value: () => {return {}},
notify: true
},
otherData: {
type: Object,
value: () => {return {}},
notify: false
},
counter: Number
}
}
constructor() {
super();
this.counter = 0;
}
static get template() {
return html`
<style> :host { display: block; } .mood { color: green; } </style>
Mood: <span class="mood">[[someData.mood]] ([[otherData.mood]])</span>
<paper-button raised on-click="setNewObject">set object</paper-button>
<paper-button raised on-click="setNewValue">set value</paper-button>
`;
}
setNewObject() {
this.counter++
const mood = `good [${this.counter}]`
this.set('someData', {mood: mood});
this.set('otherData', {mood: mood});
}
setNewValue() {
this.counter++
const mood = `better [${this.counter}]`
this.set('someData.mood', mood);
this.set('otherData.mood', mood);
}
}
customElements.define('my-element', MyElement);
class MySecondElement extends PolymerElement {
static get properties() {
return {
mood: String
}
}
static get template() {
return html`Web Components are <span>[[mood]]</span>!`;
}
}
customElements.define('my-second-element', MySecondElement);
class MyApp extends PolymerElement {
static get template() {
return html`
<my-element some-data="{{someData}}" other-data="{{otherData}}" ></my-element>
<h2>notify true</h2>
<my-second-element mood="[[someData.mood]]"></my-second-element>
<h2>notify false</h2>
<my-second-element mood="[[otherData.mood]]"></my-second-element>
`;
}
}
customElements.define('my-app', MyApp);
</script>
<my-app></my-app>
</body>
</html>
You should put the code on the value which set on the localstorage. I show it here.
import '#polymer/iron-localstorage/iron-localstorage.js';
....
<iron-localstorage name="user-info" value="{{userInfo}}" on-iron-localstorage-load-empty="initializeUserInfo"></iron-localstorage>
<vaadin-text-field id="test">
........
initializeUserInfo(){
this.set('userInfo', []);
}
ready(){
super.ready();
this.set('userInfo', this.$.test.value);
}
This is able to set data in the localstorage.

How to implement lit-html instead of juicy-html in a polymer 3 component?

I am converting an app from polymer 1 to polymer 3. I used juicy-html, but they have not updated to Polymer 3 and I see that there is lit-html. I am wondering how I can change this snippet to using lit-html. It is used for expanding a string like: 'Hello <span class="highlight">world</span>!'
Here is the snippet of code from my polymer 1 component.
<template is="dom-repeat" items="[[item.snippets]]">
<template is="dom-repeat" items="[[item.matches]]">
<div><template is="juicy-html" content$="[[item.text]]"></template></div>
</template>
</template>
Do I need to implement a new component for the inner div? Is there an example that I can look at?
Here is the resulting Polymer 3 element to display a highlighted text within a string:
import {html, LitElement} from '#polymer/lit-element/lit-element.js';
/**
* `search-snippet-highlight`
*
*
* #customElement
* #polymer
* #demo demo/index.html
*/
class SearchSnippetHighlight extends LitElement {
static get properties() {
return {
snippet: { type: String }
};
}
render() {
return html`
<style>.highlight { background-color: yellow; }</style>
<div .innerHTML="${this.sanitizeHtml(this.snippet)}"></div>`;
}
sanitizeHtml(input) {
return input; // TODO: actually sanitize input with sanitize-html library
}
}
window.customElements.define('search-snippet-highlight', SearchSnippetHighlight);
The equivalent of that <template> with juicy-html in Polymer's LitElement (the recommended base element that uses lit-html) is:
render() {
let content = '';
for (const s of this.item.snippets) {
for (const m of s.matches) {
content += `<div>${m.text}</div>`;
}
}
return html`<div .innerHTML="${this.sanitizeHtml(content)}"></div>`;
}
The render function above does the following:
builds an HTML string from inputs
sanitizes the HTML
puts the result into the container div's innerHTML, using LitElement syntax (.PROPERTY="VALUE")
<html>
<head>
<!-- Polyfills only needed for Firefox and Edge. -->
<script src="https://unpkg.com/#webcomponents/webcomponentsjs#latest/webcomponents-loader.js"></script>
</head>
<body>
<!-- Works only on browsers that support Javascript modules like
Chrome, Safari, Firefox 60, Edge 17 -->
<script type="module">
import {LitElement, html} from 'https://unpkg.com/#polymer/lit-element/lit-element.js?module';
class MyElement extends LitElement {
static get properties() {
return {
item: { type: Object }
}
}
constructor() {
super();
this.item = {
snippets: [
{
matches: [
{text: 'hello <span class="highlight">world</span>'},
{text: 'we are the <span class="highlight">world</span>'},
{text: 'war of the <span class="highlight">world</span>s'},
]
},
{
matches: [
{text: 'the <span class="highlight">cat</span> in the hat'},
{text: '<span class="highlight">cat</span>fish are in the water'},
{text: '<span class="highlight">cat</span>erpillars become butterflies'},
]
},
]
};
}
render() {
let content = '';
for (const s of this.item.snippets) {
for (const m of s.matches) {
content += `<div>${m.text}</div>`;
}
}
return html`
<style>.highlight { background: rgb(255, 251, 222); }</style>
<div .innerHTML="${this.sanitizeHtml(content)}"></div>`;
}
sanitizeHtml(input) {
return input; // TODO: actually sanitize input with sanitize-html library
}
}
customElements.define('my-element', MyElement);
</script>
<my-element mood="great"></my-element>
</body>
</html>
I put this in an answer as #tony19 was suggesting it can be formatted properly. I wasn't really asking a separate question, really just asking a rhetorical one rather than offering a separate solution.
You can simplify the approach quite a bit as lit allows you to build up your html in layers through the map function.
render() {
return html`
<div>${this.item.snippets.map(s => s.matches.map(m =>
html`<div>${this.sanitizeHtml(m.text)}</div>`
))}
</div>
`;
}
Now one issue with this (and the accepted answer) is that every render you recalculate the whole thing, including sanitizing the Html. lit-html has a guard directive that can help with this. Adding this will provide for it only re-rendering on a change.
render() {
return html`
<div>${guard(this.item.snippets, () => this.item.snippets.map(s =>
guard(s.matches, () => s.matches.map(m =>
html`<div>${this.sanitizeHtml(m.text)}</div>`
))))}
</div>
`;
}
This does require you impose some discipline on how both this.item.snippets and the underlying matches get updated. You have to ensure that the "Array" references being used change when there is an update. Something like this (assuming the matches are being updated with a new match) and sindex is the index of the snippet you want to update and mindex is the index of the match within that snippet you are updating with newMatch;
this.items.snippets = this.items.snippets.map((snippet, index) => index === sindex ?
{...snippet, matches: snippet.matches.map((match, index) => index === mindex ?
newMatch : match)} : snippet);

Render Object using lit.html

Scenario :
Inside my webComponent - health-check.js file I have:
static get properties() {
return {
plan:Object,
};
}
<div class="body"><p class="title">${this.plan.title}</p></div>
and
Inside my index.html file I pass json like below:
<health-check plan='{ "title" : "Solution Architecure",
"status" : "Approved" }'></health-check>
but it doesn't render title inside my health-check component
Problem :
Render Object Value with using lit.html?
Render title inside my health-check component.
Any ideas?
I think you cannot pass the object from html file because it's just html not lit-element or lit-html and lit-html seems not try to parse value in this case (Update I just found another way to define property see below). But of course you can still pass string and parse it inside element.
In index.html
<health-check planString='{
"title": "Solution Architecure",
"status": "Approved"
}'>
</health-check>
In health-check.js
class HealthCheck extends LitElement {
static get properties () {
return {
planString: String
}
}
render () {
return html`
<div class='body'>
<p class='title'>${JSON.parse(this.planString).title}</p>
</div>
`
}
}
But I would recommend to wrap you code in single entrypoint like my-app element
In index.html
<my-app></my-app>
In app.js
class App extends LitElement {
render () {
return html`
<health-check .plan='${{
title: 'Solution Architecure',
status: 'Approved'
}}'>
</health-check>
`
}
}
In health-check.js
class HealthCheck extends LitElement {
static get properties () {
return {
plan: Object
}
}
constructor () {
super()
this.plan = {}
}
render () {
return html`
<div class='body'>
<p class='title'>${this.plan.title}</p>
</div>
`
}
}
Update
You can define property type to tell lit-element how to serialize and deserialize.
class HealthCheck extends LitElement {
static get properties () {
return {
plan: {
type: {
fromAttribute (value) {
return JSON.parse(value)
}
}
}
}
}
render () {
return html`
<div class='body'>
<p class='title'>${this.plan.title}</p>
</div>
`
}
}
Note: This code written in lit-element 0.6.x and lit-html 0.11.x

Polymer paper-input imperative key-bindings

I'm trying to bind the enter key on a paper-input by specifying:
<paper-input class="flex" key-bindings="[[keys]]"></paper-input>
and the following properties on the host element:
keys: {
type: Object,
value: function() {
return {
'enter': this._addVariant
};
}.bind(this)
}
also tried this version:
keys: {
type: Object,
value: function() {
return {
'enter': '_addVariant'
};
}.bind(this)
}
Both do not work and I can't find any references on google. Does somebody know what I'm missing? Maybe the keyEventTarget?
Maybe this is a different way then you look for. But it works, what is the pressed key is:
<paper-input value="{{vall::input}}" on-keydown="_keyEvets" label="Arıyorum">
</template>
<script>
class MyTest extends Polymer.Element {
static get is() { return 'test-component'; }
static get observers() {return ['checkVall(vall)']}
checkVall(v){
console.log('vall :'+ v);
}
_keyEvets(k) {
console.log('_keyEvets',k.code);
}
}
Here is link to a sample

Does core Polymer 2.0 support two-way binding?

I seem to be having trouble getting two-way binding to work with Polymer 2.0. I'm keeping things minimal, only using Polymer.Element.
I define a parent component:
<dom-module id="example1a-component">
<template>
<xtal-fetch req-url="generated.json" result="{{myFetchResult}}"></xtal-fetch>
<div>fetch result:
<span>{{myFetchResult}}</span>
</div>
</template>
<script>
class Example1a extends Polymer.Element{
static get is(){return 'example1a-component'}
static get properties(){
return {
myFetchResult :{
type: String
}
}
}
}
customElements.define(Example1a.is, Example1a)
</script>
</dom-module>
The fetch class looks like:
class XtalFetch extends Polymer.Element {
static get is() { return 'xtal-fetch'; }
static get properties() {
return {
debounceTimeInMs: {
type: Number
},
reqInfo: {
type: Object,
},
reqInit: {
type: Object
},
reqUrl: {
type: String,
observer: 'loadNewUrl'
},
/**
* The expression for where to place the result.
*/
result: {
type: String,
notify: true,
readOnly: true
},
};
}
loadNewUrl() {
debugger;
}
ready() {
if (this.reqUrl) {
const _this = this;
fetch(this.reqUrl).then(resp => {
resp.text().then(txt => {
_this['_setResult'](txt);
_this['result'] = txt;
_this.notifyPath('result');
});
});
}
}
}
elements.XtalFetch = XtalFetch;
customElements.define(xtal.elements.XtalFetch.is, xtal.elements.XtalFetch);
Note that I am trying everything I can think of:
_this['_setResult'](txt);
_this['result'] = txt;
_this.notifyPath('result');
I see the result of the the fetch going into the result property of the fetch element, not into the parent.
Am I doing something wrong?
Yes, it does. Make sure to call super when you're overriding a method:
ready() {
super.ready(); // <-- important!
...
}
That's enough to make your code work (demo).
This is easy to forget, so the polymer-linter was recently updated to warn users about this.