javascripthtmlpolymerpolymer-cli

Polymer 1.6 and Polymer-cli, access app property from within a custom element


Using the polymer-cli tool, and the shopping cart boilerplate as a starting point, I made a simple mock-up to illustrate the use case.

Assume your index.html file includes "test-app.html" and the matching tag

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">

  <title>My App</title>

  <link rel="shortcut icon" sizes="32x32" href="/images/app-icon-32.png">

  <meta name="theme-color" content="#fff">
  <link rel="manifest" href="/manifest.json">

  <script>

    // setup Polymer options
    window.Polymer = {lazyRegister: true, dom: 'shadow'};

    // load webcomponents polyfills
    (function() {
      if ('registerElement' in document
          && 'import' in document.createElement('link')
          && 'content' in document.createElement('template')) {
        // browser has web components
      } else {
        // polyfill web components
        var e = document.createElement('script');
        e.src = '/bower_components/webcomponentsjs/webcomponents-lite.min.js';
        document.head.appendChild(e);
      }
    })();

    // load pre-caching service worker
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function() {
        navigator.serviceWorker.register('/service-worker.js');
      });
    }

  </script>

  <!-- <link rel="import" href="/src/bewi-app.html"> -->
  <link rel="import" href="/src/test-app.html">

  <style>

    body {
      margin: 0;
      font-family: 'Roboto', 'Noto', sans-serif;
      line-height: 1.5;
      min-height: 100vh;
      background-color: #eee;
    }

  </style>

</head>
<body>
  <span id="browser-sync-binding"></span>

  <test-app id="test"></test-app>

</body>
</html>

Now, assume test-app.html containing the following (again a mere simplified copy of my-app.html):

<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/app-route/app-location.html">
<link rel="import" href="../bower_components/app-route/app-route.html">
<link rel="import" href="test-element.html">

<dom-module id="test-app">

  <template>

    <app-location route="{{route}}"></app-location>
    <app-route
        route="{{route}}"
        pattern="/:page"
        data="{{routeData}}"
        tail="{{subroute}}"></app-route>

        test-element is loaded bellow
        <test-element></test-element>
    <div>
    </div>
  </template>

  <script>

    Polymer({

      is: 'test-app',

      properties: {

        page: {
          type: String,
          reflectToAttribute: true,
          observer: '_pageChanged'
        },
        baseUrl: {
          type: String,
          value: '/'
        },
        siteUrl: {
          type: String,
          value: 'http://fqdn.local'
        }
      },

      observers: [
        '_routePageChanged(routeData.page)'
      ],

      _routePageChanged: function(page) {
        this.page = page || 'view1';
      },

      _pageChanged: function(page) {
        // load page import on demand.
        this.importHref(
          this.resolveUrl('my-' + page + '.html'), null, null, true);
      }

    });

  </script>

</dom-module>

Now, the test-element.html

<link rel="import" href="../../bower_components/polymer/polymer.html">

<dom-module id="test-element">
  <template>
    <div> I am a test </div>
  </template>

  <script>
  (function() {
    'use strict';

    Polymer({
      is: 'test-element',
      ready: function() {
        console.log('READY');
        console.log('find #test using document.querySelector', document.querySelector('#test')); // OK
        console.log('find #test .siteUrl using document.querySelector', document.querySelector('#test').siteUrl); // undefined
        console.log('find #test .siteUrl using Polymer.dom', Polymer.dom(document.querySelector('#test')).siteUrl); // undefined
        console.log('find #test .siteUrl using Polymer.dom().node', Polymer.dom(document.querySelector('#test')).node.siteUrl); // undefined
        console.log('find #test .siteUrl using Polymer.dom().properties', Polymer.dom(document.querySelector('#test')).node.properties); // {object} but, I'm guessing not the computed values of the properties

        // So, how may I access the #test app's "siteUrl" property from within a custom element?
      }
    });
  })();
  </script>
</dom-module>

So, the real question is, how may test-element access the property "siteUrl" in the main app? I'm planning to make this variable readOnly, and access it from other custom elements. I'ld prefer this approach VS passing the siteUrl as an attribute to the test-element element..

What do you think?


Solution

  • The right way to pass information through elements is using the Data Binding system, i.e. "passing the siteUrl as an attribute to the test-elemet element"

    You'll accomplish the Read Only requirement surrounding the variable with square brackets, like this [[siteUrl]] as described in Property change notification and two-way binding.

    You can set a variable in a global environment as you said like

    <script>
      var myGlobalVar = 'is accessible in any element'
    
      Polymer({
    
        is: 'test-app',
    
        // ...
    
      });
    
    </script>
    

    and you can access it in every element.

    BUT, global variables are not recommended as you may know. References about why in the links below.