Polymer 1.0 Global Variables

时间:2015-06-15 16:03:13

标签: javascript html5 polymer polymer-1.0

在Polymer 0.5中,关于全局变量的建议如本问题/答案中所述:

Polymer global variables

然而,在Polymer 1.0中,这似乎不起作用。更改通知不会自动在基础模型上生成,而是在<dom-module>上生成,这意味着只会在<app-globals>之一上生成更改通知。

在Polymer 1.0中实现此模式的推荐方法是什么?

8 个答案:

答案 0 :(得分:12)

聚合物元素<iron-meta>也是一种选择。对我来说,这是最简单的解决方案。

答案 1 :(得分:11)

我已经扩展了Etherealones&#39;解决方案作为一种行为,并扩展聚合物&#34;设置&#34;和&#34; notifyPath&#34;自动触发更新的方法。这与我可以跨组件/元素进行真正的数据绑定一样接近:

<强>全局-behavior.html:

<script>
var instances = [];
var dataGlobal = {};

var GlobalsBehaviour = {

  properties: {
    globals: {
      type: Object,
      notify: true,
      value: dataGlobal
    }
  },

  ready: function() {
    var _setOrig = this.set;
    var _notifyPathOrig = this.notifyPath;
    this.set = function() {
      _setOrig.apply(this, arguments);
      if (arguments[0].split(".")[0] === "globals") {
        this.invokeInstances(_notifyPathOrig, arguments);
      }
    };
    this.notifyPath = function(path, value) {
      _notifyPathOrig.apply(this, arguments);
      if (arguments[0].split(".")[0] === "globals") {
        this.invokeInstances(_notifyPathOrig, arguments);
      }
    };
  },

  invokeInstances: function(fn, args) {
    var i;
    for (i = 0; i < instances.length; i++) {
      instance = instances[i];
      if (instance !== this) {
        fn.apply(instance, args);
      }
    }
  },

  attached: function() {
    instances.push(this);
  },

  detached: function() {
    var i;
    i = instances.indexOf(this);
    if (i >= 0) {
      instances.splice(i, 1);
    }
  }
};

</script>

在所有应该有权访问全局变量的聚合物元素中:

  <script>
    Polymer({
      is: 'globals-enabled-element',
      behaviors: [GlobalsBehaviour]
    });
  </script>

示例:

  1. 我已将完整示例发布为Gist on Github
  2. 这是一个片段,可以看到它的实际效果:
  3. &#13;
    &#13;
        <!DOCTYPE html>
        <html>
          <head>
            <title>Globals Behavior Example</title>
            <link rel="import" href="//rawgit.com/Polymer/polymer/master/polymer.html">
            <dom-module id="globals-enabled-element">
              <template>
                <input type="text" value="{{globals.my_variable::input}}">
              </template>
              <script>
                var instances = [];
                var dataGlobal = {};
                
                var GlobalsBehaviour = {
                  
                  properties: {
                    globals: {
                      type: Object,
                      notify: true,
                      value: dataGlobal
                    }
                  },
                  
                  ready: function() {
                    var _setOrig = this.set;
                    var _notifyPathOrig = this.notifyPath;
                    this.set = function() {
                      _setOrig.apply(this, arguments);
                      if (arguments[0].split(".")[0] === "globals") {
                        this.invokeInstances(_notifyPathOrig, arguments);
                      }
                    };
                    this.notifyPath = function(path, value) {
                      _notifyPathOrig.apply(this, arguments);
                      if (arguments[0].split(".")[0] === "globals") {
                        this.invokeInstances(_notifyPathOrig, arguments);
                      }
                    };
                  },
                  
                  invokeInstances: function(fn, args) {
                    var i;
                    for (i = 0; i < instances.length; i++) {
                      instance = instances[i];
                      if (instance !== this) {
                        fn.apply(instance, args);
                      }
                    }
                  },
                  
                  attached: function() {
                    instances.push(this);
                  },
                  
                  detached: function() {
                    var i;
                    i = instances.indexOf(this);
                    if (i >= 0) {
                      instances.splice(i, 1);
                    }
                  }
                };
              </script>
              <script>
                Polymer({
                  is: 'globals-enabled-element',
                  behaviors: [GlobalsBehaviour]
                });
              </script>
            </dom-module>
          </head>
          <body>
            <template is="dom-bind">
              <p>This is our first polymer element:</p>
              <globals-enabled-element id="element1"></globals-enabled-element>
              <p>And this is another one:</p>
              <globals-enabled-element id="element2"></globals-enabled-element>
            </template>
          </body>
        </html>
    &#13;
    &#13;
    &#13;

