Polymer deep path binding - polymer

In Polymer how do I databind to a Deep path property like shown in the example below
<!doctype html>
<html>
<head>
<script src='bower_components/webcomponentsjs/webcomponents.min.js'></script>
<link rel='import' href='bower_components/polymer/polymer.html'/>
</head>
<body>
<dom-module id='base-page'>
<template>
<input type='button' on-click='click'>
<div>{{item.mydeepproperty}}</div>
</template>
</dom-module>
<script>
Polymer({
is: 'base-page',
properties: {
item: {
type: Object,
value: function() { return { mydeepproperty: 'default' } }
}
},
click: function() {
this.item.mydeepproperty = 'woohoo';
}
});
</script>
<base-page></base-page>
</body>
Example also found here:
http://jsbin.com/qonedeleho/1/edit?html,output
Cheers

When updating a sub-property of an object, you need to be more explicit in your code to force a path change notification and either use the this.set() or this.notify() function:
click: function() {
this.set('item.mydeepproperty', 'woohoo');
}

Related

Multiple polymer 2 elements with independent properties?

I have a question to my custom polymer element. First, I made an element with a simple paper-input. My problem is, that I don't know, how to use this element as an "independent" element. My example is here in this jsfiddle. Type in the first input "asd" and hit Enter and then in the second input "asd" and hit Enter. You can see, that both elements are sharing the properties (console log "-1" not found in array and second log will be "1")
<!doctype html>
<html>
<head>
<title>2.0 preview elements</title>
<base href="http://polygit.org/polymer+v2.0.0-rc.4/webcomponentsjs+webcomponents+v1.0.0-rc.6/shadycss+webcomponents+1.0.0-rc.2/paper*+polymerelements+:2.0-preview/iron*+polymerelements+:2.0-preview/app*+polymerelements+:2.0-preview/neon*+polymerelements+:2.0-preview/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="paper-input/paper-input.html">
</head>
<body>
<input-test></input-test>
<input-test></input-test>
<dom-module id="input-test">
<template>
<paper-input value="{{aValue}}" on-keydown="_keydown"></paper-input>
</template>
<script>
window.addEventListener('WebComponentsReady', function() {
class InputTest extends Polymer.Element {
static get is() {
return 'input-test';
}
static get properties() {
return {
aValue: {
type: String,
value: ''
},
_inputStore: {
type: Array,
value: []
}
};
}
_keydown(event) {
const keyCode_enter = '13';
if (event.keyCode == keyCode_enter) {
console.log(this._inputStore.indexOf(this.aValue))
this.push('_inputStore', this.aValue);
}
}
}
customElements.define(InputTest.is, InputTest);
})
</script>
</dom-module>
</body>
</html>
What can I do, to have independent properties?
Thanks!
I found the answer.
The problem is the default value declaration of the array.
_inputStore: {
type: Array,
value: function () {
return [];
}
}
This code solves the problem.

Polymer sending/receiving events among sibling elements

Assume I have two sibling elements, and <element-b> fires an event. How does <element-a> listen to the event without having to add imperative code to the parent?
<dom-module id="parent-element">
<element-a></element-a>
<element-b></element-b>
</dom-module>
where <element-a> and <element-b> are:
<dom-module id="element-a">
<template>
<style include="shared-styles">
</style>
</template>
<script>
Polymer({
is: 'element-a',
listeners: {
'element-b': 'handleEvent',
},
ready: function() {
console.log('fired from element a')
this.fire('element-a', {employee: ''});
},
handleEvent: function (e) {
console.log('received element b event', e)
}
});
</script>
</dom-module>
<dom-module id="element-b">
<template>
<style include="shared-styles">
</style>
</template>
<script>
Polymer({
is: 'element-b',
listeners: {
'element-a': 'handleEvent',
},
ready: function() {
console.log('fired from element b')
this.fire('element-b', {employee: ''});
},
handleEvent: function (e) {
console.log('received element a event', e)
}
});
</script>
Thanks!
You could use <iron-signals> for that.
Add an <iron-signals> listener in one element:
// element-b
<iron-signals on-iron-signal-element-a="_onSignalElementA"></iron-signals>
_onSignalElementA: function(e) {
const newDate = e.detail;
...
}
...and fire an iron-signal event (with data) in the other:
// element-a
this.fire('iron-signal', {name: 'element-a', data: new Date()});
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo'
});
Polymer({
is: 'element-a',
_onClick: function() {
this.fire('iron-signal', {name: 'element-a', data: new Date()});
}
});
Polymer({
is: 'element-b',
_onSignalElementA: function(e) {
this._message = `b received: ${e.detail}`;
}
});
});
<head>
<base href="https://polygit.org/polymer+1.11.0/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="iron-signals/iron-signals.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<element-a></element-a>
<element-b></element-b>
</template>
</dom-module>
<dom-module id="element-a">
<template>
<button on-tap="_onClick">Fire event</button>
</template>
</dom-module>
<dom-module id="element-b">
<template>
<iron-signals on-iron-signal-element-a="_onSignalElementA"></iron-signals>
<div>[[_message]]</div>
</template>
</dom-module>
</body>
codepen

