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

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.

Related

how to pass data from host element to child element

Having trouble getting my data to work across elements. Say I have an object "records" in my host element. It is usable in that element no problem. But when I try to spin off the view into a separate element, it no longer works.
Here's an example snippet of code. The .template part works fine but I'm not able to replicate the functionality in the child element .my-list. When I try, nothing happens.
<dom-module id="host-element">
...
<template is="dom-repeat" items="{{records}}">
<p>{{item.userName}} - {{item.number}}</p>
</template>
<my-list></my-list>
<script>
Polymer({
is: 'host-element',
ready: function() {
this.records = [
{userName: 'Bob'},
{userName: 'Sally'}
];
}
});
</script>
</dom-module>
If I try simply taking the current .template code and placing it into .my-list, it doesn't work.
I assume I need someway to bind the data into the child element, but I'm not able to figure this out. Adding a: record="{{records}}" to the tag, and then using that in the child element didn't work.
Imagine this is pretty simple, just can't find the answer in the documentation.
It's important that each element's top-level template is a plain template (not is="dom-repeat" or other specialization), otherwise, it should be straightforward:
<link rel="import" href="//polygit.org/components/polymer/polymer.html">
<i>(Requires Chrome)</i>
<host-element></host-element>
<dom-module id="my-list">
<template>
<template is="dom-repeat" items="{{records}}">
<p>{{item.userName}} - {{item.number}}</p>
</template>
</template>
<script>
Polymer({
is: 'my-list'
});
</script>
</dom-module>
<dom-module id="host-element">
<template>
<my-list records="{{records}}"></my-list>
</template>
<script>
Polymer({
is: 'host-element',
ready: function() {
this.records = [{userName: 'Bob'}, {userName: 'Sally'}];
}
});
</script>
</dom-module>

Polymer - Select DOM Element from if template

I'm learning Polymer. I have a view that is setup like this:
my-view.html
<dom-module id="my-view">
<template>
<template is="dom-if" if="[[ areAvailable ]]">
<my-component id="myComponent"></my-component>
<button type="button" on-click="onButtonClick">Test</button>
</template>
<template is="dom-if" if="[[ !areAvailable ]]">
<div>Move along</div>
<template>
</template>
<script>
Polymer({
is:'my-view',
properties: {
areAvailable: Boolean
},
onButtonClick: function() {
this.$.myComponent.test();
}
});
</script>
</dom-module>
This view shows my-component based on the areAvailable flag. If a user clicks the "Test" button, I need to execute a function in my-component. my-component is setup like this:
my-component.html
<dom-module id="my-component">
<template>
<h1>hello there!</h1>
</template>
<script>
Polymer({
is:'my-component',
test: function() {
alert('voila!');
}
});
</script>
</dom-module>
My challenge is, my approach works IF my-component is not inside of an "if" template. When my-component is inside of an "if" template, I get an error that says:
TypeError: Cannot read property 'test' of undefined
What am I doing wrong?
Note: Nodes created dynamically using data binding (including those in dom-repeat and dom-if templates) are not added to the this.$ hash. The hash includes only statically created local DOM nodes (that is, the nodes defined in the element’s outermost template).
Source: https://www.polymer-project.org/1.0/docs/devguide/local-dom.html#node-finding
Since you have already added a listener to the click event on your button, you can redefine that listener like this:
Polymer({
...
onButtonClick: function() {
// The this.root here refers to the local dom
// It is highly advised that you use Polymer.dom(<node>) when doing dom manipulations
Polymer.dom(this.root).querySelector('#myComponent').test();
}
});
Polymer have this simple function...
this.$$('#myComponent')
https://www.polymer-project.org/1.0/docs/devguide/local-dom#node-finding

Polymer element is unregistered

I've created an element to display the results of an API call, but it not rendering. I've used the 'unregistered element' bookmarklet from the Polymer team which is showing this as unregistered. I'm using this within the Polymer starter kit.
I'm sure its a simple oversight on my behalf that I'm just not seeing.
The element is is listed in the elements.html file and is used in the main index.html file like so.
<section data-route="driver-standings">
<driver-standing></driver-standing>
</section>
The element
<dom-module id="driver-standing">
<template>
<style>
:host {
display: block;
}
</style>
<iron-ajax
auto
url="http://ergast.com/api/f1/current/driverStandings.json"
handle-as="json"
last-response="{{data}}"></iron-ajax>
<template is="dom-repeat" items="{{driverList}}">
<span>[[item.Driver.givenName]]</span> <span>[[item.Driver.familyName]]</span>
<template>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'driver-standing',
properties: {
data: {
},
driverList: {
computed: 'processDrivers(data)'
}
},
processDrivers: function (data){
console.log("processDrivers")
return data.MRData.StandingsTable.StandingsLists[0].DriverStandings;
}
});
})();
</script>
</dom-module>
Any help much appreeciated
I missed the closing / on the template tag.
<template is="dom-repeat" items="{{driverList}}">
<span>[[item.Driver.givenName]]</span> <span>[[item.Driver.familyName]]</span>
<template>
became...
<template is="dom-repeat" items="{{driverList}}">
<span>[[item.Driver.givenName]]</span> <span>[[item.Driver.familyName]]</span>
</template>
Easily missed :)
Your dom-repeat - template should look some kind of this:
You should have your items you want to display in the item tag and as Polymer goes throw your Array it safes each Object under the name defined under the as - tag, so you can access the Object inside of your template binding with the name in curly brackets, like {{driver}}
<template is="dom-repeat"
items="{{driverList}}"
as="driver">
<p>{{driver}}</p>
</template>

