I an trying a simple test about observeNodes Polymer facility. Essentially my code defines an observer for child node changes on the component.
<dom-module id="wc-A">
<template>
<div>Added Nodes : <span id="added"></span></div>
<div>Removed Nodes : <span id="removed"></span></div>
</template>
<script>
Polymer ({
is: 'wc-A',
ready: function () {
Polymer
.dom (this)
.observeNodes (function (nodes) {
console.log (nodes)
this.$.added.textContent = nodes.addedNodes.length;
this.$.removed.textContent = nodes.removedNodes.length;
});
}
});
</script>
</dom-module>
This example works properly on creation time (from my test span#added contains 5 and span#removed contains 0), but when I programmatically add/remove elements on the light DOM, the observation mechanism does not respond (span's do not change). This is my test:
<div>
<button id="btnAdd">New</button>
<button id="btnRemove">Remove</button>
</div>
<wc-A> <!-- (1) Fires observer -->
<div class="data">1</div>
<div class="data">2</div>
</wc-A>
<template id=template>
<div class="data">3</div>
</template>
<script>
HTMLImports.whenReady (function () {
document
.querySelector ('#btnAdd')
.addEventListener ('click', function (e) {
var template = document.querySelector ('#template').content;
var div = template.querySelector ('div');
var wcA = document.querySelector ('wc-A')
wcA.appendChild (div.cloneNode (true)); // (2) Does not fire observer
});
document
.querySelector ('#btnRemove')
.addEventListener ('click', function (e) {
var wcA = document.querySelector ('wc-A')
var child = wcA.querySelector ('.data');
if (child)
wcA.removeChild ( // (3) Does not fire observer
child
);
});
});
</script>
The complete code can be checked http://plnkr.co/edit/DHiH40T3pBLx9Nu6Tv3W?p=preview
What is my error? Thanks in advance.
You need to use Polymer.dom(this).appendChild instead of this.appendChild to make it work with Polymer 1.0 according to this:
https://github.com/Polymer/polymer/issues/3102
Related
I have the following html and Javascript. I want to have a set of buttons with a single onclick handler.
<div id="parent1">
<button data-idx="1" data-action="test">Click me</button>
<button data-idx="2" data-action="test">Click me</button>
</div>
<div id="parent2">
<button data-idx="1" data-action="test"><span>Click me</span></button>
<button data-idx="2" data-action="test"><span>Click me</span></button>
</div>
<script>
const parent1 = document.querySelector("#parent1");
const parent2 = document.querySelector("#parent2");
parent1.addEventListener("click", (evt) => {
let obj = evt.target.dataset;
console.log(obj);
});
parent2.addEventListener("click", (evt) => {
let obj = evt.path[1].dataset;
console.log(obj);
});
</script>
The code for parent1 works fine and I see {idx:"1", action:"test"}
The code for parent2 works as well, but only because in this example I know in advance the structure of the innerHtml of the buttons. In practise that is not known, and the hard-coded evt.path[1] is not suitable.
It is the case however that the dataset is always in the immediate child of #parent2
How can I reliably find the dataset? For example, can I get the index relative to #parent of the child that fired the event?
Maybe you should use composedpath instead of path: see event.path is undefined running in Firefox.
In generall you can loop over the path[n] and avoid undefined errors.
Or you can access the different variables by using your above evt.target.dataset[keyname].
A plugin I use creates dynamic html and I want to add a dynamic background-color using a hex passed via props.
This is the html in my component
<template>
<div class="pagination" slot="pagination"></div>
</template>
Generates dynamic HTML of this
<div class="pagination" slot="pagination">
<span class="swiper-pagination-bullet"></span>
<span class="swiper-pagination-bullet"></span>
</div>
The components receives props
props: ['primaryBgColor']
I can obviously see the color in the template if I write
<template>
<div>
{{ this.primaryBgColor }}
</div>
</template>
However when I write a style within the component like
<style>
.swiper-pagination-bullet {
background-color: {{ this.primaryBgColor }}
}
</style>
Webpack returns an error saying I have a CSS syntax error. Any ideas?
suresh's answer may not work as it listens to the Vue mounted event, but by the time Vue component mounted, the plugin element may not yet be been injected.
if the plugin provides a similar event, you can register the handler there. if not, you can listen to the outer dom element using MutationObserver instead.
<template>
<div id="outer">
</div>
</template>
<script>
const observer = new MutationObserver(mutations => {
// suresh's function here
});
observer.observe(document.getElementById('outer'), { childList: true });
</script>
In your template, you can directly inject style
<template>
<div :style="this.primaryBgColor">
{{ this.primaryBgColor }}
</div>
</template>
primaryBgColor should contain object like {'background':"#cccc"}
For more option, vuejs had superb documentation. you can refer https://v2.vuejs.org/v2/guide/class-and-style.html#Object-Syntax-1
We can query the element and apply style like as follows
mounted: function () {
var elems = document.querySelectorAll('.swiper-pagination-bullet ')
var index = 0
var length = elems.length
for (; index < length; index++) {
elems[index].style.backgroundColor = this.primaryBgColor
}
},
I have a Polymer <paper-collapse-item> inside a <div> inside another <div>. When the <paper-collapse-item> is opened, I want to change add another class to the <div class="container">. But Polymer tells me the function is undefined. Right now I have:
HTML:
<div class="container>
<h1>Header</h1>
<div class="box" onclick="[[_expandBox()]]">
<paper-collapse-item class="header" header="">
<p class="longText">Some long text</p>
</paper-collapse-item>
</div>
</div>
Script:
<script>
Polymer({
is: 'text-page',
_expandBox: function() {
var exp = this.getElementsByClassName("header")[0];
var expPar = this.getElementsByClassName("box")[0].parentNode;
if (exp.hasAttribute(opened)) {
expPar.className += " paropen";
}
}
});
</script>
So how I can I call the function properly and make it add a class to the container?
EDIT
I've made some changes to the entire setup for different reasons. Still left with this issue.
HTML:
<div class="container>
<h1>Header</h1>
<collapse-item opened="{{opened}}" on-tap="_expandBox(opened)"></collapse-item>
</div>
Script:
<script>
Polymer({
is: 'text-page',
_expandBox: function(opened) {
var exp = this.getElementsByClassName("container")[0];
if (opened) {
exp.className += " paropen";
}
}
});
</script>
This tells me that: listener method _expandBox(opened) not defined
Thanks for helping me learn. I'm new to Polymer.
Following Polymer Documentation, you have to write "on-" followed by gesture event type. You have to remove brackets within event handler too. In your case:
<div class="box" on-click="_expandBox">
EDIT:
On on-tap event declaration you must to add a listener, in this case on-tap="_expandBox" (without parameters). When the event tap occurs, an event object is passed automatically to the function as the first parameter:
_expandBox: function(event) {
console.log(event.detail);
}
Your code could be:
HTML:
<div class="container>
<h1>Header</h1>
<collapse-item id="collapseItem" on-tap="_expandBox" opened="[[_functionWithParamsThatChangesThis(param1, param2, ...)]]"></collapse-item>
</div>
Script:
<script>
Polymer({
is: 'text-page',
_expandBox: function() {
var exp = this.getElementsByClassName("container")[0];
if (this.$.collapseItem.opened) {
exp.className += " paropen";
}
}
_functionWithParamsThatChangesThis(param1, param2, ...) {
if (param1, param2, ...) return true;
return false;
}
});
</script>
I don't know how to make a disqus comments code to work inside of my custom elements.
Structure of my site:
| index.html
--------\ my-app.html (custom element)
----------------\ my-testView1.html (custom element)
----------------\ my-testView2.html (custom element)
I need to put disqus comments inside my-testView1.html and my-testView2.html
Structure of index.html:
<body>
<my-app>
<div class="disqusClass1" id="disqus_thread"></div>
<div class="disqusClass2" id="disqus_thread"></div>
<my-app>
</body>
Structure of my-app.html:
<iron-pages>
<my-testView1 name="testView"><content select=".disqusClass1"></content></my-testView1>
<my-testView2 name="testView2"><content select=".disqusClass2"></content></div></my-testView2>
</iron-pages>
Structure of my-testView1.html :
<template>
<content select=".disqusClass1"></content>
</template>
Structure of my-testView2.html :
<template>
<content select=".disqusClass2"></content>
</template>
The disqus div
I put the div of the disqus comments inside <my-app> on the index.html so that chrome could find it. It can't find it if I put it inside <my-testView1> like that:
page my-app.html
<iron-pages>
<my-testView1 name="testView"><div id="disqus_thread"></div></my-testView1>
<my-testView2 name="testView2"><div id="disqus_thread"></div></my-testView2>
</iron-pages>
because the my-app.html is a custom element itself and it won't let chrome to find it. So I had to put it outside of the shadow dom (the index.html page)
Javacript code on the pages my-testView1.html and my-testView2.htmllook like this:
<dom-module id="my-testView1">
<template>
...
<content></content>
...
</template>
<script>
Polymer({
is: 'my-testView1',
ready: function ()
{
// DEFAULT DISQUS CODE (I changed real URLs though):
var disqus_config = function () {
this.page.url = 'https://www.example.com/testView1'; // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = '/testView1';
// this.page.title = 'Test View';
};
(function() {
var d = document, s = d.createElement('script');
s.src = '//myChannelName.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
}
});
</script>
</dom-module>
Result
Comments appears only on one of these my-testView1.html my-testView2.html at the time. I need 1 disqus thread on my-testView1.html and another disqus thread on my-testView2.html
Maybe it's because of routing. Disqus console message says that I need to use ajax method https://help.disqus.com/customer/portal/articles/472107-using-disqus-on-ajax-sites Unfortunately I could not make it work when I replaced the javascript code above with the code from the example:
<script>
Polymer({
is: 'my-testView1',
ready: function ()
{
var disqus_shortname = 'myDisqusChannelId';
var disqus_identifier = '/testView1';
var disqus_url = 'http://example.com/testView1';
var disqus_config = function () {
this.language = "en";
};
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
var reset = function (newIdentifier, newUrl) {
DISQUS.reset({
reload: true,
config: function () {
this.page.identifier = newIdentifier;
this.page.url = newUrl;
}
});
};
}
});
</script>
</dom-module>
inside both custom elements (changing disqus_identifier and disqus_url correspondingly for each of them)
The error is due to the fact that the disqus library can't find the <div id="disqus_thread"> element.
It's because this element is inside a Shadow DOM (and that's why it works fine in Firefox which doesn't implement real Shadow DOM).
3 possible solutions:
Don't use Shadow DOM with your Polymer elements.
Don't put the #disqus_thread element in a Polymer element (insert it in the normal DOM).
Use <content> in your <template>, and the #disqus_thread element inside the polymer tag to make it availabe to the library:
In the custom elements:
<template>
//HTML code here
<content></content>
</template>
In the HTML page where you insert the custom element:
<my-app>
<my-testView>
<div id="disqus_thread"></div>
</my-testView>
</my-app>
The <div> will be revealed at the place where the (nested) <content> tags are placed.
I wanted to give another possible solution to using Disqus comments in Polymer. The main problem is the inability to use Disqus with elements in the shadow dom because they are hidden. So how can we make a shadow dom element visible? We can pass it down the component hierarchy from the index.html of the application.
To expose an element structure your html like this:
index.html
<polymer-app>
<div id="disqus_thread">
</polymer-app>
In Polymer App:
<dom-module id="polymer-app">
<template>
<slot></slot>
<template>
</dom-module>
Slot is where the #disqus_thread will show. And you can pass it further down to other components, inside polymer-app.
<dom-module id="polymer-app">
<template>
<my-page>
<slot></slot>
</my-page>
<template>
</dom-module>
Note: This is code is just an example. Don't try to copy and paste, it won't run.
I am trying to figure out the observeNodes() API in Polymer.
It's all straightforward (it's all explained in Observe added and removed children.
I created a really simple element:
<dom-module id="element-counter-1">
<template>
<p>This is the element counter! The result is: {{childCount}}</p>
<content id="c"></content>
</template>
<script>
Polymer({
is: 'element-counter-1',
attached: function(){
this.childCount = this.getEffectiveChildren().length;
this.anObserver = Polymer.dom( this.$.c ).observeNodes( function( info ) {
// Here, `this` is the element itself
var addedElements = info.addedNodes.filter(function(node) {
return (node.nodeType === Node.ELEMENT_NODE)
});
// Here, `this` is the element itself
var removedElements = info.removedNodes.filter(function(node) {
return (node.nodeType === Node.ELEMENT_NODE)
});
console.log("Changed!" , info, addedElements, removedElements );
});
}
})
</script>
</dom-template>
Now, if I use it like this:
<element-counter-1>
<p>one</p>
<p>two</p>
<p>three</p>
</element-counter>
The hook function will be called immediately with addedElements being the three <p> elements.
The documentation says:
The first callback from observeNodes contains all nodes added to the element, not the elements added since observeNodes was called. This works well if you’re using observeNodes exclusively.
However, I assumed this meant "all changes after the creation of the elements via the local DOM.
So... when I use observeNodes does that mean that I have to always expect the observer to be triggered with the initial elements the observed one contains?