Adventures in polymer 3: Creation
In this final part I'll be building a google+ feed using polymer core- and paper elements


It's time to do some DIY an build my self a new google+ feed... polymer style! 

But first...


Polymer is in alpha. So some of the code described in this article may or may nor work properly in your browser.

For more information on current polymer compatibility status check out

Paper elements

material design animation

A few weeks ago Google introduced "Material Design" to the world. It is Googles new design language that we will soon see become part of some (if not all) of googles software and services. Android "L-release" is out in preview and already showing how material design will be the way forward on googles phone, tablet, car, wearable and TV platforms.

The elements that are used in material design are also available as polymer web components. They are called "paper elements".

Currently there are 22 paper elements the list. These vary from buttons and check boxes to animation effects and shadows.

  • paper-button
  • paper-checkbox
  • paper-dialog-transition
  • paper-dialog
  • paper-fab
  • paper-focusable
  • paper-group
  • paper-icon-button
  • paper-input
  • paper-item
  • paper-menu-button-overlay
  • paper-menu-button-overlay-container
  • paper-progress
  • paper-radio-button
  • paper-radio-group
  • paper-ripple
  • paper-shadow
  • paper-slider
  • paper-tab
  • paper-tabs
  • paper-toast
  • paper-toggle-button

So, lots of cool new toys to play with. And it is tempting to just throw everything in there. However, in this project I'll only use a few polymer core and paper elements.


The anatomy of a polymer element

Creating your own elements is very easy. The basic structure of a polymer web component is as follows:

