跨自定义元素的共享行为状态

Shared behavior state across custom elements

我有这两个自定义聚合物元素 (Polymer 1.0.3):

  1. 显示要翻译的文本。
  2. 显示触发翻译加载的按钮。

我还有一个 Behavior 保存翻译(json 对象)并包含使翻译成为可能的所有功能。

这是我期望发生的事情:

  1. 点击元素 2 中的按钮
  2. 翻译加载到行为中
  3. 行为中设置了语言选择
  4. 元素 1 中的文本已更新为翻译后的等效内容

步骤 1 - 3 发生,但 4 没有发生。文本永远不会更新。如果元素 1 和 2 组合为同一元素,我可以让它工作,但如果它们是分开的(任何它们都需要分开)。

如果您想了解 "kick" 属性,这是我从 Polymer 0.5 中学到的东西。当这两个元素组合在一起时,它就会起作用,所以我认为当元素分开时它是必要的。

知道如何实现吗?我对其他范例持开放态度。

代码

这就是我的代码的大致布局方式。我也做了一个plunker with a single-page test case.

index.html

<!doctype html>
<html>

<head>
  <script src="http://www.polymer-project.org/1.0/samples/components/webcomponentsjs/webcomponents-lite.js"></script>
  <link rel="import" href="http://www.polymer-project.org/1.0/samples/components/polymer/polymer.html">
  <link rel="import" href="behavior.html">
  <link rel="import" href="element1.html">
  <link rel="import" href="element2.html">
</head>

<body>
  <my-element></my-element>
  <another-element></another-element>
</body>

</html>

元素 1

<dom-module id="my-element">
  <template>
    <p>{{localize(label, kick)}}</p>
  </template>
</dom-module>

<script>
  Polymer({
    is: 'my-element',
    behaviors: [
      behavior
    ],
    properties: {
      label: {
        type: String,
        value: 'original'
      }
    }
  });
</script>

元素 2

<dom-module id="another-element">
  <template>
    <button on-click="buttonClicked">load</button>
  </template>
</dom-module>
<script>
  Polymer({
    is: 'another-element',
    behaviors: [
      behavior
    ],
    buttonClicked: function() {
      this.registerTranslation('en', {
        original: 'changed'
      })
      this.selectLanguage('en');
    }
  });
</script>

行为

<script>
  var behavior = {
    properties: {
      kick: {
        type: Number,
        value: 0
      },
      language: {
        type: String,
        value: 'fun'
      },
      translations: {
        type: Object,
        value: function() {
          return {};
        }
      }
    },
    localize: function(key, i) {
      if (this.translations[this.language] && this.translations[this.language][key]) {
        return this.translations[this.language][key];
      }
      return key;
    },
    registerTranslation: function(translationKey, translationSet) {
      this.translations[translationKey] = translationSet;
    },
    selectLanguage: function(newLanguage) {
      this.language = newLanguage;
      this.set('kick', this.kick + 1);
    }
  };
</script>

首先,虽然概念是让 behavior 成为实例之间共享数据的管道,但正如所写的那样,每个实例都将拥有自己的 translations 对象副本和 kick 属性。

其次,即使该数据被私有化以便可以共享,通过 localize(label, kick) 进行的 kick 绑定与修改 kick 的表达式处于不同的范围(即this.set('kick', this.kick + 1); [{sic} 这可能只是 this.kick++;]).

要通知 N 个实例共享数据的更改,必须跟踪这些实例。执行此操作的一个好方法是附加事件侦听器。另一种方法是简单地保留一个列表。

这是您的设计的示例实现:

    <script>
      (function() {
        var instances = [];
        var translationDb = {};
        var language = '';

        window.behavior = {
          properties: {
            l10n: {
              value: 0
            }  
          },
          attached: function() {
            instances.push(this);
          },
          detached: function() {
            this.arrayDelete(instances, this);
          },
          _broadcast: function() {
            instances.forEach(function(i) {
              i.l10n++;
            });
          },
          localize: function(key, i) {
            if (translationDb[language] && translationDb[language][key]) {
              return translationDb[language][key];
            }
            return key;
          },
          registerTranslation: function(translationKey, translationSet) {
            translationDb[translationKey] = translationSet;
          },
          selectLanguage: function(newLanguage) {
            language = newLanguage;
            this._broadcast();
          }
        };
      })();
  </script>

  <dom-module id="my-element">
    <template>
      <p>{{localize(label, l10n)}}</p>
    </template>
    <script>
      Polymer({
        behaviors: [
          behavior
        ],
        properties: {
          label: {
            type: String,
            value: 'original'
          }
        }
      });
    </script>
  </dom-module>

  <dom-module id="another-element">
    <template>
      <button on-tap="buttonClicked">load</button>
    </template>
    <script>
      Polymer({
        behaviors: [
          behavior
        ],
        buttonClicked: function() {
          this.registerTranslation('en', {
            original: 'changed'
          });
          this.selectLanguage('en');
        }
      });
    </script>
  </dom-module>