简体   繁体   English

Polymer 1.0 Global Variables

[英]Polymer 1.0 Global Variables

In Polymer 0.5 the advice on globals was as outlined in this question/answer: 在Polymer 0.5中,关于全局变量的建议如本问题/答案中所述:

Polymer global variables 聚合物全局变量

However in Polymer 1.0 this doesn't seem to work. 但是在Polymer 1.0中,这似乎不起作用。 Change notifications are not automatically generated on the underlying model, they are generated on the <dom-module> instead which means that change notifications will be generated on only one of the <app-globals> . 更改通知不会在底层模型上自动生成,而是在<dom-module>上生成,这意味着只会在其中一个<app-globals>上生成更改通知。

What is the recommended way of implementing this pattern in Polymer 1.0? 在Polymer 1.0中实现此模式的推荐方法是什么?

Polymer element <iron-meta> is also an option. 聚合物元素<iron-meta>也是一种选择。 For me this was the easiest solution. 对我来说这是最简单的解决方案。

I've extended Etherealones' solution to work as a Behavior, and to extend Polymers "set" and "notifyPath" methods to trigger the updates automatically. 我已经将Etherealones的解决方案扩展为一个行为,并扩展了Polymers“set”和“notifyPath”方法以自动触发更新。 This is as close as i could get to a true databinding across components/elements: 这与我可以跨组件/元素进行真正的数据绑定一样接近:

globals-behavior.html: 全局-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>

And in all polymer elements that should have access to the globals variable: 在所有应该访问全局变量的聚合物元素中:

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

Examples: 例子:

  1. I have posted a full example as a Gist on Github 在Github上发布了一个完整的例子作为Gist
  2. Here's a snippet to see it in action: 这是一个片段,可以看到它的实际效果:

  <!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> 

I have implemented a pattern like iron-signals uses for this purpose. 为此目的,我已经实现了像iron-signals一样的模式。 So the basic principle is that you manually notify other instances when an update occurs. 因此,基本原则是您在更新发生时手动通知其他实例。

Consider this: 考虑一下:

<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>

You will simple call the version that have an underscore suffix like fire_ when you are firing an event. 在触发事件时,您将简单地调用具有下划线后缀的版本,例如fire_ You can even create a Polymer Behavior of some sort with this pattern I guess. 您甚至可以使用此模式创建某种类型的聚合物行为。

Beware that preceding underscore properties are already used by Polymer so don't go ahead and convert these to _fire . 请注意,Polymer已经使用了前面的下划线属性,所以不要继续将它们转换为_fire

PS: I didn't look around to solve how to reflect the notification of this.push(array, value); PS:我没有环顾四周解决如何反映this.push(array, value);的通知this.push(array, value); as I don't need it. 因为我不需要它。 I don't know if it's possible this way. 我不知道这是否可能。 Should go find Polymer.Base.push . 应该去找Polymer.Base.push

Sjmiles, one of Polymer's creators just posted the following snippet to the Polymer slack room as an example of shared data: 作为Polymer的创建者之一,Sjmiles刚刚将以下片段发布到Polymer slack room作为共享数据的示例:

<!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. 我实际上已经将我的应用程序移动到使用简单的数据绑定,所以我不确定这种方法的有效性,但也许它对某人有用。

I have tried to improve on Alexei Volkov's answer, but I wanted to define the global variables separately. 我试图改进Alexei Volkov的答案,但我想分别定义全局变量。 Instead of the getters/setters I used the observer property and saved the key together with the instances. 我使用了observer属性并将密钥与实例一起保存,而不是getter / setter。

The usage is: 用法是:

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

whereas the key property defines the name of the global variable. key属性定义全局变量的名称。

So for example you can use: 例如,您可以使用:

<!-- 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>

Definition of the <app-data> dom module: <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>

Fully working demo is here: http://jsbin.com/femaceyusa/1/edit?html,output 完整的工作演示在这里: http//jsbin.com/femaceyusa/1/edit?html,output

I've combined all suggestions above into the following global polymer object 我已将上述所有建议合并到以下全局聚合物对象中

<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>

and use it elsewere like 并使用它就像是

<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>

Changing either data1.loader or data2.loader affects other instances. 更改data1.loader或data2.loader会影响其他实例。 You should to extend commondata object to add more global properties like it shown with loader property. 您应该扩展commondata对象以添加更多全局属性,如使用loader属性显示的那样。

It is much easier to achieve the same effect of global variables if you wrapped your application in a template. 如果将应用程序包装在模板中,则更容易实现全局变量的相同效果。 Watch the explanation in this video (I linked to the exact minute and second where the concept is explained). 观看此视频中的解释(我链接到解释概念的确切分钟和秒)。

Using ootwch's solution , I ran into a race condition situation with lazy-loaded components. 使用ootwch的解决方案 ,我遇到了延迟加载组件的竞争条件。

As posted, lazy-loaded components are not initialized with the value from the shared data. 发布后,延迟加载的组件不会使用共享数据中的值进行初始化。

In case anyone else runs into the same problem, I think I fixed it by adding a ready callback like this: 如果其他人遇到同样的问题,我想我通过添加一个像这样的现成回调修复它:

ready: function() {
  const key = this.getAttribute('key')
  if (!key) {
    throw new Error('cm-app-global element requires key')
  }

  const val = vars.get(key)
  if (!!val) {
    this.set('data', val)
  }
},

Hope this saves someone some pain. 希望这能为人们带来一些痛苦。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM