Showing paper-dropdown-menu when selected value in another - polymer

I am trying to show a paper-dropdown-menu only when a specific value is selected in another paper-dropdown-menu.
I am using a property called selectedValue to bind selected value to the if attribute in a dom-if template.
<link rel="import" href="../bower_components/polymer/polymer-element.html">
<link rel="import" href="../bower_components/polymer/lib/elements/dom-if.html">
<link rel="import" href="../bower_components/polymer/lib/elements/dom-repeat.html">
<link rel="import" href="../bower_components/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="../bower_components/paper-listbox/paper-listbox.html">
<link rel="import" href="../bower_components/paper-item/paper-item.html">
<dom-module id="my-element">
<template>
<paper-dropdown-menu label="One" no-animations>
<paper-listbox slot="dropdown-content" class="dropdown-content" selected="{{selectedValue}}">
<template is="dom-repeat" items="[[options1]]">
<paper-item>[[item]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
<template is="dom-if" if="[[_view()]]">
<paper-dropdown-menu label="Two" no-animations>
<paper-listbox slot="dropdown-content" class="dropdown-content">
<template is="dom-repeat" items="[[options2]]">
<paper-item>[[item]]</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
</template>
</template>
<script>
/**
* #customElement
* #polymer
*/
class MyElement extends Polymer.Element {
static get is() { return 'my-element'; }
static get properties() {
return {
selectedValue : {
type : String
},
options1 : {
type: Array,
value: [1,2,3,4]
},
options2 : {
type: Array,
value : [5,6,7]
}
};
}
_view() {
return this.selectedValue === "1";
}
}
window.customElements.define(MyElement.is, MyElement);
</script>
</dom-module>
The problem is the second paper-dropdown-menu is never displayed.

The problem is your computed binding has no dependencies, so it's invoked once at initialization. Since selectedValue is initially undefined, _view() returns false, causing the dom-if to hide its contents.
To cause the computed binding to re-evaluate selectedValue, make sure to specify the variable as an argument to the binding:
<template is="dom-if" if="[[_view(selectedValue)]]">...</template>
Also note that <paper-listbox>.selected (to which selectedValue is bound) is a number by default (i.e., the index of the selected item), so this expression always evaluates to false:
selectedValue === "1"
I recommend switching the literal from a string to a number:
selectedValue === 1
So, the _view function should look like this:
_view(selectedValue) {
return selectedValue === 1;
}
demo

Related

Why doesn't my this._setPropertyName function work?

I have the following polymer element :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../shared-styles.html">
<link rel="import" href="../../bower_components/dfw-styles/dfw-styles.html">
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/paper-menu-button/paper-menu-button.html">
<link rel="import" href="../../bower_components/paper-listbox/paper-listbox.html">
<dom-module id="baseline-policy-create">
<template>
<style include="dfw-styles">
:host {
display: block;
}
.top-button{
float : right;
}
</style>
<div class="outer-buttons">
<paper-menu-button horizontal-align="right" no-overlap="true" no-animations class="top-button">
<paper-button slot="dropdown-trigger" class="dropdown-trigger create-button btn-primary">
<iron-icon icon="menu"></iron-icon>
<span>Create Baseline Policy</span>
</paper-button>
<paper-listbox slot="dropdown-content" class="dropdown-content">
<template is="dom-repeat" items="{{_domains}}">
<paper-item on-tap="getDomainSchema">[[item.label]]</paper-item>
</template>
</paper-listbox>
</paper-menu-button>
</div>
</template>
<script>
class BaselinePolicyCreate extends Polymer.Element {
static get is() {
return 'baseline-policy-create';
}
static get properties() {
return {
_domains: {
type: Array,
value: [{'name':'Package', 'label':'Package'},
{'name':'Subscription', 'label':'Subscription'}] //TODO: is it possible to get these values from an API source
},
requestedDomain: {
type: String,
value : "",
notify : true,
readOnly : true
}
}
}
getDomainSchema(evt) {
console.info("Get the schema for the following domain:", evt.target.innerText);
console.log(this.requestedDomain);
this._setRequestedDomain(evt.target.innerText);
//this.requestedDomain = evt.target.innerText;
console.log(this.requestedDomain);
}
}
customElements.define(BaselinePolicyCreate.is, BaselinePolicyCreate);
</script>
</dom-module>
I've been following this tutorial on data binding : https://www.tutorialspoint.com/polymer/polymer_data_system.htm
In the example, the code for prop-element has a property, myProp, with the attribute readOnly set to true. In the Onclick function, its still able to change the value of the property using this._setMyProp() which isn't defined anywhere explicitly.
I want to do the same in my code. That is, I want to set requestedDomain using this method. I can set it using this line :
this.requestedDomain = evt.target.innerText;
But to do so, I can't set the readOnly flag to true. If I use this._setRequestedDomain, I get an error saying it is not a function. Am I missing some import at the top to allow this to work, or maybe its been removed in Polymer 2?
I have been testing your code and it's ok.
You only will get the error "this.setRequestedDomain is not a function" if your property is set as readOnly = false.
Please, try again and tell me if its ok.
pen code example (without styling)

How to handle vaadin-grid events in custom polymer 2 element?

I need to include a vaadin-grid in a custom Polymer 2 element and need to handle the row selection event, but I can't seem to get it to work. I've managed to cobble together this from the various starter templates and demos on offer, and the basic click event handler works, but I have no idea how to handle the row selection. I'm actually using v3.0.0-beta1 of the vaadin-grid because I couldn't get v2 to work, but I don't think that's my problem.
Does anyone know how to handle events in vaadin components when you include them in your own custom elements?
Thanks.
<link rel="import" href="bower_components/polymer/polymer-element.html">
<link rel="import" href="bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="bower_components/vaadin-grid/vaadin-grid.html">
<dom-module id="my-element">
<template>
<style>
:host {
display: block;
}
</style>
<iron-ajax auto url="https://demo.vaadin.com/demo-data/1.0/people?count=200" handle-as="json" last-response="{{users}}"></iron-ajax>
<vaadin-grid aria-label="My Test Grid" items="[[users.result]]" id="grid">
<vaadin-grid-column width="50px" flex-grow="0">
<template class="header">#</template>
<template>[[index]]</template>
</vaadin-grid-column>
<vaadin-grid-column>
<template class="header">First Name</template>
<template>[[item.firstName]]</template>
</vaadin-grid-column>
<vaadin-grid-column>
<template class="header">Last Name</template>
<template>[[item.lastName]]</template>
</vaadin-grid-column>
<vaadin-grid-column width="150px">
<template class="header">Address</template>
<template>
<p style="white-space: normal">[[item.address.street]], [[item.address.city]]</p>
</template>
</vaadin-grid-column>
</vaadin-grid>
</template>
<script>
class MyElement extends Polymer.Element {
static get is() { return 'my-element'; }
ready() {
super.ready();
this.$.grid.addEventListener('click', e => {
this._handleClick(e)
});
// I added this listener code from here: https://vaadin.com/elements/-/element/vaadin-grid#demos
// but it does nothing. I've also tried adding it to this, this.$, this.$.grid without success.
// Should this event listener be added here, or if not, where exactly? The docs are very unclear.
addEventListener('WebComponentsReady', function() {
Polymer({
is: 'my-element',
properties: {
activeItem: {
observer: '_activeItemChanged'
}
},
_activeItemChanged: function(item) {
this.$.grid.selectedItems = item ? [item] : [];
console.info('row clicked');
}
});
// this works and outputs info like this: "vaadin-grid-cell-content-17 was clicked."
_handleClick(e) {
console.info(e.target.id + ' was clicked.');
}
}
window.customElements.define(MyElement.is, MyElement);
</script>
</dom-module>
I think that my code in the addEventListener is Polymer 1.x syntax but I'm not sure how to achieve the same result using Polymer 2.x.
I used only vaadin-grid v2, but I downloaded the v3.0.0-beta1 and took a look inside the package, at demo/row-details.html
There is an example on how to handle the active row change event.
<vaadin-grid on-active-item-changed="_onActiveItemChanged" id="grid" aria-label="Expanded Items Example" data-provider="[[dataProvider]]" size="200">
<template class="row-details">
<div class="details">
<img src="[[item.picture.large]]"></img>
<p>Hi! My name is [[item.name.first]]!</p>
</div>
</template>
</vaadin-grid>
You can then define a function called _onActiveItemChanged inside your polymer element to handle the active item changed event.

Dynamically added items do not trigger observers

I'm adding some items to an Array, using the Array mutation methods. The items are displayed within a <dom-repeat> and can can be edited on the fly. While the edits do change the data in the object, any attached observers do not fire to indicate that a change occurred.
tl;dr
I'm properly using the Array mutation methods to push items
this.push("data.contents", {
id: 1,
name: "Modifying this text doesn't trigger an observer"
});
I'm displaying the items in a dom-repeat
These items are displayed using a <dom-repeat>, and the sub-property name is displayed in a <paper-input> where they can be amended on the fly.
<template>
<template is="dom-repeat" items="[[data.contents]]">
<paper-input value="{{item.name::input}}"></paper-input>
</template>
</template>
It seems that while the data is modified in the object itself, any attached observers do not fire for these sub-properties.
I'm attaching wildcard observers
I'm observing using the usual wildcard observers like so:
observers: [
"logChange(data.*)"
],
Notes
Note that:
Changing the item via a direct this.set() like so:
this.set("data.contents.0.name", "Foo")
will trigger the observers just fine
An MCVE for the above.
How to use:
Press the button to push some items to the Array
Edit any one of the added items
Console should log that a change occurred in any of the items (it doesn't)
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
<link href="paper-input/paper-input.html" rel="import">
<link href="paper-button/paper-button.html" rel="import">
<dom-module id="x-example">
<template>
<paper-button on-tap="pushItem">Push to Array</paper-button>
<template is="dom-repeat" items="[[data.contents]]">
<paper-input value="{{item.name::input}}"></paper-input>
</template>
</template>
<script>
HTMLImports.whenReady(function() {
"use strict";
Polymer({
is: "x-example",
properties: {
data: {
type: Object,
value: {
contents: []
}
}
},
observers: [
"logChange(data.*)"
],
pushItem: function() {
this.push("data.contents", {
id: 1,
name: "Modifying this text doesn't trigger an observer"
})
},
logChange: function() {
console.log("change occured!");
}
});
});
</script>
</dom-module>
<x-example></x-example>
Solved here: https://github.com/Polymer/polymer/issues/4140#issuecomment-259465035
The observer isn't firing because you have one-way binding annotations on the items property, which prevents change notifications from flowing up to the element. Change it to: items="{{data.contents}}" and you'll see the changes.
In essence, turn this:
<template is="dom-repeat" items="[[data.contents]]">
<paper-input value="{{item.name::input}}"></paper-input>
</template>
to this
<template is="dom-repeat" items="{{data.contents}}">
<paper-input value="{{item.name::input}}"></paper-input>
</template>

Subset hyphenated JSON element in Polymer data binding

Consider the following JSON:
{"EMD-4091":["EMD-4084","EMD-4090"]}
which is the result of a fictitious iron-ajax call as follows:
<iron-ajax
auto
url="http://me.com/get/EMD-4091"
handle-as="json"
last-response="{{my_data}}">
</iron-ajax>
Suppose I need to refer to the inner array, say, in a dom-repeat: how would I refer to 'EMD-4091' in a data binding? e.g.
<template is="dom-repeat" items="{{my_data????}}> <!-- what should this be?-->
<p>{{item}}</p>
</template>
If the data wasn't hyphenated this is a trivial task. The hyphen is the challenge I'm facing.
P
The data binding can still parse the hyphenated key without a problem, so your binding would be:
items="{{my_data.EMD-4091}}"
HTMLImports.whenReady(() => {
"use strict";
Polymer({
is: 'x-foo',
properties : {
my_data: {
type: Array,
value: () => ({"EMD-4091":["EMD-4084","EMD-4090"]})
}
}
});
});
<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>
<template is="dom-repeat" items="[[my_data.EMD-4091]]">
<div>[[item]]</div>
</template>
</template>
</dom-module>
</body>
codepen

Polymer 1.0 services issue

I'm working on a reddit client using polymer to check out web compoments technologies. I started with the 0.5 version and got back on this project recently. That when I found out that polymer had the 1.0 released so I started over (as it wasn't that advanced anyway).
I have a service that use the iron-ajax to request reddit api and look for the posts. Here is the code :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="reddit-list-service">
<template>
<iron-ajax
url='https://www.reddit.com/new.json'
handle-as='json'
debounce-duration="300"
on-response='handleResponse'
debounce-duration="300"
auto>
</iron-ajax>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'reddit-list-service',
properties: {
modhash: {
type: String,
value: function() {
return '';
}
},
posts: {
type: Array,
value: function () {
return [];
}
},
after: {
type: String,
value: function () {
return '';
}
}
},
// Update object properties from the ajax call response
handleResponse: function (resp) {
this.properties.modash = resp.detail.response.data.modhash;
this.properties.posts = resp.detail.response.data.children;
this.properties.after = resp.detail.response.data.after;
this.post = this.properties.posts; // just to try
console.log(this.properties.posts);
}
});
})();
</script>
My log shows me that I get posts from the API and that's great!
Here's the issue when I want to use this service to make a list out of the posts array I can't figure out how to get them into my list compoment which is below :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../reddit-list-service/reddit-list-service.html">
<dom-module id="reddit-post-list">
<template>
<reddit-list-service posts="{{posts}}">
</reddit-list-service>
<template is="dom-repeat" id="post-list" posts="{{posts}}">
<p>{{post.author}}</p>
<template>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'reddit-post-list',
properties: {
},
});
})();
</script>
I've tried several think I saw in the documentation but I can't figure out what's wrong the author property doesn't show up.
Any clue?
You have a few things that aren't quite right here. In reddit-post-list you are not using the dom-repeat template correctly. See below:
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="reddit-list-service.html">
<dom-module id="reddit-post-list">
<template>
<reddit-list-service posts="{{posts}}"></reddit-list-service>
<template is="dom-repeat" items="[[posts]]">
<p>{{item.data.author}}</p>
</template>
</template>
</dom-module>
<script>
Polymer({
is: "reddit-post-list"
});
</script>
You need an attribute called items which is the array the dom-repeat will iterate over. Inside the iteration you need to refer to item as this is the array item for the current iteration. Here are the docs.
For you reddit-list-service, you need to set the the reflectToAttribute and notify attributes to true on your posts property. This means that any changes to this property are reflected back on the posts attribute on the reddit-list-service element. See here for more information.
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/iron-ajax/iron-ajax.html">
<dom-module id="reddit-list-service">
<template>
<iron-ajax auto url="https://www.reddit.com/new.json" handle-as="json" on-response="handleResponse"></iron-ajax>
</template>
</dom-module>
<script>
Polymer({
is: "reddit-list-service",
properties: {
modhash: {
type: String,
value: ""
},
posts: {
type: Array,
value: function () {
return [];
},
reflectToAttribute: true, // note these two new attributes
notify: true
},
after: {
type: String,
value: ""
}
},
// Update object properties from the ajax call response
handleResponse: function (resp) {
this.modash = resp.detail.response.data.modhash;
this.posts = resp.detail.response.data.children;
this.after = resp.detail.response.data.after;
}
});
</script>
I have also tidied up the following things:
Removed the immediately called function wrapper from the <script> tags as these are not needed.
When referring to properties in your element you only need to use this.PROPERTYNAME rather than this.properties.PROPERTYNAME.
Having looked at the JSON returned, it appears that the author property is on the another property called data.
When you declare a value for a property in your element, you only need to have a function that returns a value if the property type is an Object or Array.