Search code examples
javascripthtmlhtml5-canvaspolymerweb-component

Polymer ready function called only once


I'm testing Polymer to develop a component that I want to reuse multiple times in my html (see image below). I have developed the following element:

<link rel="import" href="bower_components/polymer/polymer.html">
<script src="js/fabric.js"></script>

<dom-module id="tooth-element">
  <template>
    <style>
      #labelWrapper {
        display: flex;
        justify-content: center;
        align-items: center;
        font-size: 35px;
        font-weight: bold;
        height: 40PX;
      }
    </style>

    <div id="toothWrapper">
      <div id="upperRootWrapper">
        <canvas id="upperRoot" />
      </div>

      <div>
        <canvas id="tooth" />
      </div>

      <div id="lowerRootWrapper">
        <canvas id="lowerRoot" />
      </div>

      <div id="labelWrapper">
        <p>{{toothLabel}}</p>
      </div>
    </div>
  </template>

  <script>
  var TOOTH_SIZE = 50;
  var TOOTH_GAP = 5;

  var UPPERROOTID = 'upperRoot';
  var LOWERROOTID = 'lowerRoot';
  var TOOTHID = 'tooth';

  Polymer({
    is: "tooth-element",
    properties: {
      // declare the owner property
      showUpperRoot: {
        type: Boolean,
        value: true
      },
      showLowerRoot: {
        type: Boolean,
        value: true
      },
      toothLabel: {
        type: String,
        value: 30
      }
    },
    ready: function() {
      drawUpperRoot(this.showUpperRoot);
      drawTooth();
      drawLowerRoot(this.showLowerRoot);
      initLabel(this.toothLabel);
    }
  });

  function initLabel (label) {
    document.getElementById("labelWrapper").style.width = new String(3*TOOTH_SIZE)+'px';
  }

  function drawUpperRoot (isVisible) {
    var canvas = new fabric.Canvas(UPPERROOTID);
    if (isVisible) {
      canvas.setHeight(TOOTH_SIZE+TOOTH_GAP);
      canvas.setWidth(TOOTH_SIZE*3+TOOTH_GAP);
      canvas.renderAll();
      canvas.add(drawFace(1));
    } else {
      document.getElementById("upperRootWrapper").style.display = "none";
    }
  }

  function drawTooth () {
    var canvas = new fabric.Canvas(TOOTHID);
    canvas.setHeight(TOOTH_SIZE*3+TOOTH_GAP*2);
    canvas.setWidth(TOOTH_SIZE*3+TOOTH_GAP*2);
    canvas.renderAll();
    canvas.add(drawFace(2));
    canvas.add(drawFace(3));
    canvas.add(drawFace(4));
    canvas.add(drawFace(5));
    canvas.add(drawFace(6));
    //canvas.add(drawFace(7));
  }

  function drawLowerRoot (isVisible) {
    var canvas = new fabric.Canvas(LOWERROOTID);
    if (isVisible) {
      canvas.setHeight(TOOTH_SIZE+TOOTH_GAP*2);
      canvas.setWidth(TOOTH_SIZE*3+TOOTH_GAP*2);
      canvas.renderAll();
      canvas.add(drawFace(7));
    } else {
      document.getElementById("lowerRootWrapper").style.display = "none";;
    }
  }

  function drawFace(face) {
    var coordinates;
    switch (face) {
      case 1:
      coordinates = getFace1Coordinates();
      break;
      case 2:
      coordinates = getFace2Coordinates();
      break;
      case 3:
      coordinates = getFace3Coordinates();
      break;
      case 4:
      coordinates = getFace4Coordinates();
      break;
      case 5:
      coordinates = getFace5Coordinates();
      break
      case 6:
      coordinates = getFace6Coordinates();
      break;
      case 7:
      coordinates = getFace7Coordinates();
      break;
      default:
      coorddinates = null;
    }

    face = new fabric.Polygon(coordinates, {
      fill: '#E1E1E1',
      selectable: false,
      stroke: 'green'
    });
    face.on('mousedown', function () {
      if (face.fill == '#E1E1E1') {
        face.fill = '#878787';
      } else {
        face.fill = '#E1E1E1';
      }
    });
    return face;
  }

  function getFace1Coordinates() {
    return [{x: TOOTH_SIZE, y: 0}, {x: TOOTH_SIZE*2+TOOTH_GAP, y: 0}, {x: TOOTH_SIZE*3+TOOTH_GAP, y: TOOTH_SIZE}, {x: 0, y: TOOTH_SIZE}];
  }

  function getFace2Coordinates() {
    return [{x: 0, y: TOOTH_GAP}, {x: TOOTH_SIZE, y: TOOTH_SIZE+TOOTH_GAP}, {x: TOOTH_SIZE, y: TOOTH_SIZE*2+TOOTH_GAP}, {x: 0, y: TOOTH_SIZE*3}];
  }

  function getFace3Coordinates() {
    return [{x: 0, y: 0}, {x: TOOTH_SIZE, y: TOOTH_SIZE}, {x: TOOTH_SIZE*2, y: TOOTH_SIZE}, {x: TOOTH_SIZE*3, y: 0}];
  }

  function getFace4Coordinates() {
    return [{x: TOOTH_SIZE*3+TOOTH_GAP, y: 0}, {x: TOOTH_SIZE*2+TOOTH_GAP, y: TOOTH_SIZE}, {x: TOOTH_SIZE*2+TOOTH_GAP, y: TOOTH_SIZE*2+TOOTH_GAP}, {x: TOOTH_SIZE*3+TOOTH_GAP, y: TOOTH_SIZE*3}];
  }

  function getFace5Coordinates() {
    return [{x: 0, y: TOOTH_SIZE*3+TOOTH_GAP}, {x: TOOTH_SIZE+TOOTH_GAP, y: TOOTH_SIZE*2+TOOTH_GAP}, {x: TOOTH_SIZE*2, y: TOOTH_SIZE*2+TOOTH_GAP}, {x: TOOTH_SIZE*3+TOOTH_GAP, y: TOOTH_SIZE*3+TOOTH_GAP}];
  }

  function getFace6Coordinates() {
    return [{x: TOOTH_SIZE+TOOTH_GAP, y: TOOTH_SIZE+TOOTH_GAP}, {x: TOOTH_SIZE*2, y: TOOTH_SIZE+TOOTH_GAP}, {x: TOOTH_SIZE*2, y: TOOTH_SIZE*2}, {x: TOOTH_SIZE+TOOTH_GAP, y: TOOTH_SIZE*2}];
  }

  function getFace7Coordinates() {
    return [{x: 0, y: 0}, {x: TOOTH_SIZE, y: TOOTH_SIZE}, {x: TOOTH_SIZE*2, y: TOOTH_SIZE}, {x: TOOTH_SIZE*3, y: 0}];
  }
  </script>
