Polymer - simple way to access child element properties from parent element - javascript

I have a simple polymer element that looks like this:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<dom-module id="selector-course">
<template>
<style>
paper-dropdown-menu {
padding:5px;
}
</style>
<paper-dropdown-menu label="Course">
<paper-listbox class="dropdown-content" selected="{{selected}}" attr-for-selected="value" id="courseSelect">
<paper-item value="main">Main</paper-item>
<paper-item value="soup">Soup</paper-item>
<paper-item value="dessert">Dessert</paper-item>
<paper-item value="appetizer">Appetizer</paper-item>
</paper-listbox>
</paper-dropdown-menu>
</template>
<script>
Polymer({
is: 'selector-course'
});
</script>
</dom-module>
This element is stored in a separate HTML file and then used in a couple of my other elements like this:
<link rel="import" href="components/selector-course.html">
...
<selector-course selected="{{recipe.course}}"></selector-course>
Now, within my parent elements I need to access the selected value of <selector-course>
Right now, I have a solution that looks like this:
this.shadowRoot.querySelector('selector-course').shadowRoot.querySelector('#courseSelect').selectedItem.getAttribute("value");
This works, however, this query seems absurdly complex for such a trivial task as accessing the property of an element.
Is there a more simple way to achieve the same thing?

Oh no, this is a very bad idea. Accessing internal elements is very brittle, because it requires parent elements to have deep (pun intended) knowledge about the implementation details of <selector-course>. Not to mention that your method will not work with Shady DOM.
In your case you could simply adding a notifying property to your element
Polymer({
is: 'selector-course',
properties: {
selected: {
notify: true
}
}
});
To select it's value outside data binding you can use the method #a1626 mentions
var selected = this.$('selector-course').selected;
Polymer({
is: 'selector-course',
properties: {
selected: {
notify: true
}
}
});
<!DOCTYPE html>
<html>
<head>
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import"/>
<link href="paper-dropdown-menu/paper-dropdown-menu.html" rel="import"/>
<link href="paper-listbox/paper-listbox.html" rel="import"/>
<link href="paper-item/paper-item.html" rel="import"/>
</head>
<body>
<template is="dom-bind">
<selector-course selected="{{selection}}"></selector-course>
<div>{{selection}}</div>
</template>
<dom-module id="selector-course">
<template>
<style>
paper-dropdown-menu {
padding:5px;
}
</style>
<paper-dropdown-menu label="Course">
<paper-listbox class="dropdown-content" selected="{{selected}}" attr-for-selected="value" id="courseSelect">
<paper-item value="main">Main</paper-item>
<paper-item value="soup">Soup</paper-item>
<paper-item value="dessert">Dessert</paper-item>
<paper-item value="appetizer">Appetizer</paper-item>
</paper-listbox>
</paper-dropdown-menu>
</template>
</dom-module>
</body>
</html>

Your parent element seems to be a Polymer element in itself as you have used binding. In that case you can access property using
this.$.<id of child>.<property name>
Or in case your element is inside dom-if or dom-repeat
this.$$(<querySelector>).<property name>
You can check it out here
In your case it'll be
<selector-course selected="{{selected}}" id="mySelector"></selector-course>
In javascript
this.$.selected

Related

Selcting the first <content> element with polymer 1

I am using Polymer 1 for web-development and I am encountering a small lack of understanding. Imagine the following:
<div class="value">
<content></content>
</div>
I can easily style the content with the .value selector. Now I only want to style the First content element. :first-child etc. doesn't seem to work.
How can I select different elements of my <content></content> in Polymer?
Thanks!
Note that <content> has been deprecated for <slot>.
To target the first child of the <slot>/<content>, use ::slotted(:first-child):
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
properties: {
foo: {
type: String,
value: 'Hello world!'
}
}
});
});
<head>
<base href="https://cdn.rawgit.com/download/polymer-cdn/1.8.0/lib/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo>
<div>first</div>
<div>second</div>
<div>third</div>
</x-foo>
<dom-module id="x-foo">
<template>
<style>
::slotted(:first-child) {
color: red;
}
</style>
<slot></slot>
</template>
</dom-module>
</body>

Get reference to dynamically created element

In Polymer you can use this.$.idOfElement to get an element with an id of idOfElement.
How can I get a dynamically created element by its id if I only have the value of id in a variable?
Note: The element I want to reach out to is created dynamically like this:
<template is="dom-repeat" items="[[days]]" as="o">
<my-custom-element id$="[[getId(o)]]"></my-custom-element>
</template>
I have plenty of these <my-custom-element> in my app and need to get hold of them one by one.
Edit:
None of these work, they all return undefined or null:
this.$[id]
this.$$(id)
this.root.querySelector('my-custom-element#' + id)
Polymer.dom(e.currentTarget).querySelector('my-custom-element#' + id)
You cannot use the automatic node finder (this.$.x) for dynamic nodes. You have to use this.$$(selector), which acts like document.querySelector() constrained to children of the current element.
This why you must use this.$$('#' + nodeId) as suggested by Alan.
Polymer({
is: 'my-elem',
makeRed: function() {
this.$$('#li2').style.color = 'red';
}
});
<!DOCTYPE html>
<html>
<head>
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import"/>
</head>
<body>
<my-elem></my-elem>
<dom-module id="my-elem">
<template>
<ul>
<template is="dom-repeat" items='[ 1, 2, 3 ]'>
<li id="li[[item]]">{{item}}</li>
</template>
</ul>
<input type="button" value="make 2 red" on-tap="makeRed" />
</template>
</dom-module>
</body>
</html>

