Change style programmatically - polymer

I'm attempting to follow the Custom property API for Polymer elements docs with regards to updating an element's CSS properties (and styles) programmatically but can't seem to get it working. Here is my current attempt:
<link rel="import" href="https://rawgit.com/Polymer/polymer/master/polymer.html">
<dom-module id="my-namecard">
<style>
:host {
--color: green;
}
span {
color: var(--color, orange);
}
</style>
<template>
<div>
Hi! My name is <span>{{name}}</span>
</div>
<button on-click="redClick">Set text to Red</button>
<button on-click="blueClick">Set text to Blue</button>
</template>
<script>
Polymer({
is: 'my-namecard',
properties: {
name: {
type: String
}
},
redClick: function() {
console.log('Setting "--color" red');
this.customStyle['--color'] = 'red';
this.updateStyles();
},
blueClick: function() {
console.log('Setting "--color" blue');
this.customStyle['--color'] = 'blue';
this.updateStyles();
}
});
</script>
</dom-module>
<my-namecard name="Sean"></my-namecard>
I've also attempted not setting a CSS property and using a direct style such as:
redClick: function() {
console.log('Setting "color" red');
this.customStyle['color'] = 'red';
this.updateStyles();
}
I've created a codepen demonstrating the issue

You've hit a bug in the current version of Polymer, where a style change like this will propagate to child Polymer elements, but not to the element itself. See:
https://github.com/Polymer/polymer/issues/1851
The workaround is to call Polymer.updateStyles() instead of this.updateStyles().
Here's the updated codepen.

Related

dynamically create elements in Polymer and setting attributes

Considering defining a polymer element
<dom-module id="custom-element">
<template>
<h1>I expect to be green</h1>
</template>
<script>
Polymer({
is: 'custom-element',
properties: {
color: {
type: String,
value: 'red'
}
},
ready: function() {
this.style.color = this.color;
}
});
</script>
</dom-module>
I would expect that the following two would produce the same result:
(in markup)
<body>
<custom-element color="green"></custom-element>
</body>
(in JS)
var customElement = document.createElement('custom-element');
customElement.color = 'green';
document.body.appendChild(customElement);
But in fact it doesn't, as it seems that the properties are set and the polymer 'ready' function is triggered before the customElement is appended to document.body.
So basically I cannot dynamically create (in JS) custom elements and set them initial properties, different than the default properties.
How would you suggest I should do that?
Thanks!
Set the color in attached callback instead of ready. Attached is called after the element has been appended in dom.
<base href="https://polygit.org/components/">
<script src="wecomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
<dom-module id="custom-element">
<template>
<h1>I expect to be green</h1>
</template>
<script>
Polymer({
is: 'custom-element',
properties: {
color: {
type: String,
value: 'red'
}
},
attached: function() {
this.style.color = this.color;
}
});
</script>
</dom-module>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
var customElement = document.createElement('custom-element');
customElement.color = 'green';
document.body.appendChild(customElement);
</script>
</body>
</html>
In your case, I would avoid altering the DOM and use a simple attribute binding.
Here is a proof of concept: http://jsbin.com/jozejemumu/1/edit?html,js,output
As you can see, I used attribute binding for the style attribute on the h1 element, which simply sets the CSS color property to whatever the value of the elements' color property is.
The code is faily simple, and looks like this:
<dom-module id="custom-element">
<template>
<h1 style$="color: [[color]];">I expect to be green</h1>
</template>
<script>
Polymer({
is: 'custom-element',
properties: {
color: {
type: String,
value: 'red'
}
}
});
</script>
</dom-module>
Using the element remains unchanged:
<custom-element color="green"></custom-element>
Or:
var customElement = document.createElement('custom-element');
customElement.color = 'orange';
document.body.appendChild(customElement);
Just make sure that the color property contains a valid HTML color name/value.

How to get the width of a polymer element inside the JS of the polymer element