</dom-module>

And I am trying to import it in the index.html file like that:

<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript" src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
        <link rel="import" href="tooth-element.html">
    </head>

    <body>
        <div id="line1">
            <tooth-element></tooth-element>
            <tooth-element></tooth-element>
        </div>
    </body>
</html>

The problem happens when I place two tooth-elements in the div line1. The second element is never shown because the ready function is never called for it. I don't know exactly what I should do in order to solve this problem. Any help would be appreciated

The layout of the component that I want to repeat in the html is shown below (I get this image for the first tooth-element but not for the second):

Tooth Representation

(SOLUTION) I was not using the DOM correctly (as explained by Neil John Ramal). Using Glenn's and Neil's suggestion of using the Poly.dom API I was able to get the behaviour I wanted


Solution

  • Please avoid using the regular DOM API as mush as possible when dealing with the local DOM (i.e. document.getElementById, document.querySelector, etc). Use Polymer.dom instead. Notice that when you set Polymer to use the Shadow DOM instead of the Shady DOM, your code would likely fail since regular DOM accessors can't penetrate the Shadow DOM (encapsulation is one of its features, which the Shady DOM tries to approximate).

    One of the problems on your code is that the resulting elements are tightly coupled to some global objects (e.g. drawUpperRoot). This essentially breaks your elements encapsulation as this code will also be used by other instances of that element so that instead of using their own scope, you're basically making them depend on the global scope.

    Also this code:

    function initLabel (label) {
      document.getElementById("labelWrapper").style.width = new String(3*TOOTH_SIZE)+'px';
    }
    

    would mean that when you create two tooth-element, the second element would basically refer to the first element's labelWrapper instead of it's own.

    https://www.polymer-project.org/1.0/docs/devguide/local-dom.html