Polymer webcomponents data binding an js object/array

On https://www.polymer-project.org/1.0/ the show the usage with firebase as datasource. However I'd like to use a simple js variable (array/object) as datasource e.g. an array with friends. However I'm not getting the array bound to the polymer.
How can I bind an js array as data bining to an polymer?
Example HTML Page index.html
<!DOCTYPE html>
<html lang="en">
<head>
<script src="/bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="friends-list.html">
</head>
<body>
<h1>Hello Data Object Arry Binding</h1>
<script>
var friends = [
{"name":"manual"},
{"name":"john"},
{"name":"doe","starred":true}
];
</script>
<friend-list data="friends"></friend-list>
</body>
</html
with expecting friend-list.html polymar may be something like this:
<dom-module id="friend-list">
<link rel="import" type="css" href="friend-list.css">
<template>
<template is="dom-repeat" items="{{data}}">
<contact-card starred="{{item.starred}}">
<img src="{{item.img}}">
<span>{{item.name}}</span>
</contact-card>
</template>
</template>
<script>
Polymer({
is: 'friend-list',
properties: {
data: Object
}
});
</script>
</dom-module>
You have to use dom-bind template if you want to use data binding outside of an element declaration.
<template is="dom-bind" id="app">
<h1>Hello Data Object Arry Binding</h1>
<friend-list data="{{friends}}"></friend-list>
</template>
<script type="text/javascript">
var app = document.getElementById('app');
app.friends = [
{"name":"manual"},
{"name":"john"},
{"name":"doe","starred":true}
];
</script>

Make 'dropdown-content' same width as paper-dropdown-menu?

