Select Item in Polymer 2 paper-dropdown-menu with TAB - html

I have a paper-dropdown-menu in Polymer 2, similar to this one:
<paper-dropdown-menu label="Dinosaurs">
<paper-listbox class="dropdown-content" selected="1">
<paper-item>allosaurus</paper-item>
<paper-item>brontosaurus</paper-item>
<paper-item>carcharodontosaurus</paper-item>
<paper-item>diplodocus</paper-item>
</paper-listbox>
</paper-dropdown-menu>
I can select an item of the paper-listbox by pressing the ENTER- Key on my Keyboard. Is there a way to select a paper-item by pressing the Tab key

You could add a key binding to <paper-listbox> via addOwnKeyBinding(). The specified handler must be a method of <paper-listbox>, so you'd have to attach your own method.
<paper-listbox id="listbox></paper-listbox>
// script
connectedCallback() {
super.connectedCallback();
// Attach our own handler for TAB-key
this.$.listbox._onListTabKey = e => {
// Cancel TAB key event
e.preventDefault();
e.stopPropagation();
e.detail.keyboardEvent.preventDefault();
// Select currently focused item
const focusedIndex = this.$.listbox.indexOf(this.$.listbox.focusedItem);
this.$.listbox.select(focusedIndex);
};
this.$.listbox.addOwnKeyBinding('tab', '_onListTabKey');
}
Polymer 1 demo
Polymer 2 demo
Note overriding TAB may be an accessibility issue.

Related

this.shadowRoot.querySelector('#upload') return always null Polymer 2.x

I'm having trouble accessing an element in the following dom-if template to add a custom event listener:
<template is="dom-if" if=[[!uploaded]]>
<vaadin-upload id="upload" target="http://localhost:3000/uploadFile" form-data-name="doc" max-files="1">
<iron-icon slot="drop-label-icon" icon="description"></iron-icon>
<span slot="drop-label">....</span>
</vaadin-upload>
</template>
<template is="dom-if" if="[[uploaded]]">
<pdf-element src=[[....]]></pdf-element>
</template>
I tried to add event listeners for upload-response and upload-success in the connectedCallback this way:
connectedCallback() {
super.connectedCallback();
console.log(this.shadowRoot.querySelector('#upload'))
this.shadowRoot.querySelector('#uploadFile').addEventListener('upload-response', event=>this._uploadFile(event))
this.shadowRoot.querySelector('#uploadFile').addEventListener('upload-success', event=>this._uploadFileSuccessful(event))
}
The uploaded property is false by default. I searched in the Polymer 2.0 docs, and didn't find a way to make it work. I always get null.
I have already used this.shadowRoot.querySelector in dom-repeat template without a problem. How do I make querySelector work with the dom-if?
The <dom-if> contents aren't yet stamped in the connectedCallback even though the if condition is true, so you'd have to wait until the next render-frame with Polymer.RenderStatus.afterNextRender():
Polymer.RenderStatus.afterNextRender(this, () => {
const uploader = this.shadowRoot.querySelector('#upload');
uploader.addEventListener('upload-success', () => { /* ... */ });
});
demo

How to add a "Clear Button" into paper-dropdown-menu?

In a paper-input I have a "Clear Button":
<paper-input
id="myinput" maxlength="10" char-counter label="myinput"
always-float-label required>
<paper-icon-button suffix
onclick="clearInput('InputClientes')"
icon="clear"
class="iconClear"
alt="clear"
title="clear">
</paper-icon-button>
</paper-input>
It is possible to add a similar button (Clear Button) to a paper-dropdown-menu to clear the selection?.
Thanks.
1. If you use a paper-dropdown-button with a paper-listbox inside, you can call its selected property to null to clear the current value.
2. Add a button that will call this method and style it with a position:relative CSS rule to position it at the right place.
3. Listen for the iron-select event to show the [close] button when an item is selected in the list.
<paper-dropdown-menu id=PDM label="Your favourite pastry">
<paper-listbox id=PL class="dropdown-content">
<paper-item>Croissant</paper-item>
<paper-item>Donut</paper-item>
<paper-item>Financier</paper-item>
<paper-item>Madeleine</paper-item>
</paper-listbox>
</paper-dropdown-menu>
<paper-icon-button id=CloseBtn icon="clear" onclick="clearInput()" hidden ></paper-icon-button>
<script>
function clearInput() {
PL.selected = null
CloseBtn.hidden = true
}
PDM.addEventListener( 'iron-select', function ( ev ) {
CloseBtn.hidden = false
} )
</script>