答案 2 :(得分:4)

我已经实现了iron-signals用于此目的的模式。因此,基本原则是您在更新发生时手动通知其他实例。

考虑一下:

<dom-module id="x-global">
<script>
(function() {
  var instances = [];

  var dataGlobal = {};

  Polymer({
    is: 'x-global',

    properties: {
      data: {
        type: Object,
        value: dataGlobal,
      },
    },

    attached: function() {
      instances.push(this);
    },

    detached: function() {
      var i = instances.indexOf(this);
      if (i >= 0) {
        instances.splice(i, 1);
      }
    },

    set_: function(path, value) {
      this.set(path, value);

      instances.forEach(function(instance) {
        if (instance !== this) { // if it is not this one
          instance.notifyPath(path, value);
        }
      }.bind(this));
    },

    notifyPath_: function(path, value) {
      instances.forEach(function(instance) {
        instance.notifyPath(path, value);
      });
    },

    fire_: function(name, d) {
      instances.forEach(function(instance) {
        instance.fire(name, d);
      });
    },
  });
})();
</script>
</dom-module>

当您触发事件时,您将简单地调用具有下划线后缀的版本,如fire_。您甚至可以使用此模式创建某种类型的聚合物行为。

请注意,Polymer已使用前面的下划线属性,因此请不要将这些属性转换为_fire

P.S .: 我没有环顾四周解决如何反映this.push(array, value);的通知,因为我不需要它。我不知道这种方式是否可行。应该去找Polymer.Base.push

答案 3 :(得分:4)

Sjmiles, one of Polymer's creators just posted the following snippet to the Polymer slack room as an example of shared data:

<!doctype html>
<html>
<head>

  <meta charset="utf-8">

  <meta name="description" content="shared-data element and repeats">

  <base href="http://milestech.net/components/">

  <script href="webcomponentsjs/webcomponents-lite.min.js"></script>
  <link href="polymer/polymer.html" rel="import">

</head>
<body>

  <demo-test></demo-test>

  <script>

    (function() {
      var private_data = [{name: 'a'}, {name: 'b'}, {name: 'c'}];
      Polymer({
        is: 'private-shared-data',
        properties: {
          data: {
            type: Object,
            notify: true,
            value: function() {
              return private_data;
            }
          }
        }
      });
    })();

    Polymer({
      is: 'xh-api-device',
      properties: {
        data: {
          type: Array,
          notify: true
        },
        _share: {
          value: document.createElement('private-shared-data')
        }
      },
      observers: [
        'dataChanged(data.*)'
      ],
      ready: function() {
        this.data = this._share.data;
        this.listen(this._share, 'data-changed', 'sharedDataChanged');
      },
      dataChanged: function(info) {
        this._share.fire('data-changed', info, {bubbles: false});
      },
      sharedDataChanged: function(e) {
        this.fire(e.type, e.detail);
      },
      add: function(name) {
        this.push('data', {name: name});
      }
    });

  </script>

  <dom-module id="demo-test">
    <template>

      <h2>One</h2>

      <xh-api-device id="devices" data="{{data}}"></xh-api-device>

      <template is="dom-repeat" items="{{data}}">
        <div>name: <span>{{item.name}}</span></div>
      </template>

      <h2>Two</h2>

      <xh-api-device data="{{data2}}"></xh-api-device>

      <template is="dom-repeat" items="{{data2}}">
        <div>name: <span>{{item.name}}</span></div>
      </template>

      <br>
      <br>

      <button on-click="populate">Populate</button>

    </template>
    <script>
      Polymer({
        populate: function() {
          this.$.devices.add((Math.random()*100).toFixed(2));
          // this works too
          //this.push('data', {name: (Math.random()*100).toFixed(2)});
        }
      });
    </script>
  </dom-module>

</body>
</html>

I've actually moved my app to using simple data binding, so I'm not sure of the validity of this approach, but maybe it would be useful to someone.

答案 4 :(得分:4)

我试图改进Alexei Volkov的答案,但我想分别定义全局变量。我使用observer属性代替getter / setter,并将密钥与实例一起保存。