I have the following piece of code:
<paper-dropdown-menu id="mydropdown" label="City?">
<paper-listbox class="dropdown-content">
<paper-item>Inbox</paper-item>
<paper-item>Starred</paper-item>
<paper-item>Sent mail</paper-item>
<paper-item>Drafts</paper-item>
</paper-listbox>
</paper-dropdown-menu>
But as seen in the image below, the dropdown-content will have very small width. How can I in a clean way set the width to the same size as the actual paper-dropdown-menu?
I believe I have created what you are looking for in this jsfiddle.
or if you prefer here is the code snippet
.custom {
width: 190px;
}
.custom2 {
width: 400px;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<base href="//polygit.org/components/">
<link rel="import" href="polymer/polymer.html">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="paper-item/paper-item.html">
<link rel="import" href="paper-listbox/paper-listbox.html">
</head>
<body unresolved>
<dom-module id='base-page'>
<template>
<paper-dropdown-menu id="mydropdown" class="custom" label="City?">
<paper-listbox class="dropdown-content custom" horizontalAlign='left'>
<paper-item>Inbox</paper-item>
<paper-item>Starred</paper-item>
<paper-item>Sent mail</paper-item>
<paper-item>Drafts</paper-item>
</paper-listbox>
</paper-dropdown-menu>
<br>
<paper-dropdown-menu id="mydropdown" class="custom2" label="City?">
<paper-listbox class="dropdown-content custom2" horizontalAlign='left'>
<paper-item>Inbox</paper-item>
<paper-item>Starred</paper-item>
<paper-item>Sent mail</paper-item>
<paper-item>Drafts</paper-item>
</paper-listbox>
</paper-dropdown-menu>
</template>
</dom-module>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'base-page',
properties: {}
});
});
</script>
<base-page></base-page>
</body>
</html>
All I did was created a custom class that was applied both to the dropdown-menu and the listbox that gave them each the same width. I believe 190px is the default, but once you have the class you can size it how you want it.
In this u can adjust the width according to you iron-dropdown give the left 0 and width:100%
.custom {
width: 100%;
}
.custom2 {
width: 100%;
}
iron-dropdown{
left:0 !important;
width:100% !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<base href="//polygit.org/components/">
<link rel="import" href="polymer/polymer.html">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="paper-item/paper-item.html">
<link rel="import" href="paper-listbox/paper-listbox.html">
</head>
<body unresolved>
<dom-module id='base-page'>
<template>
<paper-dropdown-menu id="mydropdown" class="custom" label="City?">
<paper-listbox class="dropdown-content custom" horizontalAlign='left'>
<paper-item>Inbox</paper-item>
<paper-item>Starred</paper-item>
<paper-item>Sent mail</paper-item>
<paper-item>Drafts</paper-item>
</paper-listbox>
</paper-dropdown-menu>
<br>
<paper-dropdown-menu id="mydropdown" class="custom2" label="City?">
<paper-listbox class="dropdown-content custom2" horizontalAlign='left'>
<paper-item>Inbox</paper-item>
<paper-item>Starred</paper-item>
<paper-item>Sent mail</paper-item>
<paper-item>Drafts</paper-item>
</paper-listbox>
</paper-dropdown-menu>
</template>
</dom-module>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'base-page',
properties: {}
});
});
</script>
<base-page></base-page>
</body>
</html>
Actually i believe the two answers by #aditya shrivastava and #jarod moser are not working if you want it the "pure" polymer style.
Here my reasoning:
you use CSS that's not getting shimmed by Polymer
Styles are bleeding through the DOM Tree, which is not intended by Polymer
So my solution here:
set the width of input as proposed in the other two answers
set the with of dropdown with the help of the CSS Mixin:
.custom2 {
--paper-dropdown-menu-button: {
left: 0;
width: 100%;
};
}
The Mixin gets applied to the dropdown box - an iron-dropdown actually - when the CSS is getting rewritten. See the documentation at: https://www.polymer-project.org/1.0/docs/devguide/styling#custom-css-mixins
Then your code should follow this basic setup:
<dom-module id="some-kind-of-element">
<style>
#mydropdown {
--paper-dropdown-menu-button: {
left: 0;
width: 100%;
};
}
</style>
<template>
<paper-dropdown-menu id="mydropdown" label="City?">
<paper-listbox class="dropdown-content">
<paper-item>Inbox</paper-item>
<paper-item>Starred</paper-item>
<paper-item>Sent mail</paper-item>
<paper-item>Drafts</paper-item>
</paper-listbox>
</paper-dropdown-menu>
</template>
</dom-module>
I'm not 100% percent sure about the selector now, but you can see in the web inspector how the styles are added to the elements inside the paper-dropdown-menu element. See the element documentation at polymer: https://elements.polymer-project.org/elements/paper-dropdown-menu to find guidance which CSS Mixins are avaliable to you and play around with them, until you nail it. Maybe you also have to overrule the default settings with the use of !important CSS attribute modifier.
Custom CSS is still quite annoying in Polymer...
to make 'dropdown-content' dependent to width of the paper-dropdown-menu do this:
create your own menu: < resizable-dropdown-menu > copy all component from < paper-dropdown-menu-light > (i like it more), or < paper-dropdown-menu >
import IronResizableBehavior
enter
<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
add Polymer.IronResizableBehavior
behaviors: [
//...
Polymer.IronValidatableBehavior,
Polymer.IronResizableBehavior // add this
],
Add this to attached function:
/*
*
* to make menu the same width as container;
*
*/
var ironDropdown = Polymer.dom(this.$.menuButton.root).querySelector('iron-dropdown');
this.addEventListener('iron-resize', (e) => {
ironDropdown.style.width = Polymer.dom(this.root).node.host.offsetWidth+'px';
});

How to discuss between custom Polymer elements without events

I would like to discuss between custom elements without events in a parent custom element.
How could I do this, if it's possible ?
This is my-custom-parent-element.htlm :
<link rel="import" href="../my-custom-elment-1.html">
<link rel="import" href="../my-custom-elment-2.html">
<polymer-element name="my-custom-parent-element" noscript>
<template>
<my-custom-elment-1></my-custom-elment-1>
<my-custom-elment-2></my-custom-elment-2>
</template>
</polymer-element>
this is my-custom-element-1 :
<link rel="import" href="../polymer-gestures/polymer-gestures.html">
<polymer-element name="my-custom-element-1">
<template>
<div><canvas width="300px" height="200px"></canvas></div>
</template>
<script>
Polymer({
ready: function() {
<!-- Here, I need notify my-custom-element-2 that canvas received an event without re-send an event-->
}
});
</script>
</polymer-element>
this is my-custom-element-2 :
<polymer-element name="my-custom-element-2">
<template>
<div></div>
</template>
<script>
Polymer({
ready: function() {
<!-- Here, I need to change value by my-custom-element-1 notifications -->
}
});
</script>
</polymer-element>
Not sure either how you want to do this.
But you can also give reference to elem1 from the parent :
<polymer-element name="my-custom-parent-element" noscript>
<template>
<my-custom-elment-1 id="elem1"></my-custom-elment-1>
<my-custom-elment-2 id="elem2"></my-custom-elment-2>
</template>
<script>
Polymer({
domReady: function() {
this.$.elem1.target = this.$.elem2
}
});
</script>
</polymer-element>
And in elem1 :
<polymer-element name="my-custom-element-1" attributes="target">
<template>
<div><canvas width="300px" height="200px"></canvas></div>
</template>
<script>
Polymer({
ready: function() {
<!-- Here, I need notify my-custom-element-2 that canvas received an event without re-send an event-->
target.xxx();
}
});
</script>
</polymer-element>
But usually, the event way is better

Categories