Access current item when clicking link in dom-if inside dom-repeat

I have the following template:
<template is="dom-repeat" items="{{myItems}}">
<template is="dom-if" if="{{_shouldHaveLink(item)}}">
Link
</template>
</template>
Now, if the link was not wrapped in a dom-if, I can see the item which was pressed with:
_linkTapped: function (oEvent) {
console.log('Item link tapped:', oEvent.model.get('item'));
}
But inside the dom-if I can't. Seems like item is now out of scope. How can I get it?
This is a known bug with Polymer's dom-repeat yet to be solved, but there's a simple workaround in this scenario.
Since the dom-if template without restamp simply hides its contents when the if condition is false (as an optimization), you could simulate the original behavior while avoiding the dom-if-related bug by replacing the template with a hidden attribute based on the same condition negated:
<div hidden$="{{!_shouldHaveLink(item)}}">
Link
</div>
or:
Link
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: () => [
{ name: 'google', link: 'http://www.google.com' },
{ name: 'facebook' },
{ name: 'twitter', link: 'http://www.twitter.com' },
]
}
},
_hasNoLink: function(item) {
return !item.link;
},
_linkTapped: function(e) {
console.log(e.model.item);
// for demo only...
e.preventDefault();
}
});
});
<head>
<base href="https://polygit.org/polymer+1.7.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<div>Facebook anchor is hidden because it has no link</div>
<template is="dom-repeat" items="[[items]]">
<a href="#"
hidden$="{{_hasNoLink(item)}}"
on-tap="_linkTapped">[[item.name]]</a>
</template>
</template>
</dom-module>
</body>
codepen
And as #DocDude suggested, another alternative is to use <dom-repeat>.modelForElement(e.target) if you have a reference to the <dom-repeat>:
//template
<template id="repeater" is="dom-repeat" items="[[items]]">
// script
_linkTapped: function(e) {
const m = this.$.repeater.modelForElement(e.target);
console.log(m.item);
...
}
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: () => [
{ name: 'google', link: 'http://www.google.com' },
{ name: 'facebook' },
{ name: 'twitter', link: 'http://www.twitter.com' },
]
}
},
_hasLink: function(item) {
return item.link;
},
_linkTapped: function(e) {
const m = this.$.repeater.modelForElement(e.target);
console.log(m.item);
// for demo only...
e.preventDefault();
}
});
});
<head>
<base href="https://polygit.org/polymer+1.3.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo></x-foo>
<dom-module id="x-foo">
<template>
<div>Facebook anchor is hidden because it has no link</div>
<template id="repeater" is="dom-repeat" items="[[items]]">
<template is="dom-if" if="{{_hasLink(item)}}">
[[item.name]]
</template>
</template>
</template>
</dom-module>
</body>
codepen

Polymer. Update view after model changed