<polymer-element name="the name of your element" attributes="any attribute you want to use goes here">
  This is basically what will show once you use this element on a site

      Just your normal CSS. You can also include css files just as normal
      On browsers where shadow dom works, CSS is isolated from the rest of the page.
    <div>Or what ever you want to use</div>    

            your attributes and default values if needed: "some default value", 
      MyFunction: function() {
        just javascript in here




For this project I'll use the following..

  • core-ajax — Handles our call for the json feed
  • paper-shadow — I'll use this to put some animated shadows on the "cards"
  • paper-button — For the "go to my profile button"

note: You also need to import polymer.html, but since core-ajax already imports this, i won't need to in this particular project.



My Google-plus-feed element will accept some attributes to make it run

  • apiKey — Ones google API key
  • maxResults — how many results to get/display
  • gPlusId — ones Google plus id (the +something in your profile URL)


What's going on?

There are 3 functions in this application. 2 of them are called by the core-ajax element: "on-core-response" and "on-core-error". The "on-core-response" is called when a response is received by the core-ajax element.

If a response is received, the response is parsed and various elements are created using standard createElement, setAttribute, innerHTML, etc. Having set response="{{response}}" in the ajax template lets me user "response" as a variable/object that contains the parsed json.

The core-ajax element takes a url attribute. I am using the attributes from my google-plus-feed element and "injecting" them into the url string like this: url="{{gPlusID}}/activities/public?maxResults={{maxResults}}&key={{apiKey}}". Note that in the core-response function i access attributes (gPlusID) using this.gPlusID

The template contains a div with an ID of "gplus". I can access this element by using this.$.gplus.

If for some reason there is an error (if no API key or userid is given), the "on-core-error" kicks in.

The final function, "setCardHover" adds mouseover/mouse out events to each card, changning the "z" attribute of the paper-shadow element in each card to change. I am using this.shadowRoot.querySelectorAll('.card'); to query all elements with the ".card" class in order to add the mousover/mouseout functions.


The final code

<link rel="import" href="../core-ajax/core-ajax.html">
<link rel="import" href="../paper-shadow/paper-shadow.html" />
<link rel="import" href="../paper-button/paper-button.html" />

<polymer-element name="google-plus-feed" attributes="apiKey gPlusID maxResults">
   #gplus a {
   text-decoration: none;
   line-height: 100%;
   .card {
    box-sizing: border-box;
    width: 350px;
    padding: 10px;
    margin-bottom: 10px;
    font-size: 16px;
    line-height: 100%;
    background-color: rgba(255, 255, 255, 0.8);
   .video_container {
    position: relative;
    padding-top: 30px;
    padding-bottom: 56.25%;
    margin-bottom: 8px;
   .video_container iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
   .color_button {
    background: #4285f4;
    color: #fff;
    width: 100%;
   .color_button:hover {
    background: #2a56c6;
   .color_button::shadow #ripple {
    color: #FFFFFF;
   .feed_headline {
    font-size: 28px;
    color: #4285f4;
   .image {
    width: 100%;
    height: auto;
    margin-bottom: 8px;
   .content-container {
    margin-bottom: 10px;
    padding: 0;
   @media only screen and (max-width: 800px) {
    .card {
     margin: 0 auto;
     width: 95%;

  <core-ajax auto url="{{gPlusID}}/activities/public?maxResults={{maxResults}}&key={{apiKey}}" handleAs="json" response="{{response}}" on-core-response="{{handleResponse}}" on-core-error="{{handleError}}"></core-ajax>
  <div id="gplus">Loading...</div>


  Polymer('google-plus-feed', {
   apiKey: '',
   maxResults: 10,
   gPlusID: '',
   handleResponse: function () {
    this.$.gplus.innerHTML = "";

    for (var i = 0; i < this.response.items.length; i++) {
     //Create the card
     var card = document.createElement("DIV");
     card.setAttribute("class", "card");

     //add published date
     var published = document.createElement("A");
     published.setAttribute("id", "published_" + i);
     published.setAttribute("class", "feed_headline");
     published.setAttribute("href", this.response.items[i].url);
     published.setAttribute("target", "_blank");
     published.innerHTML = this.response.items[i].published.slice(0, 10);


     //add written text (if there is any)
     if (this.response.items[i].object.content !== "") {
      //Content container
      var content_container = document.createElement("DIV");
      content_container.setAttribute("class", "content-container");
      content_container.innerHTML = this.response.items[i].object.content;


     //add attachments (if there is any)
     if (this.response.items[i].object.attachments) {
      if (this.response.items[i].object.attachments[0].objectType == "article" || this.response.items[i].object.attachments[0].objectType == "photo") {
       var image = document.createElement("IMG");
       image.setAttribute("class", "image");
       image.setAttribute("src", this.response.items[i].object.attachments[0].fullImage.url);
       var link = document.createElement("A");
       link.setAttribute("href", this.response.items[i].object.attachments[0].url);
       link.setAttribute("target", "_blank");

      if (this.response.items[i].object.attachments[0].objectType == "video") {
       if (this.response.items[i].object.attachments[0].displayName) {
        var display_name = this.response.items[i].object.attachments[0].displayName;
        published.innerHTML += " - " + display_name;

       var embed_url = this.response.items[i].object.attachments[0].embed.url;
       embed_url = embed_url.split("?")[0];
       embed_url += "?version=3&amp;enablejsapi=1&amp;modestbranding=1&amp;rel=0&amp;showinfo=0&amp;autohide=1&amp;color=white&amp;theme=light";

       var video_container = document.createElement("DIV");
       video_container.setAttribute("class", "video_container");
       var video = document.createElement("IFRAME");
       video.setAttribute("src", embed_url);
       video.setAttribute("frameborder", "0");
       video.setAttribute("allowfullscreen", "");



     //create the profile link paper button
     var paper_button = document.createElement('paper-button');
     paper_button.setAttribute("raisedButton", "");
     paper_button.setAttribute("label", "My G+ profile");
     paper_button.setAttribute("class", "color_button");

     //create a link to wrap around the button
     var profile_button = document.createElement("A");
     profile_button.setAttribute("href", ""+this.gPlusID);
     profile_button.setAttribute("rel", "publisher");
     profile_button.setAttribute("target", "_blank");
     profile_button.setAttribute("class", "profile_button");



     //add paper shadow
     var paper_shadow = document.createElement("paper-shadow");
     paper_shadow.setAttribute("z", "1");
     paper_shadow.setAttribute("animated", "");


   handleError: function () {
    this.$.gplus.innerHTML = "There was some sort of error.";
    console.log("There was some sort of error.");
   setCardHover: function () {
    var nodes = this.shadowRoot.querySelectorAll('.card');
    for (var i = 0; i < nodes.length; i++) {
     nodes[i].onmouseover = function () {
      this.querySelector('paper-shadow').setAttribute("z", "5");
     nodes[i].onmouseout = function () {
      this.querySelector('paper-shadow').setAttribute("z", "1");



To use the element:

<google-plus-feed apikey="AIzaSyBaAjRlcoTKqhhIBWXJk6ToEtg-sjtXCe8" gplusid="+FlemmingRasmussen" maxresults="3"></google-plus-feed>


The result

comments powered by Disqus