Polymer 1.0: Clickable item in dom-repeat to e.g. iron-page which contains further info (contact list)

I'm setting up a contact list in Polymer 1.0. When the user clicks on a name, there should be a (animated) page opened for further details. All of these data elements are pulled from an external .json file.
Two questions for this approach..:
1) where to begin? How do I wrap, for example, an iron-page or neon-animated-page around my current setup (which is searchable, which is also the -temporary- reason it's a dom-repeat instead of an iron-list):
<template id="resultlist" is="dom-repeat" items="{{data}}" filter="contactFilter">
<paper-item>
<paper-item-body two-line>
<div>{{item.name}}</div>
<div secondary>{{item.number}}</div>
</paper-item-body>
</paper-item>
</template>
2) For quick try-out with binding options I've created an paper-dialog (instead of an page behaviour) which displays further data for the chosen person... On top of that paper-dialog should the chosen name being displayed. But I only get the first name of the array in my .json file. How can I setup the code to display the {{item.name}} of the chosen item?
Ps. I'm aware of the contacts-app from Rob Dodson (https://github.com/robdodson/contacts-app), but I can't figure out how it should be done in Polymer 1.0.
Update 27.10.2015
After Hugo's answer I'm not able to get the solution to work in an dom-module structure.
Sorry for misunderstanding, but I can't figure out where I'm wrong.
Having to following:
phonebook.html, which acts like an index
...
<body unresolved>
<template is="dom-bind" id="application">
<neon-animated-pages selected="[[selected]]" entry-animation="fade-in-animation" exit-animation="fade-out-animation">
<contact-list></contact-list>
<contact-details></contact-details>
</neon-animated-pages>
</template>
<script>
var application = document.querySelector('#application');
application.selected = 0;
document.addEventListener('show-details', function() {
application.selected = 1;
});
document.addEventListener('show-list', function() {
application.selected = 0;
});
</script>
</body>
DOM-module contact-list.html, the list it self.
<dom-module id="contact-list">
<template>
<style include="phonebook-styles"></style>
<iron-ajax url="../data/data.json" handle-as="json" last-response="{{data}}" auto></iron-ajax>
<div class="container">
<h3>Contactlist:</h3>
<div class="template-container">
<template is="dom-repeat" id="templateUsers" items="{{data}}">
<paper-item on-tap="showDetails">
<paper-item-body two-line>
<div>{{item.name}}</div>
<div secondary>{{item.phonenumber}}</div>
</paper-item-body>
<div class="item-details-link">
<iron-icon icon="account-circle"></iron-icon>
</div>
</paper-item>
</template>
</div>
</div>
</template>
<script>
Polymer({
is: 'contact-list',
properties: {
selectedContact:{
type:Object,
value:function(){
return null;
}
}
},
showDetails: function(ev) {
var data = this.$.templateUsers.itemForElement(ev.target);
//alert(JSON.stringify(data)) // works with data chosen data selection...
this.selectedContact = data;
this.fire('show-details', this.selectedContact);
}
});
</script>
</dom-module>
DOM-module contact-details.html, the details-list.
<dom-module id="contact-details">
<template>
<!-- Do I need to declare the .json in my details module? -->
<iron-ajax url="../data/data.json" handle-as="json" last-response="{{data}}" auto></iron-ajax>
<paper-icon-button icon="arrow-back" on-tap="showList"></paper-icon-button>
<h3>Contact details</h3>
<template is="dom-repeat" items="{{data}}">
<div>{{selectedContact.name}}</div>
</template>
</template>
<script>
Polymer({
is: 'contact-details',
showList: function() {
this.fire('show-list');
}
});
</script>
</dom-module>
Everything, like the transitions, work. The chosen contact is also displayed in an alertbox (commented out in contact-list.html), but isn't forwarded to the contact-details.html page.
There are multiple steps to implement the solution:
Setup the neon animated pages ( one page would be the contact list, the other page would be the details )
Display the list of contacts ( you already have this one )
Add a "selectedContact" property to your element
Add a tap/click handler to the list items element and inside the handler set the selectedContact. You need to get the contact item from the DOM element clicked. ( Check an example here : http://jsbin.com/lofarabare/6/edit )
You can bind the contact details page elements to the selectedContact properties, e.g {{selectedContact.name}}
Inside the handler also Change the neon animated pages selected property to have it display the animation to the other page.
-- Extra feedback
I checked the way you handle events, feedback below:
Give the elements some id so you can add the event listener directly to them (e.g application.$.myContactList.addEventListener('show-detail',function(ev){...})
The way you fire the event from the contact-list is correct, however you are not reading the event data inside the event listener for the 'show-detail' event. The event listener receives the event as argument "ev". You can get the event data using ev.detail
With the event data (the selected contact) you can update your contact details component. Give it some id like 'details' and just update the 'selectedContact' property. **You need to declare the selectedContact in the details component, right now you don't have it there **

paper-dropdown searchable / filterable

Is there any component like the paper-dropdown element with any extra line to search and filter the items of the drop down? In Jquery there are tons of such elements.
It would be really cool if polymer has something like that to or if anyone can give me a hint, how I can achieve that on my own.
Thanks!!
Check out #addyo's <typeahead-country> element.
https://github.com/addyosmani/typeahead-country
You could fork it and change the country list to be whatever elements you need.
All you have to do is inside paper-listbox add a paper-input and then connect its value with a filer that repeats paper-item for dropdown. You will also need to stop propagation of events that happens on paper-input.
HTML
<paper-dropdown-menu label="Fruits">
<paper-listbox class="dropdown-content" attr-for-selected="data-value" selected="{{fruit}}">
<paper-input class="paperdropdownsearch" label="Search" value="{{key}}"
on-tap="_stopEventPropagation"
on-keydown="_stopEventPropagation"
on-keyup="_stopEventPropagation"></paper-input>
<template is="dom-repeat" items="{{allFruits}}" filter="{onMatch(key)}}">
<paper-item data-value="{{item}}">{{item}}</paper-item>
</template>
</paper-listbox>
</paper-dropdown-menu>
SCRIPT
onMatch: function (key) {
if (!key) {
return null;
} else {
try {
key = key.toLowerCase();
} catch (err) {}
return function (item) {
var curr = item.toLowerCase();
if (curr.search(key) >= 0) {
return true;
}
};
}
},
_stopEventPropagation: function(e) {
e.stopPropagation();
},
Demo
http://embed.plnkr.co/ax9gjxonA3rC8K4Xr2LL/

How do I put the selected core-menu item into a custom polymer element?

I'm trying to encapsulate a paper-dropdown in a paper-button. To do this, I made a custom element, paper-dropdown-holder:
<polymer-element name="paper-dropdown-holder" extends="paper-button" relative on-tap="{{toggle}}">
<template>
<shadow></shadow>
<content></content>
</template>
<script>
Polymer({
toggle: function() {
if (!this.dropdown) {
this.dropdown = this.querySelector('paper-dropdown');
}
this.dropdown && this.dropdown.toggle();
}
});
</script>
</polymer-element>
and I'm using it in the page like:
<paper-dropdown-holder raised tabindex="0" class="unpadded">
<paper-dropdown class="dropdown" flex>
<core-menu class="menu" selected="0">
<paper-item>Writing</paper-item>
<paper-item>Blog</paper-item>
<paper-item>Art</paper-item>
</core-menu>
</paper-dropdown>
</paper-dropdown-holder>
My problem is deciphering The documentation to figure out how to automatically put the text of the currently-selected menu item into the paper-dropdown-holder.
My first attempt was to just use a standard paper-dropdown-menu, but I couldn't as easily style that like a paper-button. Is there any way to do this that's not (for lack of a better term) hacky? I'd love if the answer would keep to the Polymer philosophies.
Bonus challenge: How do I set default text like "choose section"?
One of awesome things of Polymer is it's open source... that said you could learn how to implement new element based on already existing elements....
If you have a look at paper-dropdown-menu source you could easily make something like it but with paper-button as a "control".
So
The new element should extend core-dropdown-base not
paper-button.
To make that element logically working you could do that with
some help of paper-dropdown-menu by binding (core-overlay-open,
core-activate, core-select) events to the according handlers.
(the actual binding happens in core-dropdown-base in dropdown
getter which called inside attached event listener.
To put them together:
<polymer-element name="paper-dropdown-holder" extends="core-dropdown-base" relative>
<template>
<div>
<paper-button raised on-tap="{{toggle}}">{{selectedItemLabel || label}}</paper-button>
<content></content>
</div>
</template>
<script>
Polymer('paper-dropdown-holder', {
publish: {
label: 'Select an item',
},
selectedItemLabel: '',
overlayListeners: {
'core-overlay-open': 'openAction',
'core-activate': 'activateAction',
'core-select': 'selectAction'
},
activateAction: function(e) {
this.opened = false;
},
selectAction: function(e) {
var detail = e.detail;
if (detail.isSelected) {
this.selectedItemLabel = detail.item.label || detail.item.textContent;
} else {
this.selectedItemLabel = '';
}
}
});
</script>
</polymer-element>
Demo.