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.
Related
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
I'm creating an HTML element using Polymer, and I want it to be able to work with an ES6 class I've written. Therefore, I need to import the class first and then register the element, which is what I do:
(function() {
System.import('/js/FoobarModel.js').then(function(m) {
window.FoobarModel = m.default;
window.FoobarItem = Polymer({
is: 'foobar-item',
properties: {
model: Object // instanceof FoobarModel === true
},
// ... methods using model and FoobarModel
});
});
})();
And it works well. But now I want to write a test HTML page to display my component with some dummy data:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="/bower_components/webcomponentsjs/webcomponents.js"></script>
<script src="/bower_components/system.js/dist/system.js"></script>
<script>
System.config({
map:{
traceur: '/bower_components/traceur/traceur.min.js'
}
});
</script>
<link rel="import" href="/html/foobar-item.html">
</head>
<body>
<script>
(function() {
var data = window.data = [
{
city: {
name: 'Foobar City'
},
date: new Date('2012-02-25')
}
];
var view;
for (var i = 0; i < data.length; i++) {
view = new FoobarItem();
view.model = data[i];
document.body.appendChild(view);
}
})();
</script>
</body>
</html>
Which isn't working for one simple reason: the code in the <script> tag is executed before Polymer registers the element.
Thus I'd like to know if there's a way to load the ES6 module synchronously using System.js or even better, if it's possible to listen to a JavaScript event for the element registration (something like PolymerElementsRegistered)?
I've tried the following without success:
window.addEventListener('HTMLImportsLoaded', ...)
window.addEventListener('WebComponentsReady', ...)
HTMLImports.whenReady(...)
In the app/scripts/app.js script from the polymer starter kit, they use auto-binding template and dom-change event
// Grab a reference to our auto-binding template
var app = document.querySelector('#app');
// Listen for template bound event to know when bindings
// have resolved and content has been stamped to the page
app.addEventListener('dom-change', function() {
console.log('Our app is ready to rock!');
});
Also check this thread gives alternatives to the polymer-ready event.
The seo company I work closely with told me I needed to add this code inbetween the body tags of my meteor project.
<script type="text/javascript">
/* <![CDATA[ */
var google_conversion_id = 123456789;
var google_custom_params = window.google_tag_params;
var google_remarketing_only = true;
/* ]]> */
</script>
<script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js">
</script>
<noscript>
<div style="display:inline;">
<img height="1" width="1" style="border-style:none;" alt=""src="//googleads.g.doubleclick.net/pagead/viewthroughconversion/949352235 /?value=0&guid=ON&script=0"/>
</div>
</noscript>
However as we know script tags don't get executed properly inside body tags.
So I searched on google to find an answer and I found this code that supposedly works but it's in REACT. How can I convert this react code into normal javascript that I can refer from a template or something. I'm thinking in a onCreated and or onRendered function.
GoogleTag = React.createClass({
displayName : 'GoogleTag',
render(){
var src = '';
if(this.props.type === 'conversion'){
src = _.template('//www.googleadservices.com/pagead/conversion/<%=id%>/?label=<%=label%>&guid=ON&script=0'),
src = src({id : this.props.id, label : this.props.label})
}
if (this.props.type === 'remarketing') {
src = _.template('//googleads.g.doubleclick.net/pagead/viewthroughconversion/<%=id%>/?value=0&guid=ON&script=0'),
src = src({id: this.props.id})
}
var style = {
display : "inline"
},
imgStyle = {
borderStyle : "none"
}
return (
<div style={style}>
<img height="1" width="1" style={imgStyle} alt="" src={src}/>
</div>
)
}
})
You can use Google Remarketing library for async actions and write your meta-data via direct call tracking function:
/* remarketingTrack utility */
export default function remarketingTrack(data = {}) {
try {
trackConversion({
google_conversion_id: SOME_ID,
google_custom_params: data,
google_remarketing_only: true
});
} catch (e) {
// error
}
}
/** somewhere in ReactJS component **/
componentDidMount() {
remarketingTrack({
flight_originid: XXX,
flight_destid: XXX,
flight_startdate: date,
flight_pagetype: 'home',
});
}
I think that it's more flexible and neat solution
Place
<script type="text/javascript">
var google_conversion_id = 123456789;
var google_custom_params = window.google_tag_params;
var google_remarketing_only = true;
</script>
<script type="text/javascript" src="//www.googleadservices.com/pagead/conversion.js"></script>
in your head and you will be fine.
In Meteor you can only have one <head> defined in your project, just create a file called head.html on the client and place the code above inside a body tag in the file.
First, remove any code that you have regarding this. A standard implementation won't work with Meteor without a custom written implementation.
Second, take the code block that Google gives you. Throw out all of the JavaScript, and all of the script tags. You don't need them.
We'll be running a 'non-standard implementation', but it work's the same as far as we're concerned. Take the code within the noscript tags (the div with the img tag), and we need to place it in every place our pages render. So, place it in your Blaze or React layout template, and you should be done.
Google will detect the noscript implementation (which they create so it'll work for visitors who don't have JavaScript enabled, but we are hacking it for use with our implementation), all Google really needs to see is the url call from the img tag :)
index.html
<!DOCTYPE html>
<html>
<head>
<script src="https://google.github.io/traceur-compiler/bin/traceur.js"></script>
<script src="https://google.github.io/traceur-compiler/src/bootstrap.js"></script>
<script>
traceur.options.experimental = true;
</script>
<link rel="import" href="x-item.html">
</head>
<body>
<x-item></x-item>
</body>
</html>
and my web component:
x-item.html
<template id="itemtemplate">
<span>test</span>
</template>
<script type="module">
class Item extends HTMLElement {
constructor() {
let owner = document.currentScript.ownerDocument;
let template = owner.querySelector("#itemtemplate");
let clone = template.content.cloneNode(true);
let root = this.createShadowRoot();
root.appendChild(clone);
}
}
Item.prototype.createdCallback = Item.prototype.constructor;
Item = document.registerElement('x-item', Item);
</script>
and I get no error nor what I expect to be displayed, any idea if this should actually work?
Is this how one would extend an HTMLElement in ECMA6 syntax?
E: putting it altogether in one page solves the problem at least now I know its the right way to create a custom component, but the problem is having it in a separate file I think it has to do with how traceur handles <link rel="import" href="x-item.html"> I tried adding the type attribute to the import with no luck.
Traceur's inline processor does not appear to have support for finding <script> tags inside <link import>. All of traceur's code seems to access document directly, which results in traceur only looking at index.html and never seeing any <scripts> inside x-item.html. Here's a work around that works on Chrome. Change x-item.html to be:
<template id="itemtemplate">
<span>test</span>
</template>
<script type="module">
(function() {
let owner = document.currentScript.ownerDocument;
class Item extends HTMLElement {
constructor() {
// At the point where the constructor is executed, the code
// is not inside a <script> tag, which results in currentScript
// being undefined. Define owner above at compile time.
//let owner = document.currentScript.ownerDocument;
let template = owner.querySelector("#itemtemplate");
let clone = template.content.cloneNode(true);
let root = this.createShadowRoot();
root.appendChild(clone);
}
}
Item.prototype.createdCallback = Item.prototype.constructor;
Item = document.registerElement('x-item', Item);
})();
</script>
<script>
// Boilerplate to get traceur to compile the ECMA6 scripts in this include.
// May be a better way to do this. Code based on:
// new traceur.WebPageTranscoder().selectAndProcessScripts
// We can't use that method as it accesses 'document' which gives the parent
// document, not this include document.
(function processInclude() {
var doc = document.currentScript.ownerDocument,
transcoder = new traceur.WebPageTranscoder(doc.URL),
selector = 'script[type="module"],script[type="text/traceur"]',
scripts = doc.querySelectorAll(selector);
if (scripts.length) {
transcoder.addFilesFromScriptElements(scripts, function() {
console.log("done processing");
});
}
})();
</script>
Another possible solution would be to pre-compile the ECMA6 into ECMA5 and include the ECMA5 only. This would avoid the problem of traceur not finding the <script> tags in the import and would remove the need for the boilerplate.
This question is directly related to: flatiron-director / core-pages SPA with route specific js functions & default route . I'm sure that solution works, but I'm a little too inexperienced with polymer (and js) to determine the correct event listener in my circumstance:
How/where would you specify an appropriate event listener to set the default route if the flatiron-director is used inside a polymer element, particularly when the element's template itself does not use is="auto-binding". In this case, and to be clear, the index.html page which imports the element shown below does in fact specify a template using is="auto-binding".
Here is the element code to show what I am attempting to communicate / achieve. The flatiron routing is working (if I manually enter #itemsList or #itemOpen into the URL and use browsers previous or next buttons), but it does not add the default #itemsList to the URL automatically when hitting index.html on its own:
<polymer-element name="my-app" attributes="user items connected">
<template>
<flatiron-director id="page-director" route="{{route}}" autoHash on-director-route="{{ routeChanged }}"></flatiron-director>
<!-- HIGH LEVEL APP LAYOUT ELEMENT -->
<core-header-panel id="appHeader" mode="standard">
<!-- OUTER APP TOOLBAR ELEMENT -->
<core-toolbar id="appToolbar">
<paper-icon-button id="navicon" icon="arrow-back" on-tap="{{ showItems }}"></paper-icon-button>
<span flex>App Name</span>
<paper-icon-button id="searchbutton" icon="search"></paper-icon-button>
</core-toolbar>
<!-- MAIN CONTENT ELEMENTS -->
<!-- ATTEMPT FLATIRON ROUTING -->
<core-pages id="mainPages" selected="{{route}}" valueattr="name">
<my-items-element name="itemsList" on-core-activate="{{ itemSelect }}" user="{{user}}" items="{{items}}" item="{{item}}"></my-items-element>
<item-open-scaffold-element name="itemOpen" user="{{user}}" item="{{item}}" hidden></item-open-scaffold-element>
</core-pages>
</core-header-panel>
</template>
<script>
Polymer('my-app', {
route: "itemsList",
itemSelect: function(e, detail, sender) {
if (sender.shadowRoot.activeElement == null || sender.shadowRoot.activeElement.nodeName != "PAPER-MENU-BUTTON"){
// Ensure the user hasn't clicked on the item menu dropdown to perform alternative actions (or another element with actions for that matter)
// (i.e. make sure the user intends to open the item)
this.openItem();
}
},
openItem: function() {
this.$.mainPages.children.itemOpen.hidden = false;
this.$.mainPages.selected = "itemOpen";
//this.route = "scaffoldPage";
},
showItems: function() {
this.$.mainPages.children.itemOpen.hidden = true;
this.$.mainPages.selected = "itemsList";
}
});
</script>
<script>
var template = document.querySelector('template');
template.addEventListener('template-bound', function() {
this.route = this.route || "itemsList";
});
</script>
As noted by Jeff, use ready() lifecycle method as intra-element equivalent to template-bound event outside of element. So...based on the example above, its as simple as including the following line within polymer element's ready():
this.route = this.route || "itemsList"