为什么一个元素在 Polymer 1.x 中仅限于一个实例?

Why would an element be limited to only one instance in Polymer 1.x?

我创建了一个名为 <us-map-select> 的自定义元素,并想在我的应用中使用它两次。像这样:

<us-map-select></us-map-select>
<us-map-select></us-map-select>

我用过一次(如下)就可以了

<us-map-select></us-map-select>

但是当我使用它两次(如下)时,应用程序中断并且我看到一个空白页面。

<us-map-select></us-map-select>
<us-map-select></us-map-select>

我认为这可能与我的代码中的以下某些行有关,这些行在运行多个实例时可能会发生冲突。可能有问题的行如下...

<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
...
google.charts.setOnLoadCallback(this._drawRegionsMap.bind(this));

The full code can be seen in this jsBin(及以下):

问题

Can anyone tell me what might be causing the second simultaneous instance of my element to "break" my app?

http://jsbin.com/gihezatigo/1/edit?html,控制台,输出
<!doctype html>

<head>
  <meta charset="utf-8">
  <base href="https://polygit.org/components/">
  <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
  <link href="polymer/polymer.html" rel="import">
  <link href="iron-selector/iron-selector.html" rel="import">

  <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
  <script type="text/javascript" src="https://www.google.com/jsapi"></script>

</head>

<body>

  <dom-module id="x-element">

    <template>
      <style>
        #geochart {
          width: 100%;
          max-height: 500px;
        }
      </style>
      <button on-tap="_show">Show</button>
      <button on-tap="clearAll">Clear All</button>
      <button on-tap="selectAll">Select All</button>
      <div id="geochart"></div>
      <div hidden id="selectMirror">
        <iron-selector id="selector" activate-event="" selected-values="{{selectedStates}}" multi attr-for-selected="name">
          <template is="dom-repeat" items="[[items]]">
            <span name="[[item.0]]"></span>
          </template>
        </iron-selector>
      </div>
    </template>

    <script>
      (function(){
        Polymer({
          is: 'x-element',
          properties: {
            items: {
              type: Array,
              notify: true,
              value: function() {
                return [['State', 'Select'], ['Alabama', 0], ['Alaska', 0], ['Arkansas', 0], ['Arizona', 0], ['California', 0], ['Colorado', 0], ['Connecticut', 0], ['Delaware', 0], ['Florida', 0], ['Georgia', 0], ['Hawaii', 0], ['Iowa', 0], ['Idaho', 0], ['Illinois', 0], ['Indiana', 0], ['Kansas', 0], ['Kentucky', 0], ['Louisiana', 0], ['Massachusetts', 0], ['Maryland', 0], ['Maine', 0], ['Michigan', 0], ['Minnesota', 0], ['Missouri', 0], ['Mississippi', 0], ['Montana', 0], ['North Carolina', 0], ['North Dakota', 0], ['Nebraska', 0], ['New Hampshire', 0], ['New Jersey', 0], ['New Mexico', 0], ['Nevada', 0], ['New York', 0], ['Ohio', 0], ['Oklahoma', 0], ['Oregon', 0], ['Pennsylvania', 0], ['Rhode Island', 0], ['South Carolina', 0], ['South Dakota', 0], ['Tennessee', 0], ['Texas', 0], ['Utah', 0], ['Virginia', 0], ['Vermont', 0], ['Washington', 0], ['Wisconsin', 0], ['West Virginia', 0], ['Wyoming', 0]];
              }
            },
            options: {
              type: Object,
              notify: true,
              value: {
                region: 'US',
                displayMode: 'regions',
                resolution: 'provinces',
                legend: 'none',
              }
            },
            chart: {
              type: Object,
              notify: true,
              //computed: '_computeChart()',
            },
            selectedStates: {
              type: Array,
              notify: true,
              reflectToAttribute: true,
              value: function () {
                return [];
              }
            },
          },

          observers: [
            '_selectedChanged(selectedStates.*)'
          ],

          _selectedChanged: function () {
            this.items.forEach(function (stateItem, index) {
              if (!index) return;
              this.set('items.' + index + '.1', 0);
            }.bind(this));
            this.selectedStates.forEach(function (selectedState) {
              var index = null;
              this.items.some(function (state, stateIndex) {
                if (state[0] === selectedState) {
                  index = stateIndex;
                  return true;
                }
                return false;
              });
              this.set('items.' + index + '.1', 1);
            }.bind(this));

            this.selectedStates.sort();

            if (google.visualization && this._renderMap) {
              this._drawRegionsMap();
            } else {
              this._renderMap = true;
            }
          },

          _computeChart: function() {
            var out = new google.visualization.GeoChart(this.$.geochart);
            google.visualization.events.addListener(out, 'select', this._selectHandler.bind(this));
            return out;
          },

          get data() {
            return google.visualization.arrayToDataTable(this.items);
          },

          ready: function(){
            google.charts.load('current', {
              'packages': ['geochart']
            });
            google.charts.setOnLoadCallback(this._drawRegionsMap.bind(this));

          },

          clearAll: function() {
            this.set('selectedStates', []);
          },

          selectAll: function() {
            var ar = [],
                a = this.items,
                i = a.length;
            while(i---1){
              ar.push(a[i][0]);
            }
            ar.sort();
            this.set('selectedStates', ar);
          },

          _drawRegionsMap: function() {
            this.set('chart', this._computeChart());
            this.chart.draw(this.data, this.options);
          },
          _selectHandler: function() {
            var selectedItem = this.chart.getSelection();
            var row = selectedItem[0].row;
            var value = this.data.getValue(row, 0);
            this._renderMap = false;
            this.$.selector.select(value);
          },
          _show: function() {
            //console.log(this.items);
            //console.log(this.data);
            //console.log(this.chart);
            console.log(this.selectedStates);
          },
        });
      })();
    </script>

  </dom-module>

  <x-element selected-states='["Ohio"]'></x-element>

</body>

据我所知,Google 图表 API 不喜欢同时拥有自己的两个实例 运行。 Similar Problem.

我可以建议查看 google-chart 元素。据我所知,它可以满足您的所有需求。希望这对您有所帮助。

您不能多次调用 google.charts.load。一个粗略的解决方案是创建一个闭包变量作为标志来同步图表的加载状态 api.

所以,在你已经有闭包的顶部:

  (function(){
    var chartStatus = '';

然后添加 _syncChartApi 方法来管理状态。

      ready: function(){
        this._syncChartApi();
      },

      _syncChartApi: function() {
        switch (chartStatus) {
          case '':
            chartStatus = 'loading'
            google.charts.load('current', {
              'packages': ['geochart']
            });
          case 'loading':
            google.charts.setOnLoadCallback(function() {
              chartStatus = 'loaded';
              this._drawRegionsMap();
            }.bind(this));
            break;
          case 'loaded':
            this._drawRegionsMap();
            break;
        }
      },

http://jsbin.com/pibepis/7/edit?html,output

我提供以上内容主要是为了教育价值。 :)

正如@Tjwato 所说,您应该查看官方 google-chart 实现,因为它具有更复杂的同步。