I have a polymer element and inside its Javascript I am trying to find its width and height as it is inside the DOM. I tried many ways but I always get 0 back.
...and here's the one which works for me:
Add the Polymer.IronResizableBehavior behaviour to your element (see https://elements.polymer-project.org/elements/iron-resizable-behavior). Then your element will be sent a iron-resize event when it gets sized. In the handler, check to see whether it's not 0; if not, you're golden.
Untested code follows:
<dom-module id="hard-chicken">
<template>
Hello <span>{{name}}</span>
</template>
</dom-module>
<script>
Polymer({
is: 'hard-chicken',
behaviors: [ Polymer.IronResizableBehavior ],
listeners: { "iron-resize": "onWidthChange" },
onWidthChange: function() {
var w = this.offsetWidth;
if (w > 0)
console.log("width is now ", w);
}
});
</script>
This took way, way too long to figure out...
polymer 1.0
<link rel="import" href="../polymer/polymer.html">
<!--
Here's where you'll define your element. You can define multiple elements
if you want, but the package name will be taken from the first custom
element you define in the file. You can also document your element! For
more info, see [the docs](https://ele.io/docs).
#element hard-chicken
-->
<dom-module id="hard-chicken" attributes="name">
<template>
<style>
:host {
font-family: sans-serif;
}
</style>
Hello {{name}}
</template>
<script>
Polymer({is:'hard-chicken',
/**
* The name of the person you want to say hello to.
* #attribute name
* #type string
* #default "Polymer Dev"
*/
name: 'Polymer Dev',
ready: function() {
console.log(this.offsetWidth);
});
</script>
</dom-module>
You probably want to calculate the width in the attached handler of the element, as that comes last when the unit is actually attached to the DOM.
See https://www.polymer-project.org/1.0/docs/devguide/registering-elements.html#initialization-order
attached: function() {
console.log(this.offsetWidth);
}
This should do it:
attached: function() {
this.async(function() {
console.log(this.offsetWidth);
});
}
Read the documentation at:
https://www.polymer-project.org/1.0/docs/devguide/registering-elements#initialization-order
<dom-module id="hard-chicken">
<style>
:host {
font-family: sans-serif;
}
</style>
<template>
Hello <span>{{name}}</span>
</template>
</dom-module>
<script>
Polymer({
is: 'hard-chicken',
/**
* The name of the person you want to say hello to.
* #attribute name
* #type string
* #default "Polymer Dev"
*/
properties: {
name: {
value : 'Polymer Dev',
type: String
}
},
_getWidth: function () {
console.log(this.offsetWidth);
},
ready: function() {
this.async(this._getWidth, 500);
}
});
</script>
<link rel="import" href="../polymer/polymer.html">
<!-- You can import core and paper elements -->
<link rel="import" href="../core-ajax/core-ajax.html">
<!--
Here's where you'll define your element. You can define multiple elements
if you want, but the package name will be taken from the first custom
element you define in the file. You can also document your element! For
more info, see [the docs](https://ele.io/docs).
#element hard-chicken
-->
<polymer-element name="hard-chicken" attributes="name">
<template>
<style>
:host {
font-family: sans-serif;
}
</style>
Hello {{name}}
</template>
<script>
Polymer('hard-chicken', {
/**
* The name of the person you want to say hello to.
* #attribute name
* #type string
* #default "Polymer Dev"
*/
name: 'Polymer Dev',
ready: function() {
console.log(this.offsetWidth);
}
});
</script>
</polymer-element>

Use <paper-material> element within custom element

I'm playing around with the Polymer Starter kit and am creating a nested custom element. I have an outer element that 'outputs' the inner element multiple times.
My issue is that the inner element (business-card) contains a <paper-material>. This element is not being affected by global styles. I know that Polymer adds a class of scoped-style to the element which ensures it can only affect the local DOM. Removing the scoped-style class in Dev Tools applies global styling.
How do I apply the styles from the standard <paper-element> to my nested element or include those same styles within my custom element.
Edit
It appears the my issue is that the styles within 'app-theme' are not applied to the internal element. I can get the desired outcome if I copy the <paper-element> styles, including media queries, plus follow the answer from #Zikes.
It seems against the modular nature of polymer to duplicate everything from an element when the element is already perfect. Am I missing something?
business-card.html
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-material/paper-material.html">
<dom-module id="business-card">
<style>
:host {
display: block;
}
</style>
<template>
<paper-material>
<content></content>
</paper-material>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'business-card'
});
})();
</script>
Any help much appreciated
Polymer protects element internals from document styles and vice-versa. This is CSS scoping and it's a prominent feature of Web Components.
It can seem problematic in simple examples, but it's generally very beneficial to component reuse that component styles don't bash each other, and that document styles don't unintentionally foul up a component.
Polymer Starter Kit is not ideally set up for using app-theme.html in other scopes, but one thing you can do is copy the style rules you want to use into a CSS file, and then import that CSS file in your element code as below. The import and styles are used efficiently (e.g., the import is only loaded once, even if you use it in multiple elements).
<dom-module id="business-card">
<link rel="import" type="css" href="theme-styles.css">
<style>
:host {
display: block;
}
</style>
<template>
<paper-material>
<content></content>
</paper-material>
</template>
<script>
Polymer({
is: 'business-card'
});
</script>
</dom-module>
Live example: http://jsbin.com/hojajo/edit?html,output
If you'd like to apply the paper-material effects to your element directly, you can do so like this:
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../paper-styles/shadow.html">
<dom-module id="business-card">
<style>
:host {
display: block;
position: relative;
#apply(--shadow-transition);
}
:host([elevation="1"]) {
#apply(--shadow-elevation-2dp);
}
:host([elevation="2"]) {
#apply(--shadow-elevation-4dp);
}
:host([elevation="3"]) {
#apply(--shadow-elevation-6dp);
}
:host([elevation="4"]) {
#apply(--shadow-elevation-8dp);
}
:host([elevation="5"]) {
#apply(--shadow-elevation-16dp);
}
</style>
<template>
<content></content>
</template>
</dom-module>
<script>
Polymer({
is: 'business-card',
properties: {
/**
* The z-depth of this element, from 0-5. Setting to 0 will remove the
* shadow, and each increasing number greater than 0 will be "deeper"
* than the last.
*
* #attribute elevation
* #type number
* #default 1
*/
elevation: {
type: Number,
reflectToAttribute: true,
value: 1
},
/**
* Set this to true to animate the shadow when setting a new
* `elevation` value.
*
* #attribute animated
* #type boolean
* #default false
*/
animated: {
type: Boolean,
reflectToAttribute: true,
value: false
}
}
});
</script>
This is copied from the paper-material code itself: https://github.com/PolymerElements/paper-material/blob/master/paper-material.html

