How do I set an element's height with Polymer? - polymer

I have created a Polymer behavior that is supposed to take a selected element's width and set its height to that width on "ready" and when the window is resized. I've done the "get the width" part but setting the height does not work. Here's the code:
<link rel="import" href="../bower_components/polymer/polymer.html">
<script>
SquareBehavior = {
properties: {
victim: Object
},
listeners: {
'resize': 'squareIt' // not working
},
squareIt: function() {
this.victim = this.$$('.round');
console.log(this.victim.offsetWidth); // this works fine
// what do I add here?
},
ready: function() {
this.squareIt(); //works
}
};
</script>

You can use iron-resizable-behavior.
https://github.com/polymerelements/iron-resizable-behavior
<html>
/*...*/
<link rel="import" href="../../bower_components/iron-resizable-behavior/iron-resizable-behavior.html">
/*...*/
Polymer({
/*...*/
listeners: {
'iron-resize': "_resizeHandler"
},

You might have to declare style property and set the reflect to attribute to true
<link rel="import" href="../bower_components/polymer/polymer.html">
<script>
SquareBehavior = {
properties: {
victim: Object
style: {
type: String,
reflectToAttribute: true
}
},
listeners: {
'resize': 'squareIt' // not working
},
squareIt: function() {
this.victim = this.$$('.round');
console.log(this.victim.offsetWidth); // this works fine
// what do I add here?
this.style = "{height: " + this.victim.offsetWidth + " }"
},
ready: function() {
this.squareIt(); //works
}
};
</script>

Related

Polymer dynamic element name in dom-repeat

how can I use dom-repeat to create different elements for iron-pages? something like this:
<template is="dom-repeat" items="{{pages}}">
<[[item.name]]></[[item.name]]>
</template>
...
pages: {
type: Array,
value() {
return [
{
"name": "element-a",
},
{
"name": "element-b",
},
{
"name": "element-c",
},
},
I'm using polymer 2.0.
since my comment had some interest, I put it as an answer.
The code examples will be in polymer 1.9 for now, I'll update my answer when I'll do the switch to 2.0 but the idea should be the same anyway
First you need a wrapper element, wich will be capable of creating another element dynamically from a property, and adding it to itself.
In my example the name of the element to create will be a property named type of a JSON object Data wich came from a database in XHR.
With a dynamically created element the binding won't work, so you have to do it by hand. That's what the _updateStatefunction is for, here it only update on one property but the idea is the same is there is more.
wrapper :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../styles/shared-styles.html">
<dom-module id="element-wrapper">
<template></template>
<script>
Polymer({
is: 'element-wrapper',
properties: {
elementData: {
type: Object
},
saveFbPath: {
type: String
},
element: {
type: Object
},
formSubmitPressed: {
type: Boolean,
value: false
}
},
observers: [
'_updateState(elementData.*)'
],
attached: function () {
console.log("creating element : ", this.elementData);
this.async(function () {
this.element = this.create(this.elementData.type, {
"data": this.elementData
});
this.appendChild(this.element);
}.bind(this));
},
_updateState: function (elementData) {
if (typeof this.element !== "undefined") {
this.element.data = elementData.value;
console.log('Change captured', elementData);
}
}
});
</script>
</dom-module>
The this.element = this.create(this.elementData.type, {"data":this.elementData}); line is the one creating the element, first argument is the dom-modulename, and the second a JSON object wich will be binded to the properties of the element.
this.appendChild(this.element);will then add it to the dom
All this is in a this.async call for a smoother display
You then need a dom-repeat which will call this element, and give it the datas it need to create the dynamic ones.
Here is an example of an element-list but you don't necessary need a specific element for that, this logic can be in a bigger one.
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../styles/shared-styles.html">
<link rel="import" href="element-wrapper">
<dom-module id="element-list">
<template>
<style is="custom-style" include="shared-styles"></style>
<template is="dom-repeat" items="[[ datas ]]" initial-count="10" as="data">
<element-wrapper element-data="[[data]]"></element-wrapper>
</template>
</template>
<script>
Polymer({
is: 'element-list',
properties: {
datas: {
type: Array,
value: function () {
return [];
}
}
}
});
</script>
</dom-module>
This should do the trick :)

How to combine polymer-redux and <app-route>?

We are currently using <app-route> for routing, and are now implementing Redux using polymer-redux. It's unclear what the best way is to combine the two, however. Since <app-route> maintains its own state, we can't really store it in our Redux store. However, for some actions the user can perform, we also want to update the URL.
My current line of thinking is doing something with middleware, but it's not quite clear to me how best to access/modify the routes in <app-route> from within that middleware. How can we best approach this?
I've ran into the same problem myself. The clash is that the main principle of redux is that it advocates for a unique centralized state, while polymer advocates for multiple decentralized states. Reconciling both obviously requires some hack from time to time. Syncing the browser URL in the redux state through polymer (to use with app-route) is a good example. Using app-location, what I've done is this:
<!-- my-app.html -->
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/app-route/app-location.html">
<link rel="import" href="/src/redux/redux-behavior.html">
<link rel="import" href="/src/redux/route-action-creator.html">
<dom-module id="my-app">
<template>
<app-location route="{{_browserRoute}}"></app-location>
</template>
<script>
Polymer({
is: 'my-app',
behaviors: [ReduxBehavior, RouteActionCreator],
properties: {
_browserRoute: {
type: Object
}
},
observers: [
'_browserRouteChanged(_browserRoute.*)'
],
_browserRouteChanged: function(browserRoute) {
this.dispatch('browsed', browserRoute.base)
}
})
</script>
</dom-module>
We use a two-way binding to keep my-app's _browserRoute in sync with the browser url. Upon change, it dispatches a 'browsed' action, which is going to reduce the current route in the state to the new value of _browserRoute.
<!-- route-action-creator.html -->
<script>
const ROUTE_ACTIONS = {}
const routeActions = {}
ROUTE_ACTIONS.BROWSED = 'ROUTE_BROWSED'
routeActions.browsed = function(route) {
return {
type: ROUTE_ACTIONS.BROWSED,
route: route
}
}
ROUTE_ACTIONS.REDIRECTED = 'ROUTE_REDIRECTED'
routeActions.redirected = function(path) {
window.history.pushState({}, null, path)
window.dispatchEvent(new CustomEvent('location-changed'))
return {
type: ROUTE_ACTIONS.REDIRECTED,
path: path
}
}
/**
* Route action creator behavior
* #polymerBehavior
*/
RouteActionCreator = {
actions: routeActions
}
</script>
<!-- route-reducer.html -->
<link rel="import" href="/src/redux/route-action-creator.html">
<script>
const reducers = {}
reducers.routeReducer = function(state, action) {
if (!state) {
return {
current: null
}
}
switch (action.type) {
case ROUTE_ACTIONS.BROWSED:
return Object.assign({}, state, {
current: action.route
})
case ROUTE_ACTIONS.REDIRECTED:
// Browser state is reduced in action creator because it uses an event
return state
default:
return state
}
}
</script>
And so now I can bind
statePath: 'route.current'
to any app-route component in my app. I can also use
this.dispatch('redirected', '/some-path')
for redirection and standard anchor tags as the browser will sync with the state.
I would create a non-visual polymer element that wraps an app-route and also implements the polymer-redux behavior and handles the interaction between app-route and the redux store.
Here is an example of a such an element for a wizard (with steps and analysis to be loaded):
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/app-route/app-location.html">
<link rel="import" href="../bower_components/app-route/app-route.html">
<dom-module id="my-router">
<style>
</style>
<template>
<app-location route="{{route}}" id="location" use-hash-as-path></app-location>
<app-route
active="{{active}}"
route="{{route}}"
pattern="/:step/:id"
data="{{page}}"
tail="{{tail}}">
</app-route>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'my-router',
behaviors: [ ReduxBehavior ],
properties: {
page: {
type:Object,
value: function(){ return {};},
},
currentAnalysisId: {
type:String,
statePath: 'id',
},
currentStep: {
type:String,
statePath:'currentStep',
},
active:{
type: Boolean,
observer:'_onInValidRoute'
}
},
observers: [
'_changeRoute(page)',
'_changeStepOrAnalysisId(currentStep,currentAnalysisId)',
],
actions: {
changeStep: function(step) {
return {
type: 'CHANGE_CURRENT_STEP',
step,
}
},
loadAnalysis: function(id,step) {
return {
type: 'LOAD_ANALYSIS',
id,
step,
}
},
},
_initialized : false,
ready: function() {
// required otherwise if navigating to a sub-route for the first time will be reset, by the localstorage initial redux state update
this._initialized = true;
},
_changeRoute: function(page) {
// required because otherwise
this.debounce('route_update',()=>{
let step = page.step;
let id = page.id;
if (id && this.getState().id !== id) {
ga('send', 'event', 'Analysis', 'load');
this.dispatch('loadAnalysis',id,step)
}
else if (this.getState().currentStep !== step) {
this.dispatch('changeStep',step);
}
ga('send', 'pageview');
},1);
},
_changeStepOrAnalysisId: function(step, id) {
if (!this._initialized) {
return;
}
this.debounce('state_changed',()=>{
if (this.page.step !== step || (this.page.id !== id && this.page.id !== '' && id !== null)) {
this.page = {step,id};
}
})
},
_onInValidRoute: function(valid) {
if (!valid) {
this.async(()=> {
this.$.location.set('route.path','/start/');
})
}
},
});
})();
</script>
</dom-module>

