In polymer, we can create insertion points for HTML content with <content> within custom modules like this.
<my-custom-element>
Surrounding HTML ...
<content></content>
Surrounding HTML ...
</my-custom-element>
and then later use the module like this:
<my-custom-element>foo content</my-custom-element>
Can a custom element support multiple of these <content>-based insertion points? And how?
You can use standard selection of elements from <template> using the <content>'s select attribute with a CSS selector to get different inserted elements.
<template>
Name : <content select="h2"></content><br>
Mail : <content select="#mail"></content>
</tempalte>
<my-custom-element>
<h2>Some One</h2>
<span id="mail">email#internet.com</span>
</my-custom-element>
Related
Background: this question relates to development of an extension for recent versions of Chrome. It relies on javascript features such as HTML imports and custom elements that are not available on all browsers but that's OK for this case.
I'm trying to implement a HTML custom element simplified as follows:
<custom-el>
<span slot="head">Great</span>
<span slot="item">Item one</span>
<span slot="item">Item two</span>
<span slot="foot">done</span>
</custom-el>
I register the <custom-el>. Each time the element is created, my code's custom element class attaches a shadow root and appends to the shadow root content from the following template:
<template id="main">
<h1><slot name="head"></slot></h1>
<ul>
<slot name="item"></slot>
</ul>
<i><slot name="foot"></slot></i>
</template>
I would like to redistribute each <span> with attribute slot="item" to a secondary template responsible for rendering an individual item:
<template id="sub">
<li><slot name="item"></slot></li>
</template>
The number of slots with attribute name="item" is not fixed. It is generated from a database and changes regularly.
I understand that a slot can be redistributed by attaching a shadowRoot to the slot's parent element and setting the slot's slot attribute, e.g. <slot name="item" slot="newItem">. But I don't think this will work in my case since the sub template needs to wrap each item instance, not the list of items.
I could attach shadow roots and sub templates to each item in the main document. This would work but my preference is that the main template import and apply any nested shadowRoots and templates. This way, the primary document need only import the file containing the main template. The implementation of the component's details is encapsulated in the main template html file.
I could also use the slotchange event and the HTMLSlotElement.assignedNodes method to cobble together a scripting solution. But I'd rather not go that way.
Is there another approach? My actual use case involves a more complex HTML structure. Or maybe my architecture or understanding of web components is flawed?
The simplest approach, if possible is to add items with the <li> tag in the light DOM.
<span slot=item><li>Item 1</li></span>
<span slot=item><li>Item 2</li></span>
This way you don't need to use Javascript to generate the <li> tags.
I was contemplating some code which would wrap each child of of the web component. Something like:
<dom-module id=test">
<template>
<template is="dom-repeat" items="{{contents.children}}">
<div>
{{item}}
</div>
</template>
</template>
</dom-module>
so what would happen is if i did something like:
<test>
<span>foo</span>
<span>bar</span>
</test>
it would kick out:
<test>
<div><span>foo</span></div>
<div><span>bar</span></div>
</test>
I was looking over ways i could do this, but i am having issues trying to create the dom-repeat correctly since contents.children isnt a real property.
Edit i was looking at what attributes are allowed for content tags, which is select plus all globals.
So in theory, i could assign an ID and then get the children of it?
<content id="myContent"></content>
and then in the dart say:
ContentElement get _content => $['myContent'];
#property HtmlElement children = [];
a.created(): super.created(){
set("children", _content.children);
}
and then mark the template accordingly?
<template is="dom-repeat" items="{{children}}">
...
</template>
It looks like you would like to wrap certain elements and add some new functionality to them.
The simplest (and most Polymer-ish) way to do that, is to introduce another custom element, which is (only) responsible for the wrapping.
Your test element (which, by the way, has an incorrect name - the name must contain a dash) would then look like this:
<dom-module id=test-element">
<template>
<content></content>
</template>
</dom-module>
(I fixed the naming according to convention. Renamed from test to test-element.)
And the newly created wrapper element (named wrapper-element) should look something like this:
<dom-module id=wrapper-element">
<template>
<div><content></content></div>
</template>
</dom-module>
Everything put together, the usage would be something like this:
<test-element>
<wrapper-element><span>foo</span></wrapper-element>
<wrapper-element><span>bar</span></wrapper-element>
</test-element>
With this, you get a clean solution, where you don't even need any JS code for the wrapping logic.
Question
Which method of placing the <script> tags is "best-practice?"
Inside the <dom-module>?
or
Outside the <dom-module>?
Also, please answer:
Why?
What is the source of your answer?
What downside risks are there by doing it the "wrong" way?
Polymer Starter Kit: OUTSIDE
In the Polymer Starter Kit, the my-list.html and my-greeting.html files place the <script> tag outside the <dom-module>.
Like this:
<dom-module>
<style>...</style>
<template>...</template>
<dom-module>
<script>...</script>
Other Experts: INSIDE
However, I have heard and seen several examples from Google employees and Google developers that suggest the <script> tags should go inside the <dom-module>.
Like this:
<dom-module>
<style>...</style>
<template>...</template>
<script>...</script>
<dom-module>
The correct answer is - it shouldn't matter. While the documentation is indeed as #Mowzer noted, this is just an example rather than a definition. At least some actual Polymer elements like e. g. iron-image have it outside dom-module.
The relationship between the dom-module and the object Polymer constructor defines is established through the 'is' property of the object passed to the Polymer constructor and the id attribute of the dom-module.
From Local DOM guide:
Give the <dom-module> an id attribute that matches its element’s is property and put a inside the <dom-module>. Polymer will automatically clone this template’s contents into the element’s local DOM.
As a side note, you can also successfully use <script src="external.js"></script> to separate the html from the JS - I'm just guessing this is one possible reason for this question. The only drawback to this (AFAIK) is that in this case a vulcanized version of your element will show incorrect (offset) code line numbers for JS errors.
Looks like <script> tags should go inside the <dom-module>.
Per this definition in the developer guide.
Element definition
<dom-module id="element-name">
<template>
<style>
/* CSS rules for your element */
</style>
<!-- local DOM for your element -->
<div>{{greeting}}</div> <!-- data bindings in local DOM -->
</template>
<script>
// element registration
Polymer({
is: "element-name",
// add properties and methods on the element's prototype
properties: {
// declare properties for the element's public API
greeting: {
type: String,
value: "Hello!"
}
}
});
</script>
</dom-module>
I'm tinkering around with Polymer again and there is something I want to do but it doesn't work and the workaround looks so messy. I want to style an element but take advantage of Data Binding. Basically I try to do this:
<dom-module id="proto-element">
<template>
<style>
#wrapper{
width:{{pixels}};
background: #e1e1e1;
}
#bar{
width:80%;
}
</style>
<div id="wrapper">
<div id="bar" style$={{barStyle}}>I'm the bar! <b>{{test}}</b></div>
</div>
</template>
</dom-module>
<script>
Polymer({
is: "proto-element",
ready: function() {
this.pixels = "300px";
this.test = "Fear me!"
}
});
</script>
The text bound to this.test works flawlessley. But the moustache tags that are inside of style get ignored. Is there a simple solution to this? Because using the inline style$= syntax is super messy when you are working with multiple css rules and also you always need to concatinate a string for it to work if the value you want is obtained elsewhere. Any ideas on a nice clean solution?
I'm afraid what you want is (at least currently) not possible. Data-binding works only inside <template> tags, either a <template is="auto-binding"> or the template inside a <dom-module>.
In the <style> tag, data-binding simply won't work. You could try putting another <style> tag INSIDE the <template> tag (which is messy), but you still won't get the data-bound property to work, since the curly brackets {{...}} must be inside an individual tag and can't be (currently) surrounded by whitespaces (spaces, newlines, ...), see docs:
String concatenation is not supported inside a tag, and the tag can’t contain any whitespace ...
tl;dr: There are two issues that prevent you from achieving what you want: 1) Data-binding only works inside a template tag and 2) if you want to print out data-bound properties, they must be enclosed inside a html tag.
You could try a different approach:
ready: function () {
this.$.wrapper.style.width = '300px';
}
this.$ allows you to easily access any DOM element with an id attribute (for example your #wrapper, which is accessed as this.$.wrapper) and from there, you can set any other attribute of the element (this.$.wrapper.style.width in this case).
EDIT: Also, I just noticed that you've actually put <style> tag inside the <template>, which is not advised. Since Polymer 0.8, this is the recommended structure for a custom element:
<dom-module>
<style> ... </style>
<template> ... </template>
</dom-module>
<script> ... </script>
Check out the Overview and Migration guide.
EDIT 2: As of Polymer 1.1 the suggested element structure (as pointed out by Max Waterman) has been updated to:
<dom-module>
<template>
<style> ... </style>
...
</template>
<script> ... </script>
</dom-module>
It is documented that you can override Polymer's data binding default behavior of escaping HTML by using the injectBoundHTML method.
That's fine for some use cases, but is there a way to prevent HTML escaping for just standalone templates?
For example, I have a template in the <body> of my HTML page that looks like this; how can I prevent the HTML of the myHTMLsnippet value from being escaped?
<template is="auto-binding">
<div>{{myHTMLsnippet}}</div>
</template>
I was able to find a workaround by nesting the juicy-html element inside the auto-binding template.
<template is="auto-binding">
<div>
<template is="juicy-html" content="{{myHTMLsnippet}}"></template>
</div>
</template>