Polymer 1.0: How to style distributed nodes with #apply?

We have a custom element that is making an AJAX call to fetch some html generated on the server side and then injected into its light dom via Polymer.dom(this).innerHTML. The response coming from the server has another custom element in it that exposes a CSS property for theming purposes. On the main page, we're setting the value for the property, but it doesn't appear to be working. How do we get Polymer to style dynamically added light DOM elements that are distributed by another element.
index.html
<style is="custom-style">
x-bar {
--mixin-property: {
background: red;
};
}
</style>
...
<body>
<x-baz>
</x-baz>
</body>
x-baz.html
<dom-module id="x-baz">
<template>
<x-foo></x-foo>
</template>
</dom-module>
<script>
Polymer({
is: "x-baz"
});
</script>
x-foo.html
<dom-module id="x-foo">
<template>
<iron-ajax auto url="..." last-response="{{response}}"></iron-ajax>
<content></content>
</template>
</dom-module>
<script>
Polymer({
is: "x-foo",
properties: {
response: {
type: String,
obeserver: 'responseChanged'
}
},
responseChanged: function(newVal)
Polymer.dom(this).innerHTML = newVal;
}
});
</script>
x-bar.html
<dom-module id="x-bar">
<style>
.elementToStyle {
#apply(--mixin-property);
}
</style>
<template>
<div class="elementToStyle">
...
</div>
</template>
</dom-module>
</script>
Polymer({
is: "x-bar"
});
</script>
The iron-ajax call returns <x-bar> ... </x-bar>.
I would expect the div inside the x-bar that comes back from the AJAX response to have a red background, but it doesn't seem to be working. What do we need to adjust to make this work correctly?
Thanks in advance!

Paper-toast not showing

I am trying to display a toast when an ajax request fails using core-ajax and paper-toast elements. I created an handler that calls show on the paper-toast element. However it is still not showing...
What am I doing wrong?
Is there a better way to do that? (Maybe having the same toast element for all the application messages)
Here it follows my custom element code:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/core-ajax/core-ajax.html">
<link rel="import" href="../bower_components/paper-toast/paper-toast.html">
<polymer-element name="fd-rest-element-service" attributes="fditems fdtype">
<template>
<style>
:host {
display: block;
}
paper-toast {
z-index: 1000;
bottom: 40px;
left: 10px;
}
</style>
<paper-toast id="toast" text="There was a problem loading {{fdtype}} data.">
</paper-toast>
<core-ajax id="ajax"
auto on-core-error="{{errorHandler}}"
url="https://wrong.url.com:9113/{{fdtype}}/"
disabled-on-core-response="{{elementsLoaded}}"
response="{{fditems}}"
handleAs="json" withCredentials >
</core-ajax>
</template>
<script>
Polymer('fd-rest-element-service', {
fdtype:'environments',
created: function() {
this.fditems = [];
},
elementsLoaded: function() {
// Make a copy of the loaded data
console.log(this.fdtype +" : "+ this.$.ajax.response);
this.fditems = this.$.ajax.response.slice(0);
},
errorHandler: function(event){
console.log(event);
console.log(this.$.toast);
this.$.toast.show();
}
});
</script>
</polymer-element>
Since I have got no console error and the logged objects are as expected I believe the problem arises because the element is used inside an element managed by core-animated-pages that is not displayed. Any suggestion on how to create a shared toast element that can be accessed by the other elements in my application?
I ended up creating the paper-toast element inside the outmost element and then pass it through the children element via a toast attribute.
Here it follows some sample code. In my root element I created a paper-toast element referenced by id and share "top-down" in the other inner elements.
<paper-toast
id="toast"
text="There was a problem loading data.">
</paper-toast>
<fow-login user="{{user}}" userPhoto="{{userPhoto}}"
class="loginButton"
toast="{{$.toast}}"
token="{{token}}">
</fow-login>
In my inner element I use it like this:
<polymer-element name="fow-login" attributes="toast user userPhoto globals appID token">
...
<script>
...
loginFail: function(event){
console.log("Error:", event);
if(this.toast){
this.toast.text="There was a login problem.";
this.toast.show();
}
},
...
</script>
</polymer-element>