This is a common question in Polymer about updating view when model was updated (answer is to use this.set or this.push Polymer methods).
But when I have two elements:
First element:
properties:{
items: {
type: Array
}
},
someFunction: {
this.push('items', {'Name': 'something'});
}
Second Element has property which is bound to 'items' from first element
ready: function(){
this.items = firstElement.items;
}
I would like second element 'items' to be updated when 'items' updated on firstElement. I can't manually notify secondElement, because of app restrictions.
So for now I see (in devTools) that model of second element is correct ('items' contains objects), but view isn't updated.
How to update it?
You need to set notity on the property of the first element to receive changes outside of the element itself
properties:{
items: {
type: Array,
notify: true
}
},
then, in secondElement you can
<firstElement items="{{items}}" />
Let Polymer's two-way binding handle the notifications for you.
Consider two elements, x-foo and x-bar, in a container element, x-app.
In x-foo, declare items with notify:true so that its changes would be propagated upward.
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: function() { return []; },
notify: true
}
}
});
In x-app, bind x-foo's items to x-bar's.
<dom-module id="x-app">
<template>
<x-foo items="{{items}}"></x-foo>
<x-bar items="[[items]]"></x-bar>
</template>
<script>
Polymer({ is: 'x-app' });
</script>
</dom-module>
Now, x-bar would be notified of all changes to the items array (when an item is added/removed).
HTMLImports.whenReady(() => {
"use strict";
Polymer({
is: 'x-app'
});
Polymer({
is: 'x-foo',
properties: {
items: {
type: Array,
value: function() { return []; },
notify: true
}
},
_addItem: function() {
this.push('items', {name: 'item' + this.items.length});
}
});
Polymer({
is: 'x-bar',
properties: {
items: {
type: Array
}
}
});
});
<head>
<base href="https://polygit.org/polymer+1.6.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-app></x-app>
<dom-module id="x-app">
<template>
<x-foo items="{{items}}"></x-foo>
<x-bar items="[[items]]"></x-bar>
</template>
</dom-module>
<dom-module id="x-foo">
<template>
<h2><code>x-foo</code></h2>
<button on-tap="_addItem">Add item</button>
</template>
</dom-module>
<dom-module id="x-bar">
<template>
<h2><code>x-bar</code></h2>
<ul>
<template is="dom-repeat" items="[[items]]">
<li>[[item.name]]</li>
</template>
</ul>
</template>
</dom-module>
</body>
codepen

Polymer, how to get url params in page.js into polymer element?

I want to make a single page application. When I go to path like localhost/person-info/101, how can I get the URL params using page.js into a Polymer element?
I have to use these params to select or change data in the person-info element before using neon-animated-page to change page
Contents of app.js:
window.addEventListener('WebComponentsReady', function () {
var app = document.querySelector('#app');
page('/', home);
page('/person-info/:user', showPersonInfo);
page({
hashbang: true
});
function home() {
app.route = 'index';
console.log("app route" + app.route);
console.log("home");
}
function showPersonInfo(data) {
console.log(data);
console.log(data.params.user);
app.params = data.params;
app.route = 'person-info';
}
});
And my Polymer element person-info.html
<link rel="import" href="bower_components/iron-list/iron-list.html">
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/paper-toolbar/paper-toolbar.html">
<link rel="import" href="bower_components/paper-icon-button/paper-icon-button.html">
<script type="text/javascript" src="bower_components/jquery-2.1.4.min/index.js"></script>
<script src="js/app.js"></script>
<dom-module id="person-info">
<style>
:host {
display: block;
font-family: sans-serif;
#apply(--layout-fit);
#apply(--layout-vertical);
}
.toolbar {
background: #3f78e3;
}
a {
color:#FFF;
}
</style>
<template id="person-info">
<paper-toolbar class="toolbar">
<a href="/">
<paper-icon-button icon="icons:arrow-back"></paper-icon-button>
</a>
<div>test</div>
<div>test</div>
</paper-toolbar>
<content></content>
</template>
<script>
Polymer({
is: 'person-info',
ready: function () {
console.log("person-info page");
this.testdd = "adisak";
console.log("user ---->"+this.params);
console.log(this);
},
properties: {
dataindex: String
}
});
</script>
</dom-module>
Variable app is global, so if you bind a data to app.params from your route than you can access it everywhere using app.params. Or just access your element and set the properties on your element like below
function showPersonInfo(data){
var personInfo=document.querySelector('person-info');
personInfo.user=data.params;
app.route = 'person-info';
}
From your person-info element you can access by using this.user but i think you need to set the property user on element properties.