How to use web component - polymer

Dear Polymer community,
I am having a hard time to learn polymer and follow the docs and tutorials on the web.
I am an experienced server-side C++ and Java developer. But my relationship with front-end and web development has never been smooth.
Maybe that is because web development keeps changing at a very fast pace and the docs and tutorial assume the user comes with a baggage of experience from previous web framework.
A few years ago I had to implement a web interface to the embedded box my team was working on. I did the web component using bootstrap and AngularJs. I really enjoyed that side project and was a breeze of fresh air compared to writing c++ for the embedded platform. I also really liked how AngularJs applied the MVC pattern to the web page.
Fast forward to the present, now google polymer and web components are the new fashion trends.
Went through the polymer tutorial and read a bit about web components. I think it is a great idea to reuse web components. Just browse the huge library of existing web components, pick and choose what you want. Sounds good in theory ... except ... the newbie like me has been unable for the entire day to use a single web component correctly so far.
I find the docs lacking and the sample code inaccurate. Let me go through a simple example. One of the web component I want to use is iron-pages. Awesome web component and that is exactly what I need !
I read the docs for iron-pages and looked at the demo on webcomponents.org and now is the time to use it. Except I cannot get the thing to work.
Here is exactly what I did on the command line by using the polymer-cli
mkdir my-app
cd my-app
polymer init
I chose the polymer-2-application
I try my app
polymer serve --open
Hurray it shows Hello my-app-app! on the webpage
Next I try to use the iron-pages web component. So I need to download that component using bower
bower install --save PolymerElements/iron-pages
The demo for iron-pages is listed here along with the sample code. But the sample code does not match what should appear in a web component ... or at least what I read so far in the tutorials for using a webcomponent.
There is no tag in that sample code. There is a tag which should not be there. I am sure an experienced web developer would know how to bridge the gap ... but not me.
I tried my best to adapt that iron-pages sample code to fit into my file src/my-app-app/my-app-app.html
Here is my code:
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
<dom-module id="my-app-app">
<template>
<style>
:host {
display: block;
}
iron-pages {
width: 100%;
height: 200px;
font-size: 50px;
color: white;
text-align: center;
}
iron-pages div {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
iron-pages div:nth-child(1) {
background-color: var(--google-blue-500);
}
iron-pages div:nth-child(2) {
background-color: var(--google-red-500);
}
iron-pages div:nth-child(3) {
background-color: var(--google-green-500);
}
</style>
<h2>Hello [[prop1]]!</h2>
<iron-pages selected="0">
<div>One</div>
<div>Two</div>
<div>Three</div>
</iron-pages>
</template>
<script>
/**
* #customElement
* #polymer
*/
class MyAppApp extends Polymer.Element {
static get is() { return 'my-app-app'; }
static get properties() {
return {
prop1: {
type: String,
value: 'my-app-app'
}
};
}
}
window.customElements.define(MyAppApp.is, MyAppApp);
var pages = document.querySelector('iron-pages');
pages.addEventListener('click', function(e) {
pages.selectNext();
});
</script>
</dom-module>
The iron-pages do not appear on the web page. The developer console on chrome shows an error that I am trying to do addEventListener on a null pointer. So I tried to comment out the addEventListener portion. I expect to still see one of the iron-pages even if I can't click to rotate among the pages. But the iron pages still don't appear at all.
I would appreciate if one of you experienced web devs could enlighten me. I have other examples of other web components I could not use perfectly such as app-toolbar, I could not get the iron-icon to appear ... even if I bower install iron-icon. Anyhow, I would be happy if I could start by getting my iron-pages hooked up.

Remove the quotes and use like : bower install --save PolymerElements/iron-pages
instead of : bower install --save PolymerElements/'iron-pages'
And also all single quotes in your code 'iron-pages'
just use iron-pages
DEMO
<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
<dom-module id="my-app-app">
<template>
<style>
:host {
display: block;
}
iron-pages {
width: 100%;
height: 200px;
font-size: 50px;
color: white;
text-align: center;
}
iron-pages div {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
iron-pages div:nth-child(1) {
background-color: var(--google-blue-500);
}
iron-pages div:nth-child(2) {
background-color: var(--google-red-500);
}
iron-pages div:nth-child(3) {
background-color: var(--google-green-500);
}
</style>
<h2>Hello [[prop1]]!</h2>
<iron-pages selected="0">
<div>One</div>
<div>Two</div>
<div>Three</div>
</iron-pages>
<script>
/**
* #customElement
* #polymer
*/
class MyAppApp extends Polymer.Element {
static get is() { return 'my-app-app'; }
static get properties() {
return {
prop1: {
type: String,
value: 'my-app-app'
}
};
}
}
window.customElements.define(MyAppApp.is, MyAppApp);
var pages = document.querySelector('iron-pages');
pages.addEventListener('click', function(e) {
pages.selectNext();
});

You are getting null pointer becouse of setting an event outside of your class.
No code should be there except the class registration (customElements.define...)
Generally in Polymer you should set listeners in the ready callback.
Custom element lifecycle here: https://www.polymer-project.org/2.0/docs/devguide/custom-elements
In your case:
ready() {
super.ready();
var pages = document.querySelector('iron-pages');
pages.addEventListener('click', function(e) {
pages.selectNext();
});
}

Related

Polymer 3 - Google Maps files missing

I am having the same issue as Polymer 3 - Google Maps. After reading the above thread, I seem to be missing the two files google-apis/google-maps-api.js and google-map-marker.js.
In the hope it would download all the dependent files for Google Maps I issued these commands:
npm install #em-polymer/google-map
npm install google-maps
npm install
What am I doing wrong?
This worked for me.
import { html } from '#polymer/lit-element';
import '#em-polymer/google-map/google-map.js';
import '#em-polymer/google-map/google-map-marker.js';
import { PageViewElement } from './page-view-element.js';
// These are the shared styles needed by this element.
import { SharedStyles } from './shared-styles.js';
class MyView1 extends PageViewElement {
render() {
return html`
${SharedStyles}
<section>
<h2>Static page</h2>
<p>This is a text-only page.</p>
<p>It doesn't do anything other than display some static text.</p>
</section>
<section>
<style>
google-map {
height: 600px;
}
</style>
<google-map latitude="39.84143133531688" longitude="-117.4895451470561" api-key="1234"></google-map>
</section>
`;
}
}
window.customElements.define('my-view1', MyView1);

Import external stylesheets in polymer 3

Is there a way to import external css files that only affects the shadow DOM? I am working with sass and creating the css files automatically, so any tricks using javascript imports can't be done.
Right now, what I have is:
static get template() {
return html`
<style>
:host {
display: block;
}
</style>
....
}
In Polymer 2, it was possible to do something like:
<dom-module id="my-app">
<link rel="stylesheet" href="style.css">
<template></template>
</dom-module>
Is there a Polymer 3 way of acheving the same thing?
You can use variables in html-tag, like this:
import { htmlLiteral } from '#polymer/polymer/lib/utils/html-tag.js';
import myCSS from "style.css";
let myCSSLiteral = htmlLiteral(myCSS);
...
class MyElement extends PolymerElement {
static get template() {
return html`<style>${myCSSLiteral}</style>...`;
...
}
...
}
Please note: You have to convert variable from string to a htmlLiteral for using it in html-tag, though I do not know why Polymer does not support raw string variable directlly. good luck!
this works great for me!
return html`
<link rel="stylesheet" href="//cdn.jsdelivr.net/chartist.js/latest/chartist.min.css">
<style>
/* I had to put !important to override the css imported above. */
</style>
<divclass="blablabla"></div>
`;

Meteor+React element duplication

I'm trying to create the structure of a basic Meteor app with React:
This is the main.html
<head>
<title>test</title>
</head>
<body>
<div id="render-target"></div>
</body>
this is the startup function
Meteor.startup(function () {
ReactDOM.render(<AppLayout />, document.getElementById("render-target"));
});
and this is the app layout
AppLayout = React.createClass({
render() {
return (
<div className="wrapper">
{this.props.nav}
{this.props.content}
{this.props.footer}
</div>
)
}
});
from what I understand the
ReactDOM.render(<AppLayout />, document.getElementById("render-target"));
should replace the div "render-target" with the AppLayout element (the "wrapper" div), but when i run the app, inside the body i see two div: the "render-target" and the "react-root".
As you can see here
why is the "render-target" div still there and the "wrapper" is duplicated?
EDIT:
the router part is
FlowRouter.route("/", {
name: "HomePageRoute",
action:function() {
ReactLayout.render(AppLayout, {
nav: <NavBar />,
content: <HomePageContainer />,
footer: <Footer/>
});
}
});
ReactDOM.render(<AppLayout />, document.getElementById("render-target"));
Will not replace the render-target id, that will always exist. As for the duplicate, I've never seen that "accidentally" happen. If you could show your whole code I'm almost certain we'll find that you have something else rendering to that element.
What packages are you using? What version of meteor? What router? Those are all important to know here.
In the end, I'd recommend using react-template-helpers for this as it's a lot more clear what's going on when using meteor + react. An example of it's usage is available from this meteor starter boilerplate.

Use <paper-material> element within custom element

I'm playing around with the Polymer Starter kit and am creating a nested custom element. I have an outer element that 'outputs' the inner element multiple times.
My issue is that the inner element (business-card) contains a <paper-material>. This element is not being affected by global styles. I know that Polymer adds a class of scoped-style to the element which ensures it can only affect the local DOM. Removing the scoped-style class in Dev Tools applies global styling.
How do I apply the styles from the standard <paper-element> to my nested element or include those same styles within my custom element.
Edit
It appears the my issue is that the styles within 'app-theme' are not applied to the internal element. I can get the desired outcome if I copy the <paper-element> styles, including media queries, plus follow the answer from #Zikes.
It seems against the modular nature of polymer to duplicate everything from an element when the element is already perfect. Am I missing something?
business-card.html
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/paper-material/paper-material.html">
<dom-module id="business-card">
<style>
:host {
display: block;
}
</style>
<template>
<paper-material>
<content></content>
</paper-material>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'business-card'
});
})();
</script>
Any help much appreciated
Polymer protects element internals from document styles and vice-versa. This is CSS scoping and it's a prominent feature of Web Components.
It can seem problematic in simple examples, but it's generally very beneficial to component reuse that component styles don't bash each other, and that document styles don't unintentionally foul up a component.
Polymer Starter Kit is not ideally set up for using app-theme.html in other scopes, but one thing you can do is copy the style rules you want to use into a CSS file, and then import that CSS file in your element code as below. The import and styles are used efficiently (e.g., the import is only loaded once, even if you use it in multiple elements).
<dom-module id="business-card">
<link rel="import" type="css" href="theme-styles.css">
<style>
:host {
display: block;
}
</style>
<template>
<paper-material>
<content></content>
</paper-material>
</template>
<script>
Polymer({
is: 'business-card'
});
</script>
</dom-module>
Live example: http://jsbin.com/hojajo/edit?html,output
If you'd like to apply the paper-material effects to your element directly, you can do so like this:
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../paper-styles/shadow.html">
<dom-module id="business-card">
<style>
:host {
display: block;
position: relative;
#apply(--shadow-transition);
}
:host([elevation="1"]) {
#apply(--shadow-elevation-2dp);
}
:host([elevation="2"]) {
#apply(--shadow-elevation-4dp);
}
:host([elevation="3"]) {
#apply(--shadow-elevation-6dp);
}
:host([elevation="4"]) {
#apply(--shadow-elevation-8dp);
}
:host([elevation="5"]) {
#apply(--shadow-elevation-16dp);
}
</style>
<template>
<content></content>
</template>
</dom-module>
<script>
Polymer({
is: 'business-card',
properties: {
/**
* The z-depth of this element, from 0-5. Setting to 0 will remove the
* shadow, and each increasing number greater than 0 will be "deeper"
* than the last.
*
* #attribute elevation
* #type number
* #default 1
*/
elevation: {
type: Number,
reflectToAttribute: true,
value: 1
},
/**
* Set this to true to animate the shadow when setting a new
* `elevation` value.
*
* #attribute animated
* #type boolean
* #default false
*/
animated: {
type: Boolean,
reflectToAttribute: true,
value: false
}
}
});
</script>
This is copied from the paper-material code itself: https://github.com/PolymerElements/paper-material/blob/master/paper-material.html

How do I style ShadowDOM elements using external CSS in Firefox?

I'm trying to build a custom HTML element. The problem is that I'm not able to apply styles to the Shadow DOM elements provided using external CSS. The code is working in Chrome but not in Firefox.
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
console.log('Element creation started...');
var inputTextElement = document.createElement('input');
inputTextElement.type = 'text';
inputTextElement.className = 'simpleElem';
// Shadow DOM root
var shadowRoot = this.createShadowRoot();
shadowRoot.appendChild(inputTextElement);
console.log('Element creation ended...');
};
var SimpleElement = document.registerElement('simple-element', { prototype: proto });
simple-element {
}
simple-element::shadow .simpleElem {
height: 30px;
margin: 0px;
padding: 0px;
width: 180px;
}
<!doctype html>
<html>
<head>
<title>HTML5 | Custom Elements</title>
<link type="text/css" rel="stylesheet" href="simple-elem.css">
<script type="text/javascript" src="simple-elem.js"></script>
</head>
<body>
<simple-element></simple-element>
</body>
</html>
Not able to figure out what is wrong with Firefox.
As noted by Gábor Imre, Shadow DOM is not enabled by default in Firefox because it is still under development. You can, however, use a polyfill to get pretty good Shadow DOM behavior in all browsers that don't support it.. If you do, you'll then need to use polyfill-next-selector to obtain the behavior you want.
Update: FF Shadow DOM support has arrived.
Firefox has no Shadow DOM support yet, see CanIUse.com. I recommend sticking to Chrome.
EDIT: FF Nightly has some support, it can be enabled manually.
While Shadow DOM in general has been supported in Firefox for a while now (invalidating the two other answers), with Firefox 72 you can now target custom/Shadow DOM elements via the part attribute and ::part() pseudo-element, respectively:
//this JS boilerplate adapted from MDN
let template = document.getElementById("simple-element");
globalThis.customElements.define(template.id, class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content);
}
});
simple-element::part(shadow) {
height: 30px;
margin: 0;
padding: 0;
width: 180px;
background: green;
}
<template id="simple-element">
<div part="shadow">Hi</div>
</template>
<simple-element></simple-element>
Obviously this code looks a lot different from what your question code looks like because the Shadow DOM spec/implementations have changed quite a lot since 2014.