I want to create my own <menu-item> element to remove some boilerplate code. I wrapped <paper-icon-item> in my own element in the following way:
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/paper-item/paper-icon-item.html">
<dom-module id="menu-item">
<template>
<paper-icon-item>
<iron-icon icon="[[icon]]" item-icon></iron-icon>
<content></content>
</paper-icon-item>
</template>
</dom-module>
<script>
Polymer({
is: "menu-item",
properties: {
icon: { type: String }
}
});
</script>
The problem when using my <menu-item> is, however, that it behaves differently as to writing the template code directly in the HTML file. I suspect that the item is missing some interaction capabilities with the menu to function properly, but I can't figure it out. I tried using behaviors Polymer.IronControlState and Polymer.IronButtonState just like the paper item does, but no luck.
What am I missing to make my <menu-item> behave like a regular <paper-item>?
Answer
Following code is largely copied from the paper-icon-item.html.
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/iron-behaviors/iron-control-state.html">
<link rel="import" href="bower_components/iron-behaviors/iron-button-state.html">
<link rel="import" href="bower_components/iron-flex-layout/iron-flex-layout.html">
<link rel="import" href="bower_components/paper-styles/typography.html">
<link rel="import" href="bower_components/paper-styles/color.html">
<link rel="import" href="bower_components/paper-styles/default-theme.html">
<link rel="import" href="bower_components/paper-item/paper-item-behavior.html">
<link rel="import" href="bower_components/paper-item/paper-item-shared-styles.html">
<link rel="import" href="bower_components/iron-icon/iron-icon.html">
<link rel="import" href="bower_components/iron-icons/iron-icons.html">
<dom-module id="menu-item">
<template>
<style include="paper-item-shared-styles"></style>
<style>
:host {
#apply(--layout-horizontal);
#apply(--layout-center);
#apply(--paper-font-subhead);
#apply(--paper-item);
#apply(--paper-icon-item);
}
.content-icon {
width: var(--paper-item-icon-width, 56px);
#apply(--layout-horizontal);
#apply(--layout-center);
}
</style>
<div id="contentIcon" class="content-icon">
<iron-icon icon="{{ico}}" item-icon></iron-icon>
</div>
<content></content>
</template>
<script>
Polymer({
is: 'menu-item',
properties: {
ico: {
type: String,
value: "icons:stars"
}
},
behaviors: [
Polymer.PaperItemBehavior
]
});
</script>
</dom-module>
On Extending Elements
I think the simplest way of changing the template of an element is by just copying it, modifying the template and its name. This approach is not without problems, it becomes very difficult to keep the new element up to date with changes in the original one. I am really craving for a better solution!.
On Using Behaviors
You should try to avoid consuming both an element and a behavior used by that element at the same time, you might get into trouble. For example the reason your menu-item doesn't look proper is because the paper-item inside it doesn't acquire the attribute focused. The paper-item doesn't acquire that particular attribute is because its parent element menu-item has the focus, menu-item has the focus because both menu-item and paper-item have tabindex="0" set on them. Why do both these elements have it set on them?, it is because of Polymer.IronControlStateBehavior in both of them.
Related
I want to import an element that contains a paper-fab and have the paper-fab overlap the seam between the app-header element and the imported element. (In this case, I'm calling the imported element a fab-element). In other words, I simply want the paper-fab to "float" (as advertised and like it's supposed to).
What I expect it to look like:
jsBin
What it actually looks like:
FAB inside app-toolbar. (works) Click here for jsBin.
FAB outside app-toolbar but inside app-header. (works) Click here for jsBin.
FAB outside app-header and inside main-content. (fails) Click here for jsBin.
I need to use the app-header-layout element and import the fab-element inside the main content section. As the above 3 jsBins show, that last combination seems to break the element (causing the top half of the paper-fab to hide underneath the app-toolbar).
How do I get the entire paper-fab to float over the app-toolbar while using the fab-element inside the main content section of the app-header-layout?
Or does this potentially expose a possible bug in app-header-layout element itself?
Edit
z-index (on paper-fab) has no effect.
Notice the last example has the z-index increased to 99999. It still seems to have no effect on the output.
Edit 2
z-index (on parent element) has no effect.
Also, setting the z-index on the parent element fab-element also has no impact on the result.
Edit 3
I wonder if what's happening with the z-index described in this question (read the comments) is related?
Per #robodna on Polymer Slack Site provides the following answer:
Set z-index:2!important on #contentContainer.
See this jsBin.
http://jsbin.com/mitegigose/edit?html,output
<!doctype html>
<head>
<meta charset="utf-8">
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import">
<link href="iron-icon/iron-icon.html" rel="import">
<link href="iron-icons/iron-icons.html" rel="import">
<link href="paper-icon-button/paper-icon-button.html" rel="import">
<link href="app-layout/app-drawer/app-drawer.html" rel="import">
<link href="app-layout/app-drawer-layout/app-drawer-layout.html" rel="import">
<link href="app-layout/app-header-layout/app-header-layout.html" rel="import">
<link href="app-layout/app-header/app-header.html" rel="import">
<link href="app-layout/app-toolbar/app-toolbar.html" rel="import">
<link href="paper-fab/paper-fab.html" rel="import">
</head>
<body>
<dom-module id="fab-element">
<template>
<style>
paper-fab {
position: absolute;
right: 40px;
top: 40px;
z-index: 99999;
}
</style>
<paper-fab icon="add"></paper-fab>
</template>
<script>
(function(){
Polymer({
is: "fab-element",
properties: {},
});
})();
</script>
</dom-module>
<dom-module id="x-element">
<template>
<style>
app-toolbar {
color: white;
background-color: #3F51B5;
z-index: 1;
}
app-header-layout ::content #contentContainer {
z-index: 2!important;
}
fab-element {
z-index: 99999;
}
</style>
<app-header-layout>
<app-header fixed condenses effects="waterfall">
<app-toolbar>
<div main-title>App name</div>
</app-toolbar>
</app-header>
<div>
main content
<fab-element></fab-element>
</div>
</app-header-layout>
</template>
<script>
(function(){
Polymer({
is: "x-element",
properties: {},
});
})();
</script>
</dom-module>
<x-element></x-element>
</body>
I am starting to use Polymer 1.0: the only thing I tried is a simple template like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>
<script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="bower_components/polymer/polymer.html"></link>
<link rel="import" href="bower_components/iron-icons/iron-icons.html">
<title>Polymer test1</title>
</head>
<body unresolved>
<dom-module id="pres-test">
<template>
<content></content>
<p>This is my name:<h3>{{name}}</h3></p>
<iron-icon icon="star" hidden="{{!star}}"></iron-icon>
<img src="http://placehold.it/300x100"></img>
</template>
</dom-module>
<script>
Polymer({
is:'pres-test',
properties:{
name:String,
star:Boolean
}
});
</script>
<pres-test name="withStar" star>
<h1>Example1:</h1>
<img src="http://placekitten.com/g/200/180" alt="image"></img>
</pres-test>
<pres-test name="withoutStar">
<h2>Example:</h2>
<img src="http://placesheen.com/100/100"></img>
<p>No star icon :()</p>
</pres-test>
</body>
</html>
This code works fine on Chrome and Opera, except that even if I don't put the boolean star in pres-test, it still shows the star.
In Firefox and IE, it just shows the h1 and img in the pres-test.
In Safari it seems that it doesn't understand the tags like dom-module, template or pres-test, since it first displays what is in the dom-module, then what is in pres-test, without adapting to the variables.
I looked for the compatibility of Polymer, but I only found it for the version 0.5.
Am I doing something wrong, or is it just not compatible with these browsers?
Only Chrome supports having custom element definitions inline in your main document. Other browsers currently do not have full and proper implementations of the new and upcoming standard.
Take the pres-test element definition and move it into its own HTML file, then import it.
Also, you only need to import one of the webcomponents.js polyfills - and for Polymer 1.0, you'll want to use webcomponents-lite.js.
All said and done you'll have two files:
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="pres-test.html">
<title>Polymer test1</title>
</head>
<body unresolved>
<pres-test name="withStar" star>
<h1>Example1:</h1>
<img src="http://placekitten.com/g/200/180" alt="image"></img>
</pres-test>
<pres-test name="withoutStar">
<h2>Example:</h2>
<img src="http://placesheen.com/100/100"></img>
<p>No star icon :()</p>
</pres-test>
</body>
</html>
pres-test.html:
<link rel="import" href="components/polymer/polymer.html">
<link rel="import" href="components/iron-icons/iron-icons.html">
<dom-module id="pres-test">
<template>
<content></content>
<p>This is my name:<h3>{{name}}</h3></p>
<iron-icon icon="star" style$="{{getStarStyle(star)}}"></iron-icon>
<img src="http://placehold.it/300x100"></img>
</template>
</dom-module>
<script>
Polymer({
is:'pres-test',
properties:{
name: {
type: String
},
star: {
type: Boolean,
value: false
}
},
getStarStyle: function(star) {
return star ? '' : 'display: none';
}
});
</script>
I'm having a lot of trouble getting a core-pages element to have a non-zero height within my custom element. What is the best practice for having the core-pages height be the same as its selected content. Here's a trivial example which clearly breaks:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Polymer</title>
</head>
<body>
<script src="http://www.polymer-project.org/platform.js"></script>
<link rel="import" href="http://www.polymer-project.org/components/polymer/polymer.html">
<link rel="import" href="http://www.polymer-project.org/components/core-pages/core-pages.html">
<polymer-element name="x-foo">
<template>
<core-pages id="pages" selected="{{selected}}">
<content></content>
</core-pages>
</template>
<script>
Polymer('x-foo', {
ready: function() {
this.selected = 0;
}
});
</script>
</polymer-element>
<polymer-element name="x-bar">
<template>
<div><content></content></div>
</template>
<script>
Polymer('x-bar', {});
</script>
</polymer-element>
<p>BEFORE</p>
<x-foo>
<x-bar>some text here</x-bar>
<x-bar>some other text here</x-bar>
</x-foo>
<p>AFTER</p>
</body>
</html>
And the jsbin to see the results: http://jsbin.com/xowoxakuwu/1/edit (notice how the core pages content overlaps with the next element)
This example shows a core-pages element within a custom element. The content that gets injected into the core-pages are also custom elements.
Whats the best practice here?
You can apply a style to the currently selected page in the x-foo element which sets display: block and position: relative so x-bar will inherit the height of it's content.
I've also added the "block" attribute to the x-foo element so it too inherits the height of the selected page. Other general attributes are here -> https://www.polymer-project.org/docs/polymer/layout-attrs.html#general-purpose-attributes
<script src="http://www.polymer-project.org/platform.js"></script>
<link rel="import" href="http://www.polymer-project.org/components/polymer/polymer.html">
<link rel="import" href="http://www.polymer-project.org/components/core-pages/core-pages.html">
<polymer-element name="x-foo" block>
<template>
<style>
::content > .core-selected {
position: relative;
display: block;
}
</style>
<core-pages id="pages" selected="{{selected}}">
<content></content>
</core-pages>
</template>
<script>
Polymer('x-foo', {
ready: function() {
this.selected = 0;
}
});
</script>
</polymer-element>
<polymer-element name="x-bar">
<template>
<div>
<content></content>
</div>
</template>
<script>
Polymer('x-bar', {});
</script>
</polymer-element>
<p>BEFORE</p>
<x-foo>
<x-bar>some text here</x-bar>
<x-bar>some other text here</x-bar>
</x-foo>
<p>AFTER</p>
with Polymer it's easy to extend another web component. You can use <shadow> to mark the place in the child's template where the shadow dom of the Parent should be (polymer docs).
I'm looking to extend an element, but in a way that a specific part of child gets wrapped by the parent. This kind of setup I've used with some template engines. Can this be done with html includes?
Parent ::
<link rel="import" href="/bower_components/polymer/polymer.html">
<polymer-element name="tpl-parent" noscript>
<template>
<section>
<content></content><!-- put the child template here -->
</section>
</template>
</polymer-element>
Child ::
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="parent.html">
<polymer-element name="tpl-child" extends="tpl-parent" noscript>
<template>
<shadow>
<p>whatever I put here should be "wrapped" by the _section_ in parent</p>
</shadow>
</template>
</polymer-element>
You can't do it declaratively like that. But I think you can leverage automatic node finding to move a child into the parent's section if it has an id.
<polymer-element name="x-foo">
<template>
<style>
#wrapper p { color: red; }
</style>
<section id="wrapper">
<p>Hello from x-foo</p>
</section>
</template>
<script>
Polymer({
});
</script>
</polymer-element>
<polymer-element name="x-bar" extends="x-foo">
<template>
<p id="foo">I was wrapped by x-foo</p>
<p>I was not wrapped</p>
<shadow></shadow>
</template>
<script>
Polymer({
domReady: function() {
this.$.wrapper.appendChild(this.$.foo);
}
});
</script>
</polymer-element>
<x-bar></x-bar>
Even though #robdodson's answer is completely correct, it kind of feels.. too "loose".
When discussing this further at work, somebody noted the principle of "Composition over inheritance". This led to me viewing this situation from a different (and I think better) perspective.
I ended up with this (also includes an example of passing arguments):
-- x-foo.html
<polymer-element name="x-foo" arguments="status">
<template>
<style>
#wrapper.valid ::content p { color: green; }
#wrapper.invalid ::content p { color: red; }
</style>
<section id="wrapper">
<p>x-bar will be put in the content tag below</p>
<content></content>
</section>
</template>
<script>
Polymer({
status: '',
statusChanged: function(oldVal, newVal) {
if (['valid', 'invalid'].indexOf(newVAl) === -1) {
this.$.wrapper.setAttribute('class', newVal);
}
}
});
</script>
</polymer-element>
-- x-bar.html
<link rel="import" href="x-foo.html" arguments="status">
<polymer-element name="x-bar">
<template>
<x-foo status="{{status}}">
<p>color me</p>
</x-foo>
</template>
<script>
Polymer();
</script>
</polymer-element>
-- index.html
<link rel="import" href="x-bar.html" arguments="status">
<x-bar></x-bar>
The only "disadvantage" is you have to pass on "status". But this feels better then having to append to a "floating" div#wrapper in the super class
I have code as follows:
<body unresolved>
<core-header-panel>
<core-toolbar layout horizontal center>
<h1 flex>Title</h1>
Users
Terms
</core-toolbar>
<div class="container" layout horizontal>
<core-input placeholder="Placeholder text here"></core-input>
</div>
</core-header-panel>
</body>
The problem is that my core-input component doesn't get rendered. The core-header panel and core-toolbar do, but the core-input doesn't. It gets a width and height of 0px. Even if I assign width and height to it, it renders with nothing inside. I'm loading all components using imports.
Am I missing something?
Imports are:
<link rel="import" href="../components/font-roboto/roboto.html">
<link rel="import" href="../components/core-header-panel/core-header-panel.html">
<link rel="import" href="../custom-components/admin-users.html">
<link rel="import" href="../components/core-input/core-input.html">
Chrome is version 38.
From the documentation:
Important: The core-header-panel will not display if its parent does not have a height.
Styling the core-header-panel with the appropriate height and width properties will display the placeholder. Live Demo available here.
Update your HTML imports to include core-toolbar:
<link rel="import" href="../components/font-roboto/roboto.html">
<link rel="import" href="../components/core-header-panel/core-header-panel.html">
<link rel="import" href="../components/core-toolbar/core-toolbar.html">
<link rel="import" href="../components/core-input/core-input.html">
And add this styling on the same page as your HTML imports:
<style>
core-header-panel {
width: 360px;
height: 400px;
}
</style>
Replace
<core-input placeholder="Placeholder text here"></core-input>
with
<input is="core-input" placeholder="Placeholder text here">