How to reflect change from child to parent in Polymer

The current code is below. I have element-value in the main file. This value is passed to the child elements app-element and from there to app-element-add.
The value changes in app-element-add. But I cannot get the value reflected in the main element.
The observers never get invoked.
main.html
<app-element element-value = {{ elementValue }}></app-element>
Polymer({
is: 'app-main-element',
properties: {
elementValue: {
type:Array,
notify:true,
observer:'listUpdated'
}
});
app-element.html
<app-element-add element-value = {{ elementValue }}></app-element-add>
Polymer({
is: 'app-element',
properties: {
elementValue: {
type:Array,
notify:true,
observer:'listUpdated'
}
});
app-element-add.html
Polymer({
is: 'app-element-add',
properties: {
elementValue: {
type:Array,
notify:true,
reflectToAttribute:true
}
});
Any hints on how to reflect changes in app-element-add in app-main-element. Thanks.
You don't need to use reflectToAttribute here. The only option required here is notify. However, your current code works:
HTMLImports.whenReady(_ => {
"use strict";
Polymer({
is: 'app-main-element',
properties : {
elementValue: {
type: Array,
notify: true,
observer: 'listUpdated',
value: _ => [100,200,300]
}
},
listUpdated: function() {
console.log('[app-main-element] list updated');
},
ready: function() {
console.log('[app-main-element] ready');
}
});
Polymer({
is: 'app-element',
properties : {
elementValue: {
type: Array,
notify: true,
observer: 'listUpdated'
}
},
listUpdated: function() {
console.log('[app-element] list updated');
},
ready: function() {
console.log('[app-element] ready');
}
});
Polymer({
is: 'app-element-add',
properties : {
elementValue: {
type: Array,
notify: true
}
},
ready: function() {
console.log('[app-element-add] ready (will set elementValue in 1000ms)');
this.async(_ => {
console.log('[app-element-add] updating elementValue');
this.elementValue = [1,2,3];
}, 1000);
}
});
});
<head>
<base href="https://polygit.org/polymer+1.11.0/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<app-main-element></app-main-element>
<dom-module id="app-main-element">
<template>
<app-element element-value={{elementValue}}></app-element>
<div>app-main-element.elementValue = [[elementValue]]</div>
</template>
</dom-module>
<dom-module id="app-element">
<template>
<app-element-add element-value={{elementValue}}></app-element-add>
<div>app-element.elementValue = [[elementValue]]</div>
</template>
</dom-module>
<dom-module id="app-element-add">
<template>
<div>app-element-add.elementValue = [[elementValue]]</div>
</template>
</dom-module>
</body>
codepen

Polymer 1.0 observers watching a object path is not triggering even though data has changed

jsFiddle: https://jsfiddle.net/ghstahl/09tL1ku7/
<script src="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/polymer/polymer.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-styles/paper-styles.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-styles/color.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-styles/default-theme.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-ripple/paper-ripple.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-behaviors/paper-inky-focus-behavior.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-checked-element-behavior/iron-checked-element-behavior.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-toggle-button/paper-toggle-button.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-input/paper-input.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-button/paper-button.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-flex-layout/iron-flex-layout.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-flex-layout/classes/iron-flex-layout.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-flex-layout/classes/iron-shadow-flex-layout.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-dropdown-menu/paper-dropdown-menu.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-menu-button/paper-menu-button.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-a11y-keys-behavior/iron-a11y-keys-behavior.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-behaviors/iron-control-state.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-behaviors/iron-button-state.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-icons/iron-icons.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-icon/iron-icon.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/iron-selector/iron-selector.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-item/paper-item.html">
<link rel="import" href="https://cdn.rawgit.com/download/polymer-cdn/1.1.3/lib/paper-behaviors/paper-button-behavior.html">
<dom-module id="pingo-toggle">
<style>
.line {
margin-bottom: 40px;
}
.line span {
margin-left: 24px;
}
</style>
<template>
<div class="line">
<paper-toggle-button checked={{singleToggle.data}}></paper-toggle-button> <span>{{singleToggle.label}}</span>
<span>{{computeBooleanToString(singleToggle.data)}}</span>
</div>
<template is="dom-repeat" items="{{_workingArray}}">
<div class="line">
<paper-toggle-button checked={{item.value.data.checked}}></paper-toggle-button>
<span>{{item.value.label}}</span>
<span>{{item.value.id}}</span>
<span>{{computeBooleanToString(item.value.data.checked)}}</span>
</div>
</template>
</template>
<script>
(function() {
Polymer({
is: 'pingo-toggle',
properties: {
singleToggle: {
type: Object,
notify: true
},
toggleItems: {
type: Object,
notify: true,
observer: '_toggleItemsChanged'
},
},
_toggleItemsChanged: function(newValue, oldValue) {
if (this.toggleItems !== undefined) {
this._workingArray = this._toArray(this.toggleItems);
}
},
_toArray: function(obj) {
var index = 0;
var thisElement = this;
this._arrayData = Object.keys(obj).map(function(key) {
var id = "item_" + index;
++index;
var val = {};
val.data = obj[key];
val.label = "hi:" + key;
val.data = obj[key];
val.id = id;
val.original = obj.key;
return {
name: key,
value: val
};
});
return this._arrayData;
},
computeBooleanToString: function(a) {
return a === true ? 'true' : 'false';
}
});
})();
</script>
</dom-module>
<dom-module id="pingo-toggle-container">
<style>
</style>
<template>
<pingo-toggle single-toggle={{_singleToggle}} toggle-items={{_toggleItems}}></pingo-toggle>
<paper-item>{{_singleToggleHello}}</paper-item>
<paper-item>{{_toggleItemsHello}}</paper-item>
</template>
<script>
(function() {
Polymer({
is: 'pingo-toggle-container',
properties: {
_singleToggleHello: {
type: String,
notify: true,
value: "Well Hello There"
},
_toggleItemsHello: {
type: String,
notify: true,
value: "Hi there from many"
},
_singleToggle: {
type: Object,
notify: true,
value: {
label: "Single Toggle",
data: true
}
},
_toggleItems: {
type: Object,
notify: true,
value: {
a: {
label: "a Toggle2",
checked: true
},
b: {
label: "a Toggle2",
checked: false
}
}
}
},
// Observers
/////////////////////////////////////////////////////////
observers: ['_toggleItemsChanged(_toggleItems.*)', '_singleToggleChanged(_singleToggle.*)'],
// Smart check. only fire if we change state.
_singleToggleChanged: function(changeRecord) {
var thisElement = this;
this._singleToggleHello = this.computeBooleanToString(this._singleToggle.data) + Math.random() + changeRecord.path;
console.log("_singleToggle in pingo-toggle-container changed:" + changeRecord.path);
},
_toggleItemsChanged: function(changeRecord) {
var thisElement = this;
this._toggleItemsHello = "_workingToggleItemsChanged fired" + Math.random() + changeRecord.path;
console.log("pingo-toggle-container notWorking:" + changeRecord.path);
},
computeBooleanToString: function(a) {
return a === true ? 'true' : 'false';
},
ready: function(e) {
}
});
})();
</script>
</dom-module>
<pingo-toggle-container></pingo-toggle-container>
Scenario: Parent element owns a data object. Parent element passes portion of the data object to children elements which bind to values in the object. When children change the value, because they have an object reference, the data is changed directly in the parent owned master data object.
Problem: '_toggleItemsChanged(_toggleItems.*)' fires once, the very first time, but never fires again even though data in _toggleItems has changed.
Proof: In the pingo-toggle-container element;
Put a breakpoint at _singleToggleChanged and _toggleItemsChanged.
Both fire at startup.
Toggle both 'hi:a' and 'hi:b' back and forth.
nothing fires.
So I set 'hi:a' and 'hi:b' both to True.
'Single Toggle' does fire, so I toggle that and my breakpoint hits.
evaluate this._toggleItems;
Hmm, you both are set to true. You can toggle 'hi:a' and 'hi:b' to false and redo the test. Now they are both set to false.
Why you not fire observers: ['_toggleItemsChanged(_toggleItems.)', '_singleToggleChanged(_singleToggle.)'], for data change?
Expected: I want the _toggleItemsChanged function to get called with the changeRecord.path of _toggleItems.a.checked or _toggleItems.a.checked repectively.
I don't know if I could understand your example right, but I believe you are trying to observe a deeper sub-property change. It won't work.
Observers cannot watch that deeper. They observe the first level properties changes only when you use . If you need to go deeper, you need to observe other paths, like _toggleItems.a. and so on.

Extending native HTML Elements with Polymer

Hi i'd like to extend from a native HTML Element with Polymer for creating a custom web component.
My polymer ready-callback is getting called, when i'm not extending. As soon as i extend, nothing gets called anymore. Though the shadow DOM for the element is being created...
Here is my code for the usage:
<!DOCTYPE html>
<html>
<head>
<title>Custom Component Usage</title>
<script src="bower_components/platform/platform.js"></script>
<link rel="import" href="elements/extended-item.html">
</head>
<body>
<extended-item>I was extended from div!</extended-item>
</body>
</html>
Custom Element, which extends div:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="extended-item" extends="div">
<template>
<content></content>
</template>
</polymer-element>
<script>
Polymer('extended-item',
{
created: function ()
{
console.log('EXTENDED ITEM IS CREATED!');
},
ready: function ()
{
console.log('EXTENDED ITEM IS READY!');
},
attached: function ()
{
console.log('EXTENDED ITEM IS ATTACHED!');
},
domReady: function ()
{
console.log('EXTENDED ITEMS DOM IS READY!');
},
detached: function ()
{
console.log('EXTENDED ITEM IS DETACHED!');
},
attributeChanged: function (attrName, oldVal, newVal)
{
//var newVal = this.getAttribute(attrName);
console.log(attrName, 'old: ' + oldVal, 'new:', newVal);
}
});
</script>
Any idea?
thanks in advance,
Rob
This works properly if, instead of referring to the element as <extended-item>, you instead use <div is="extended-item">.
Here's an example (jsbin):
<polymer-element name="extended-item" extends="div">
<template>
<p>This is part of the template.</p>
<content></content>
</template>
<script>
Polymer('extended-item', {
created: function() {
console.log('EXTENDED ITEM IS CREATED!');
},
ready: function() {
console.log('EXTENDED ITEM IS READY!');
},
attached: function() {
console.log('EXTENDED ITEM IS ATTACHED!');
},
domReady: function() {
console.log('EXTENDED ITEMS DOM IS READY!');
},
detached: function() {
console.log('EXTENDED ITEM IS DETACHED!');
},
attributeChanged: function (attrName, oldVal, newVal)
{
//var newVal = this.getAttribute(attrName);
console.log(attrName, 'old: ' + oldVal, 'new:', newVal);
}
});
</script>
</polymer-element>
<div is="extended-item">I was extended from div!</div>
EDIT: As pointed out in this comments, this is expected behavior, documented in the Custom Elements spec.