Polymer 1.0 Extending Elements - paper-dialog with custom element

I am am trying to create a custom element that plays a youtube video in paper-dialog. So videoPlayer = Polymer.dom(this.root).querySelector('video-player'); inherits/has access to that paper-dialogs open method, I am trying to extend my custom element. It isn't working, but hopefully I am on the right track and someone can show me correctly.
I am using Polymer 1.0, but I only have https://www.polymer-project.org/0.5/docs/polymer/polymer.html#extending-other-elements to go by for extending elements.
<link rel="import" href="../bower_components/paper-dialog/paper-dialog.html">
<link rel="import" href="../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../bower_components/iron-icons/iron-icons.html">
<link rel="import" href="../bower_components/google-youtube/google-youtube.html">
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="video-player">
<template>
<div class="layout horizontal">
<paper-button dialog-dismiss>
<paper-icon-button icon="arrow-back"></paper-icon-button>
</paper-button>
</div>
<div style="height: 100%; width: 100%">
<google-youtube style="height: 100%;"
video-id="YMWd7QnXY8E"
rel="1"
start="5"
playsinline="0"
controls="2"
showinfo="0"
width="100%"
height="100%"
autoplay="1">
</google-youtube>
</div>
</template>
<script>
Polymer({
is: "video-player"
});
</script>
<paper-dialog name="video-player" extends="video-player">
<template>
<shadow></shadow>
</template>
<script>
Polymer();
</script>
</paper-dialog>
<video-player></video-player>
As was mentioned in the comments, you can't yet extend custom elements, so the existing pattern (or at least the one I use) is to make use of behaviors wherever possible and wrappers wherever not.
e.g.
<dom-module id="popup-video-player">
<template>
<video-player></video-player>
</template>
<script>
Polymer({
is: 'popup-video-player',
behaviors: [Polymer.PaperDialogBehavior],
...
});
</script>
</dom-module>
Now you can use <popup-video-player> just like a paper-dialog.
I know it stinks because if video-player has a bunch of properties that you want access to, you have to copy them in the popup-video-player element's API, which is not exactly DRY.
If you look at the paper-input source, you'll see them doing the same thing. It's obvious that they want to extend iron-input, but they can't so you get things like this:
<input is="iron-input" id="input"
aria-labelledby$="[[_ariaLabelledBy]]"
aria-describedby$="[[_ariaDescribedBy]]"
disabled$="[[disabled]]"
title$="[[title]]"
... >
As a side note, you could always hook into the <video-player>s "properties" property and make the API additions programatically.
maybe something like this would work: (untested!)
Polymer({
...
properties: (function () {
var prop = {
//special properties specific to the pop up version of video-player
//..obviously be careful to avoid name space conflicts.
};
var video_player = document.createElement('video-player');
video_player.properties.keys().forEach( function(key) {
props[key] = video_player[key];
});
return props;
}()),
});

bind a polymer template to html5 video.textTracks

I try to create a html5 video timeline where all the tracks and the cues are displayed using polymer.
this is the code I have so far, but the binding does not work. It always displays a length of 0:
<polymer-element name="video-timeline">
<template>
<video id="testVideo" src="./path/to/video.webm" width="300" controls="controls"></video>
<button on-click="{{addTrack}}">Add Track</button>
<template bind="{{$.testVideo.textTracks}}">
<p>Item count: {{length}}</p>
<ul>
<template repeat>
<li>{{kind}}</li>
</template>
</ul>
</template>
</template>
<script>
Polymer('video-timeline', {
addTrack: function() {
var track = this.$.testVideo.addTextTrack('metadata', 'label');
track.mode = 'showing';
},
});
</script>
</polymer-element>
a binding would be useful because editing cues and tracks will folow
I would suggest something like the following:
<script src="//www.polymer-project.org/platform.js"></script>
<link rel="import" href="//www.polymer-project.org/components/polymer/polymer.html">
<polymer-element name="video-timeline" attributes="src">
<template>
<div>
<video id="testVideo" src="{{src}}" width="300" controls></video>
</div>
<button on-tap="{{addTrack}}">Add Track</button>
<p>Item count: {{textTracks.length}}</p>
<ul>
<template repeat="{{textTrack in textTracks}}">
<li>{{textTrack.kind}}</li>
</template>
</ul>
</template>
<script>
Polymer({
textTracks: null,
addTrack: function() {
var track = this.$.testVideo.addTextTrack('metadata', 'label');
track.mode = 'showing';
this.textTracks.push(track);
},
ready: function() {
this.textTracks = [];
}
});
</script>
</polymer-element>
<video-timeline src="http://v2v.cc/~j/theora_testsuite/320x240.ogg"></video-timeline>
One of the problems with your approach is that Polymer's mutation observers can't detect when textTracks are added to $.testVideo. The syntax you were using for your repeated template was also incorrect.
The approach I went with uses an array defined on the Polymer element (this.textTracks) as a sort of proxy, and updates the contents of the array each time you add a new track.
This should work for your stated use case, with the caveat that if your <video>'s tracks get updated outside of the addTrack() handler, the array won't be updated and the rendered <template repeat="{{}}"> will be inaccurate.