How do I show a property in the light DOM - html

I'm expecting to see hello but I'm seeing {{someVar}}
<my-parent some-var="hello">
<div>{{someVar}}</div>
</my-parent>
I am passing some-var="{{someVar}}" to <content>:
<dom-module id="my-parent>
<template>
<content some-var="{{someVar}}"></content>
</template>
<script> Polymer({is: "my-parent"}); </script>
</dom-module>
I need this variable to be bound, such that if I embed another component, rather than a <div>, the embedded component can change the parent's property

This is a little hard to explain.. but the only element that can see the {{someVar}} binding is the element that contains my-parent. The div is not physically moved inside of the content element, it's just 'projected' so it visually renders in that position. This is how native Shadow DOM works.
If you need to do something with your distributed children you'll probably want to use the effectiveChildNodes API https://www.polymer-project.org/1.0/docs/devguide/local-dom.html#effective-children

In some-child, make sure the property definition has notify: true
properties: {
locationHref: {
type: String,
notify: true
}
}
See https://www.polymer-project.org/1.0/docs/devguide/data-binding.html#property-notification

Related

Polymer access elements within vaadin-grid

Working jsbin illustrating the problem: http://jsbin.com/nomewa/3/edit?html,console,output
I am trying to set the innerHTML of notworking span inside of a template that makes up the vaadin-grid. It does not currently seems to be possible to bind to on-change of vaadin-checkbox when said vaadin-checkbox lies within vaadin-grid since I cannot even access the element within the grid.
It does not currently seems to be possible to bind to on-change of vaadin-checkbox when said vaadin-checkbox lies within vaadin-grid since I cannot even access the element within the grid.
You can use declarative event bindings inside the column tempates, so you don’t have to have a reference to the actual stamped element.
Something like this:
<vaadin-grid-column>
<template>
<vaadin-checkbox on-checked-changed="_myListener">Checkbox</vaadin-checkbo>
</template>
</vaadin-grid-colum>
This is a great way to try some codes (jsbin :) Here this code works but I do not know is it going to fit your need. (I cut and copied only changed parts :
At the property declaration, you may set a pre-value to notWorking property. Then you may change it with dynamically in a function.
<iron-ajax url="https://randomuser.me/api?results=10" last-response="{{response}}" auto></iron-ajax>
<span id="working">Working</span>
<vaadin-grid id="grid" items="{{response.results}}" style="height: auto;">
<vaadin-grid-column>
<template class="header">Name</template>
<template>[[item.name.first]] [[item.name.last]] <span>{{notWorking}}</span></template>
</vaadin-grid-column>
</vaadin-grid>
</template>
<script>
class MyTest extends Polymer.Element {
static get is() { return 'test-component'; }
ready() {
super.ready();
this.set('notWorking',"test")
debugger;
}
}
Here the working link

How to access Polymer functions from JS

Sorry if this comes out a bit garbled, I'm not sure how to ask this question.
What I am trying to do is keep the DOM synced with a localStorage value, and am updating the localStorage value with an interact.js mouse event.
Currently, I am able to properly set the localStorage value, but am having problems updating the DOM.
My current build is within the Polymer framework, so I am having trouble selecting shadow DOM content.
The DOM tree looks like
PARENT-ELEMENT
# SHADOW ROOT
EL
EL
DIV
CUSTOM ELEMENT
EL
EL
Here are some ways I have failed to solve the problem. The Custom Element is in pure JS, since I am not sure how to properly wrap interact.js function in Polymer:
I tried directly accessing the PARENT-ELEMENT's shadow DOM from the Custom Element in pure JS.
var shadowDOMNode = document.querySelector('PARENT-ELEMENT');
var dom_object_1 = shadowDOMNode.querySelector('#dom_object_1');
dom_object_1.innerHTML = localStorage.dom_object_1;
I tried selecting a helper updateDOM() function from the PARENT Polymer element and running it from the Custom Element's setter directly.
if (event.dy > 0) {
this.$$('PARENT-ELEMENT').updateDOM();
}
Maybe I am taking the wrong approach entirely, but I haven't been able to find analogues for interact.js in using native Polymer functions.
I hope this question was clear enough...
If we ignore the interact.js part of the problem and focus on Polymer, you could probably solve this without coupling the two.
To bind to a localStorage value with Polymer, use the <iron-localstorage> element. In the following example, the localStorage value named flavor_1_amount is loaded and stored into a property named _flavor1Amount. If the value doesn't exist in localStorage or is empty, the <iron-localstorage> element fires an event (iron-localstorage-load-empty), which allows you to bind to a callback (e.g., to initialize it).
<iron-localstorage name="flavor_1_amount"
value="{{_flavor1Amount}}"
use-raw
on-iron-localstorage-load-empty="_initFlavor1Amount">
</iron-localstorage>
In the same element, you could provide an input for the user to update the localStorage value.
<paper-input label="Flavor Amount (mL)" value="{{_flavor1Amount}}"></paper-input>
And you can use <iron-localstorage>.reload() to keep your data binding in sync, assuming it could be changed externally.
See this codepen for a full demo. Check your localStorage from Chrome DevTools:
Generally speaking you should use this.set() or any of the array mutation methods if it's an array in order for the ShadowDOM to be notified properly.
Since you want to perform this update from outside the element itself, imperatively, I'd suggest this:
Expose a couple of methods from your element that you can use to add/remove/change property values from outside your element.
These methods would internally use the proper channels to make the changes.
An example (you can call addItem() to add items from outside your element):
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
<dom-module id="x-example">
<template>
<template is="dom-repeat" items="[[data]]">
<div>{{item.name}}</div>
</template>
</template>
<script>
HTMLImports.whenReady(function() {
"use strict";
Polymer({
is: "x-example",
properties: {
data: {
type: Array,
value: [
{name: "One"},
{name: "Two"},
{name: "Three"}
]
}
},
// Exposed publicly, grab the element and use this method
// to add your item
addItem: function(item) {
this.push("data", item);
}
});
});
</script>
</dom-module>
<x-example id="x-example-elem"></x-example>
<script>
setTimeout(function() {
// simply 'grab' the element and use the
// `addItem()` method you exposed publicly
// to add items to it.
document.querySelector("#x-example-elem").addItem({name: "Four"});
}, 2500);
</script>
Important: That being said, this is not the "Polymeric" way of doing stuff as this programming-style is imperative, in constrast with Polymer's style which is more declarative. The most Polymeric solution is to wrap your interact.js functionality in an element itself and use data-binding between your 2 elements to perform the changes.

How to dynamically change CSS in polymer?

I have a polymer element which is a stack of images that need to expand and reveal each of the images upon hovering on the stack. It's supposed to look like this if left untouched:
And upon hovering, the stack would expand vertically.
The code for the element is (do-profile-pic is another element that puts each image down):
<polymer-element name="do-profile-pic-stack" attributes="images">
<template>
<style>...</style>
<div class="stack-container"
on-mouseover='{{onHovered}}'
on-mouseout='{{onUnhovered}}'>
<template repeat="{{picture, i in images}}">
<div class="stack-img-container"
/* the stack is angled so bit of every image is visible */
style="top: {{5 * i}}; z-index: {{10 - i}}">
<do-profile-pic imgurl="{{picture.url}}" showtime="false">
</do-profile-pic>
</div>
</template>
</div>
</template>
<script>
Polymer('do-profile-pic-stack', {
images: [],
...
});
</script>
</polymer-element>
Now i've looked around, and there seem to be two ways to do this. One is to use the polymer selectors. The other is to use user event methods. I'm not using any of these methods to achieve the default angled layout (comment above). To expand the stack vertically, i'll have to play with positioning and layout, but i cannot seem to think of a good way to implement the this.
The polymer selectors appear complicated.
Programmatically, it looks like playing with HTMLElement will be required. I can do that, but angular-js handles this much better. So is there a better way to go about this that i might be mssing? Thanks.
If you want to change appearance on mouse-over I think you can go with a pure CSS solution with two sets of styles one normal and one for mouse-over.
stack-img-container {
// normal style here
}
stack-img-container:hover {
// mouse-over style
// overrides and extends normal style
}
and put it in your style tag (instead of the dots ...).
Use Element.classList to change class dynamically.
<script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.3/platform.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.3/polymer.js"></script>
<my-app></my-app>
<polymer-element name='my-app'>
<template>
<style>
.hovered {
background-color:red;
}
</style>
<span on-mouseenter='{{onHover}}'
on-mouseleave='{{onUnhover}}'>
Mouse over me, please.
</span>
</template>
<script>
Polymer('my-app', {
onHover: function(e) {
e.srcElement.classList.add('hovered');
},
onUnhover: function(e) {
e.srcElement.classList.remove('hovered');
}
})
</script>
</polymer-element>
Notes:
Event.srcElement is not standard will and not work for every user.
The standard way to reference an element within an Polymer element is the Polymer.Base $$(slctr):
this.$$('span').classList.add('hovered');
It works in my project, but not in this snippet.
The code above was copied and edited from Peter Burns' code snippet from How can I handle a hover the Polymer way without external libraries?
Curly braces around onHover in the event handler shouldn't be necessary, however, they don't work otherwise in this snippet. Not sure if it's because of the version of Polymer used. I tried to link to the latest but it failed.
Here is a demo of changing class dynamically with plain javascript.

Is it possible to inject HTML into a polymer component via an attribute?

I'm using one of the core polymer components that basically has:
<polymer-element attributes="label">
<div>{{label}}</div>
as part of the source. I'd like to inject some HTML into this so that it ultimately renders as:
<div>Item <small>Description</small></div>
Is there any way to do this without copying the entire component (which is basically impossible considering the dependency chain)?
Polymer doesn't allow setting HTML inside {{}} expressions because it's a known XSS outlet. However, there are ways around it (1, 2).
I'm not sure there's a great way around this issue but I found something that works. You want to extend the element but also need to modify its shadow dom because of the .innerHTML limitation. Taking paper-button as an example, it has an internal {{label}}. You could extend the element, drill into its shadow dom, and set .innerHTML of the container where {{label}} is set. React to label changing (labelChanged) and call this.super():
<polymer-element name="x-el" extends="paper-button">
<template>
<shadow></shadow>
</template>
<script>
Polymer('x-el', {
labelChanged: function() {
// When label changes, find where it's set in paper-button
// and set the container's .innerHTML.
this.$.content.querySelector('span').innerHTML = this.label;
// call paper-button's labelChanged().
this.super();
}
});
</script>
</polymer-element>
Demo: http://jsbin.com/ripufoqu/1/edit
Problem is that it's brittle and requires you to know the internals of the element you're extending.

Polymer document.querySelector not working as expected

Either I am doing something horribly wrong or Polymer just doesn't like me. See following:
<polymer-element name="menu-paper-ui" noscript>
<template>
<paper-dialog heading="Dialog" transition="paper-dialog-transition-bottom">
[ .. ]
</paper-dialog>
<paper-button label="Dialog Bottom" on-tap="{{toggleDialog}}"></paper-button>
</template>
<script>
Polymer('menu-paper-ui', {
toggleDialog : function() {
var dialog = document.querySelector('paper-dialog');
console.log(dialog); //returns null
dialog.toggle();
}
})
</script>
</polymer-element>
Now, I have my reasons to use querySelector. So, if someone can tell me whats going wrong that will be great!
This question is nearly identical to Using querySelector to find nested elements inside a Polymer template returns null.
The short answer is that elements in a polymer-element's template are put into the ShadowDOM of that element, are not not visible to the anything outside of that element. This is so that you can control styling more easily, and element IDs are scoped.
You can either give the dialog an id and use Polymer's automatic node finding, or use this.shadowRoot.querySelector('paper-dialog').
The Problem is that you can not access the shadow DOM inside a custom element with document.querySelector. See my answer to a similar question.