What's the Polymer 1.0 equivalent to injectBoundHTML()?
(i.e. appending HTML strings to nodes within a Polymer element and having data bindings resolve)
A JSbin example - http://jsbin.com/jufase/edit?html,output
EDIT: don't have enough SO cred to accept my own answer yet, but it should be down below somewhere. TL;DR - use "dom-bind" templates
Although as techknowledgey pointed out it's not really supported well yet. The following seems to do the trick.
function injectBoundHTML(html, element) {
var template = document.createElement('template', 'dom-bind');
var doc = template.content.ownerDocument;
var div = doc.createElement('div');
div.innerHTML = html;
template.content.appendChild(div);
while (element.firstChild) {
element.removeChild(element.firstChild);
}
element.appendChild(Polymer.Base.instanceTemplate(template));
}
If your HTML was already parsed then use something like "doc.importNode(sourceNode, true);" instead of getting/setting innerHTML.
Looks like this is not really a supported feature yet, looking at the comments from #kevinpschaaf:
https://github.com/Polymer/polymer/issues/1778
Using dom-bind, I should be able to satisfy my use case, e.g. http://jsbin.com/caxelo/edit?html,output
Bindings are to properties by default, and hyphens can be used to denote capitalizations:
<element inner-h-t-m-l="{{prop}}"></element>
Thanks guys for the prototype that I updated for my own needs : Generate markup in polymer, as dom-repeat was unable to perform this operation.
Tags for search engines :
Polymer Generation dynamically dynamic markup custom element dom-repeat dom repeat balise dynamique dynamiquement
http://jsbin.com/wiziyeteco/edit?html,output
<!doctype html>
<html>
<head>
<title>polymer</title>
<script src="https://rawgit.com/webcomponents/webcomponentsjs/master/webcomponents-lite.js"></script>
<link rel="import" href="http://polygit.org/components/paper-button/paper-button.html">
</head>
<body>
<dom-module id="x-test">
<template>
<div id="container"></div>
</template>
</dom-module>
<script>
Polymer({
is: 'x-test',
ready: function() {
// Declare custom elements
var customElements = [
{name:'paper-button', title:'A'},
{name:'paper-button', title:'B'},
{name:'paper-button', title:'C'},
{name:'paper-button', title:'D'},
];
// Declare auto-binding, as we are at the root HTML document
var domBind = document.createElement('template', 'dom-bind');
domBind.customElements = customElements;
var domBindDocument = domBind.content.ownerDocument;
// Declare custom elements
for (var i in domBind.customElements) {
var item = domBind.customElements[i];
var elem = domBindDocument.createElement(item.name);
elem.setAttribute('raised', 1);
elem.innerHTML = item.title;
domBind.content.appendChild(elem);
}
// Append to #container
this.$.container.appendChild(domBind);
}
});
</script>
<x-test></x-test>
</body>
</html>
Related
This question already has an answer here:
Nested element (web component) can't get its template
(1 answer)
Closed 3 years ago.
I was trying to understand how web components work so I tried to write a small app that I served on a webserver (tested on Chrome which support rel="import"):
index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="import" href="my-app.html" />
</head>
<body>
<my-app />
</body>
</html>
my-app.html:
<template id="template">
<div>Welcome to my app!</div>
</template>
<script>
class MyApp extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: "open"});
const template = document.getElementById("template");
const clone = document.importNode(template.content, true);
shadow.appendChild(clone);
}
}
customElements.define("my-app", MyApp);
</script>
But it doesn't seem to work. The <my-app /> tag is not rendered at all in the DOM and I get this error on the console:
Uncaught TypeError: Cannot read property 'content' of null
What cannot I retrieve the template node? What am I doing wrong?
What I would also like to know is if I am allowed to write an HTML document without the boilerplate code (doctype, head, body, ...), because it's meant to describe a component and not an entire document to be used as is. Is it allowed by the HTML5 specs and/or is it correctly interpreted by a majority of browsers?
Thank you for your help.
While inside the template, don't use the document global:
<template id="template">
<div>Welcome to my app!</div>
</template>
<script>
class MyApp extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: "open"});
// while inside the imported HTML, `currentDocument` should be used instead of `document`
const currentDocument = document.currentScript.ownerDocument;
// notice the usage of `currentDocument`
const template = currentDocument.querySelector('#template');
const clone = document.importNode(template.content, true);
shadow.appendChild(clone);
}
}
customElements.define("my-app", MyApp);
</script>
Plunker demo: https://plnkr.co/edit/USvbddEDWCSotYrHic7n?p=preview
PS: Notes com compatibility here, though I assume you know HTML imports are to be deprecated very soon.
I have some JavaScript in which I set a global variable to hold the function document.getElementById. In a function in the same file, I then try to use that variable, along with the id of an HTML paragraph element, to write to the innerHTML property. However, in the IE11 console, I get the error "SCRIPT65535: Invalid Calling Object". Explicitly writing document.getElementByID("someid").innerHTML = "value" works. Here are the key parts of the code (all in the same file).
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="name1"></p>
<script>
var objDocGEBI = document.getElementById;
function writeData() {
if (true) {
objDocGEBI("name1").innerHTML = "value";
}
}
</script>
</body>
</html>
Your problem is to do with function binding.
The short version, is you need to bind the function to the document like so:
var objDocGEBI = document.getElementById.bind(document);
This will make sure that it is correctly bound to the document without actually running the function. Once you fix this line, you should find that the rest of your code above works as intended.
Because, you need to call your function for executing the innerHTML function.
And you cannot using your syntax. You need to write your document.getElementById; like that :
The first way :
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="name1"></p>
<script>
var objDocGEBI = document.getElementById('name1');
function writeData(){
if (true){
objDocGEBI.innerHTML = "value";
}
}
writeData(); // it's calling your function and execute your innerHTML
</script>
</body>
</html>
The second way :
// Example with IIFE
(() => {
var objDocGEBI = document.getElementById('name1');
if (true){
objDocGEBI.innerHTML = "value";
}
})()
This may be useful for you:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p id="name1"></p>
<p id="name2"></p> <!-- added, just for a 2nd example -->
<script>
function writeData(id, value){
document.getElementById(id).innerHTML = value;
}
writeData('name1', 'John');
writeData('name2', 'Peter');
</script>
</body>
</html>
It's much cleaner this way, and you won't have to keep that weird variable. ;)
It's because your variable objDocGEBI is storing a method/function in the syntax of an event listener, but it's not an event listener. Correct syntax would be:
function writeData(name) {
if (true) {
document.getElementById(name).value = "value";
}
}
I'd like to ask you people about workflow with Polymer. I know that I should use my own elements or double check if element that I need isn't alredy published. It's really nice, I admit it. However the Polymer Starter Kit comes as single-page app. Is it the recommended approach for using Polymer? What about large pages that would need a lot of data to be loaded? Are there alternative approaches?
You don't need to have all your elements rendered at same time. They can be created on the fly only when needed, and can be destroyed as well.
To create your elements on the fly, you can use DOM Manipulation methods like:
var myElement = document.createElement("my-element");
this.$.container.appendChild(myElement);
myElement.myProperty = "anything";
To remove, just do this way:
var myElement = this.$.container.querySelector("my-element");
myElement.parentNode.removeChild(myElement);
If you need dynamic load a HTML Import, you can use this.importHref if your code is inside a polymer Element (and it should be).
this.importHref('myElement.html', function(e) {
// Create your element here
});
Putting things together...
Suppose you have a polymer element like that:
<dom-module id="my-app">
<template>
<div id="container"></div>
<paper-button on-click="_loadElement">Load Element</paper-button>
<paper-button on-click="_removeElement">Remove Element</paper-button>
</template>
</dom-module>
<script>
Polymer({
is: 'my-app',
_loadElement: function() {
this.importHref('myElement.html', function(e) {
var myElement = document.createElement("my-element");
this.$.container.appendChild(myElement);
myElement.myProperty = "anything";
});
},
_removeElement: function() {
var myElement = this.$.container.querySelector("my-element");
myElement.parentNode.removeChild(myElement);
}
});
</script>
I'm trying to use core-list extension from polymer.
I used example from Google Developers from This YT Link
I add is="auto-binding" value to my template i go error:
Uncaught TypeError: Cannot set property 'data' of null
I'm also trying to set height: 100% to core-list , it's also not working , I got warning:
core-list must either be sized or be inside an overflow:auto div that is sized
Below I'm adding my code:
<link rel="import" href="../bower_components/paper-tabs/paper-tabs.html">
<link rel="import" href="../bower_components/core-list/core-list.html">
<link rel="import" href="../bower_components/core-pages/core-pages.html">
<link rel="import" href="../bower_components/core-ajax/core-ajax.html">
<!--{{hash}-->
<polymer-element name="progress-page" attributes="hash">
<template>
<template is="auto-binding" id="app" style=" height:100%; overflow: auto;">
<core-list data="{{data}}" flex style=" height:100%;">
<template>
<div class="row" layout horizontal center>
<div data-index="{{index}}">{{model.name}}</div>
</div>
</template>
</core-list>
</template>
</template>
<script>
Polymer('progress-page', {
ready: function () {
this.selected = 0;
var app = document.querySelector('#app');
app.data = generateContacts();
function generateContacts() {
var data = [];
for (var i = 0; i < 1000; i++) {
data.push({
name: 'dddd',
string: 'asd'
});
}
return data;
}
}
});
</script>
</polymer-element>
Cam somebody please tell me what's wrong ? This is 1:1 version from link I've added.
You shouldn't be using template is="auto-binding" inside a Polymer element. The whole point of an autobinding template is that it allows you to set up data binding outside a Polymer element.
So you're combining two concepts here. These two lines are what you'd use if you were using an auto-binding template in your main page:
var app = document.querySelector('#app');
app.data = generateContacts();
Here you're trying to retrieve a reference to the auto-binding template and add a data property to it.
This won't work in your context. '#app' is inside <progress-page>'s shadow DOM, so document.querySelector won't find it.
This line, on the other hand, is adding a property to the <progress-page> element, which is the proper thing to do if you're using core-list inside an element:
this.selected = 0;
So if you want to use an auto-binding template, eliminate the Polymer element and use the first approach (querySelector and assign app.data, app.selected).
If you want to create a Polymer element, eliminate the auto-binding template and use the second approach:
this.data = generateContacts();
this.selected = 0;
Here's a working version of this code with the auto-binding template removed:
http://jsbin.com/lofega/2/edit
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.