Polymer - Select DOM Element from if template - polymer

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

Related

Multiple elements which can be called via one element

I have a set of Polymer 1.x components which act as fillable forms. Each form can be called via <form-one></form-one>, <form-two></form-two> etc...
<dom-module id="form-one">
<template>
form content
</template>
</dom-module>
<script>
Polymer({
is: "form-one"
});
</script>
I'm looking for a solution to combine them and to have only one element and call the forms like:
<form-handler form="form-one"></form-handler>
Where to start? And is it also possible to keep the dom modules in separated files?
Thanks in advance.
You can have your element like this:
<dom-module id="form-handler">
<template>
<div id="formContainer">
</template>
</dom-module>
<script>
Polymer({
is: "form-handler",
properties:{
form: {type:String, observer:'_formChanged'}
},
_formChanged: function(){
var formElement = document.createElement(this.form);
this.$.formContainer.empty();
this.$.formContainer.append(formElement);
}
});
</script>

On attached, `this.$` an empty object

I'm trying to observe added and removed dom nodes to my custom Polymer element without success. I've noticed that the this.$ is an empty object.
Polymer({
is: 'dramatic-view',
attached: function () {
this._observer = Polymer.dom(this.$.contentNode).observeNodes(function (info) {
console.log('kromid info', info);
});
}
});
The callback is being called only once (even tho I change content afterwards) with the following strange parameters:
I was following the docs here.
this.$ is a hash of elements in your template that have an id attribute (see Automatic node finding).
So, for this.$.contentNode to exist, you need an element with id="contentNode" in your <dom-module>'s <template>, and it must not be inside a dom-if or dom-repeat template:
<dom-module id="x-foo">
<template>
<content id="contentNode"></content>
</template>
</dom-module>
Nodes created dynamically (i.e., those inside dom-if or dom-repeat) must be queried with this.$$() (e.g., this.$$('#contentNode')). If you're trying to set them up in the attached callback, you'll need to wait until after the nodes are stamped, which could be observed with Polymer.RenderStatus.afterNextRender().
<dom-module id="x-foo">
<template>
<template is="dom-if" if="[[useDynamicContent]]">
<content id="contentNode"></content> <!-- unavailable to this.$ -->
</template>
</template>
<script>
Polymer({
is: 'x-foo',
attached: function() {
Polymer.RenderStatus.afterNextRender(this, function() {
var contentNode = this.$$('#contentNode');
if (contentNode) {
contentNode.observeNodes(...);
}
});
}
});
</script>
</dom-module>
To trigger the node observer, use Polymer's DOM API like this:
Polymer.dom(this).appendChild(node);
Polymer.dom(this).removeChild(node);
codepen
UPDATE It seems you're integrating Polymer with Elm, and expecting to see the observer callback for node-changes made my Elm. You'll need to hook Elm's calls to appendChild/removeChild somehow so that it uses Polymer's DOM API.

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 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>

Can I select any element in the document using Polymer's Automatic Node Finding?

I am working on a Polymer project and I a few custom elements nested. For example, I have a custom element called <example-main>. Suppose that this element has a function called displayMessage()
I have another custom element defined inside <example-main> called <example-alerts>. I want the alerts element to be able to use the function in its parent, main, to display a message.
To achieve this, I am trying to select the element <example-main id='mainEl'> by using the Polymer's Automatic Node Finding hash, this.$.mainEl and using the function like so: this.$.mainEl.displayMessage();
However, I get this error message:
Uncaught TypeError: Cannot read property 'displayMessage' of undefined
Am I making a wrong approach of having Polymer elements call functions in other Polymer objects?
To visualize things a little better, the index.html would look something like:
...
<body>
<example-main id='mainEl'></example-main>
</body>
...
while example-main.html looks something like:
<polymer-element name='example-main'>
<template>
...
<example-alerts>
</template>
<script>
Polymer(example-element, {
displayMessage: function(){
//do message showing magic
}
});
</script>
</polymer-element>
and lastly, example-alerts.html looking like:
<polymer-element name='example-alerts'>
<template>
...
<paper-button on-tap='{{callFunction}}'></button>
</template>
<script>
Polymer(example-alerts, {
callFunction: function(){
//call the function in parent
this.$.mainEl.displayMessage();
}
});
</script>
</polymer-element>
You are not doing this correct way , you cannot find nodes this way .
Node finding just search within same element defined in elements outermost template.
You should either change your approach or you can use events to handle the situation pls see the sample demo which can work for you.
<polymer-element name='example-alerts'>
<template>
...
<paper-button on-tap='{{callFunction}}'></button>
</template>
<script>
Polymer(example-alerts, {
callFunction: function(){
//call the function in parent
this.fire('ouch', {msg: 'That hurt!'});
}
});
</script>
</polymer-element>
--Second Element
<polymer-element name='example-main'>
<template>
...
<example-alerts on-ouch='{{displayMessage}}'>
</template>
<script>
Polymer(example-element, {
displayMessage: function(){
//do message showing magic
}
});
</script>
</polymer-element>