用法是:

<app-data  key="fName" data="{{firstName}}" ></app-data>

key属性定义全局变量的名称。

例如,你可以使用:

<!-- Output element -->
<dom-module id="output-element" >
  <template>
    <app-data key="fName" data="{{data1}}" ></app-data>
    <app-data key="lName" data="{{data2}}" ></app-data>
    <h4>Output-Element</h4>
    <div>First Name: <span>{{data1}}</span></div>
    <div>Last Name: <span>{{data2}}</span></div>
  </template>
</dom-module>

<script>Polymer({is:'output-element'});</script>

<app-data> dom模块的定义:

<dom-module id="app-data"></dom-module>
<script>
(function () {
    var instances = [];
    var vars = Object.create(Polymer.Base);

    Polymer({
        is: 'app-data',
        properties: {
           data: {
                type: Object,
                value: '',
                notify: true,
                readonly: false,
                observer: '_data_changed'
            },
          key: String
        },
        created: function () {
          key = this.getAttribute('key');
          if (!key){
            console.log(this);
            throw('app-data element requires key');
          }

          instances.push({key:key, instance:this});
        },

        detached: function () {
            key = this.getAttribute('key');
            var i = instances.indexOf({key:key, instance:this});
            if (i >= 0) {
                instances.splice(i, 1);
            }
        },

      _data_changed: function (newvalue, oldvalue) {
        key = this.getAttribute('key');
        if (!key){
            throw('_data_changed: app-data element requires key');
            }
        vars.set(key, newvalue);

        // notify the instances with the correct key
        for (var i = 0; i < instances.length; i++) 
        {
          if(instances[i].key == key)
          {
            instances[i].instance.notifyPath('data', newvalue);
          }
        }
      }


    });
})();
</script>

完全正常工作的演示在这里:http://jsbin.com/femaceyusa/1/edit?html,output

答案 5 :(得分:1)

我已将上述所有建议合并到以下全球聚合物对象

<dom-module id="app-data">
</dom-module>
<script>
    (function () {
        var instances = [];
        var vars = Object.create(Polymer.Base);
        var commondata = {
            get loader() {
                return vars.get("loader");
            },
            set loader(v) {
                return setGlob("loader", v);
            }
        };

        function setGlob(path, v) {
            if (vars.get(path) != v) {
                vars.set(path, v);
                for (var i = 0; i < instances.length; i++) {
                    instances[i].notifyPath("data." + path, v);
                }
            }
            return v;
        }

        Polymer({
            is: 'app-data',
            properties: {
                data: {
                    type: Object,
                    value: commondata,
                    notify: true,
                    readonly: true
                }
            },
            created: function () {
                instances.push(this);
            },

            detached: function () {
                var i = instances.indexOf(this);
                if (i >= 0) {
                    instances.splice(i, 1);
                }
            }
        });
    })();
</script>

并使用它就像

<dom-module id="app-navigation">
    <style>

    </style>
    <template>
        <app-data id="data01" data="{{data1}}" ></app-data>
        <app-data id="data02" data="{{data2}}"></app-data>
        <span>{{data1.loader}}</span>
        <span>{{data2.loader}}</span>
    </template>

</dom-module>
<script>

    (function () {
        Polymer({
            is: 'app-navigation',
            properties: {
            },
            ready: function () {
                this.data1.loader=51;
            }
        });
    })();
</script>

更改data1.loader或data2.loader会影响其他实例。您应该扩展commondata对象以添加更多全局属性,如使用loader属性显示的那样。

答案 6 :(得分:1)

如果将应用程序包装在模板中,则更容易实现全局变量的相同效果。观看此video中的解释(我与解释概念的确切分钟和秒相关联。)

答案 7 :(得分:1)

使用ootwch's solution,我遇到了延迟加载组件的竞争条件。

发布时,延迟加载的组件未使用共享数据中的值进行初始化。

如果其他人遇到同样的问题,我想我通过添加这样一个现成的回调修复它:

options(stringsAsFactors = FALSE)
claims <- read.csv("claims.csv",header=TRUE)
claims$X <- NULL
unemp <- read.csv("unemp.csv",header=TRUE)
unemp$X <- NULL
unemp$County <- toupper(unemp$County)

m <- inner_join(unemp, claims)
dim(m)

# [1] 6960    8

希望这可以为某人带来一些痛苦。