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
Related
I know that Polymer recommends to use style tag inside template tag since v1.1 but supports both. Can anyone tell me the advantages of doing so. If it is inertness then can you please give an example where keeping style tag outside template exposed it outside of shadow-dom
The 1.1 release notes indicate performance reasons:
Previously, we recommended that a <style> element should be placed inside an element's <dom-module> but outside of its template. This is still supported, but we've now optimized placing styles within the template itself, so having the <style> tag outside of the template will be slower.
If I read the code correctly, this is Polymer's procedure for parsing CSS:
Select child nodes that can contain CSS (including <style> and <template>).
For each node:
a. If the node is <template>, recurse on the node (go to step 1).
b. Else if the node is <style>, remove the node (to prevent style leak), and then append the node's text to the string buffer.
c. Else if the node is <link rel="import" type="css">, append its imported text to the string buffer.
Return the string buffer.
If all styles are parsed using this procedure, I don't understand how the placement of <style> would affect performance (maybe I'm missing something).
please give an example where keeping style tag outside template exposed it outside of shadow-dom
The <style> doesn't leak regardless of whether it's placed inside the <template> (because of step 2b above), as seen in the following demos.
<head>
<base href="https://polygit.org/polymer+1.5.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo></x-foo>
<div class="title">outside <x-foo> (should not be styled)</div>
<dom-module id="x-foo">
<style>
div.title {
font-family: Arial;
color: blue;
}
</style>
<template>
<div class="title">inside <x-foo></div>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-foo'
});
});
</script>
</dom-module>
</body>
codepen
<head>
<base href="https://polygit.org/polymer+1.5.0/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo></x-foo>
<div class="title">outside <x-foo> (should not be styled)</div>
<dom-module id="x-foo">
<template>
<style>
div.title {
font-family: Arial;
color: blue;
}
</style>
<div class="title">inside <x-foo></div>
</template>
<script>
HTMLImports.whenReady(function() {
Polymer({
is: 'x-foo'
});
});
</script>
</dom-module>
</body>
codepen
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>
Whatever I do, I can't seem to be able to style paper-elements using custom properties inside a custom element:
<dom-module id="ts-dashboard">
<style>
:host {
display: block;
--paper-tabs-selection-bar-color : #ED1C23;
}
paper-tabs {
background-color : #962E33;
}
</style>
<template>
<paper-tabs selected="{{selected}}">
<paper-tab>Choice 1</paper-tab>
<paper-tab>Choice 2</paper-tab>
</paper-tabs>
<!-- some more elements... -->
</template>
</dom-module>
<script>
//Module definition here
</script>
But the --paper-tabs-selection-bar-color is not taken into account, and I end up with the default yellow instead of bright red.
Notably, I use shadow-dom instead of shady-dom, but switching back to the shady implementation hasn't changed anything. I also use a theme file, as an html import, to set --default-primary-color and other custom properties of the sort. These seem to work though, inside the :root{ } css property, but even if I put --paper-tabs-selection-bar-color : #ED1C23; there it doesn't work either.
I have tried with paper-input-controller but the styles don't get applied either. Any idea what I'm doing wrong here?
I use import an external style sheet like:
<dom-module id="ts-dashboard">
<link rel="import" type="css" href="ts-dashboard.css">
<template>
<paper-tabs selected="{{selected}}">
<paper-tab>Choice 1</paper-tab>
<paper-tab>Choice 2</paper-tab>
</paper-tabs>
<!-- some more elements... -->
</template>
</dom-module>
<script>
//Module definition here
</script>
Then this should work:
paper-tabs {
--paper-tabs-selection-bar-color: #ED1C23;
}
(Update: Just realized I pasted that wrong. Fixed to what is in my external CSS file)
I've been trying to use Polymer for a project I'm working on. And although I enjoyed it quite a lot so far, I ran into a problem I just can't solve.
I dumped it down to a simple example. Basically it's just a list element (item-list) that contains item elements (item-card) and i want to parse the items position to the element via the attribute pos. But for some reason the items attribute is allways undefined! Is this because the attribute is bound to the variable i, which dies after the template repeat? If so, how do I work around it? Is there a different approach I should be using here?
SOLUTION: You can find the solution by reading through all the comments, but to sum it up: apperantly there was a timing issue and the attribute wasn't ready at the ready callback. But I found out about the domReady callback (polymer lifecycle documentation). Using domReady it works just fine! Thanks to Günter Zöchbauer for the help!
This is the item-list.html:
<link rel="import" href="components/polymer/polymer.html">
<link rel="import" href="item-card.html">
<polymer-element name="item-list">
<template>
<style>
</style>
<template repeat="{{values, i in data.data}}">
<item-card pos="{{i}}"></item-card>
</template>
</template>
<script>
Polymer({
created: function()
{
this.num = 123456;
this.data = { "data":
[
{
"value":999
},
{
"value":666
},
{
"value":555
},
{
"value":222
}
]
};
}
});
</script>
</polymer-element>
This is the item-card.html
<link rel="import" href="components/polymer/polymer.html">
<polymer-element name="item-card" attributes="pos">
<template>
<style>
</style>
</template>
<script>
Polymer({
ready: function()
{
console.log("ready: " + this.pos);
}
});
</script>
</polymer-element>
I didn't bother putting the index.html in, since it just containts one item-list element.
Thanks alot!!
I think you need a pos field in <item-card> in addition to the attributes="pos" declaration.
The repeated element also references the bound model which can be accessed like querySelector('item-card').templateInstance.model property.
See https://github.com/PolymerLabs/polymer-selector/blob/master/polymer-selector.html#L286 for an example.
Info:
According to the comments it turned out to be a timing issue. The value wasn't yet assigned when the ready callback was called but using domReady worked.
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>