【问题标题】:Polymer data-binding and Immutable.js togetherPolymer 数据绑定和 Immutable.js 在一起
【发布时间】:2015-10-20 12:31:00
【问题描述】:

考虑以下自定义聚合物元素的用法

<app-header bar-foo="[[abc.def.ghi]]" app-title="[[appTitle]]"></app-header>

这里我将两个变量绑定到自定义元素app-header。现在,当我想更新值时,您可能希望按如下方式执行此操作(在 app-header! 的父级中):

this.abc.def.ghi = 10;
this.appTitle = 'New title';

但是,这只会更新appTitle 而不是abc.def.ghi。要同时更新此值,您需要执行以下操作:

this.abc = {def: {ghi: 10}};

如果有人知道这是为什么,请告诉我!!

无论如何,正因为如此,我想使用Immutable! 但是,这会在数据如何绑定到自定义元素方面引入一些问题

这里是一个例子 sn-p:

<dom-module id="my-app">
    <template>
        <app-header hits="[[state.get('page').get('hits')]]"></app-header>
    </template>
    <script>
        (function () {
            class MyApp {
                beforeRegister() {
                    this.is = 'my-app';

                    this.properties = {
                        state: {
                            type: Object,
                            notify: true,
                            value: Immutable.Map({
                                page: Immutable.Map({hits: 10})
                            })
                        }
                    };
                }
            }

            Polymer(MyApp);
        })();
    </script>
</dom-module>

所以在绑定数据到元素的时候会出现问题:

<app-header hits="[[state.get('page').get('hits')]]"></app-header>

这样的事情是可能的还是我在做其他完全错误的事情?

【问题讨论】:

    标签: javascript data-binding polymer immutable.js custom-element


    【解决方案1】:

    当您更新结构数据时,您应该使用 Polymer API。这将触发更改的事件,并且绑定的数据将更新。在路径更改通知中查看this article。在这种情况下,您需要将代码更改为:

    this.set('abc.def.ghi', 10);
    

    我不熟悉 Immutable,但是 Polymer 不支持这种表达方式。

    hits="[[state.get('page').get('hits')]]"
    

    您可以绑定到元素的(子)属性或计算函数。计算功能必须在您的元素中定义。您不能在数据绑定中的任意对象上调用任意函数。 也许,使用set API 将消除使用 Immutable 的需要。

    【讨论】:

      【解决方案2】:

      我知道这篇文章有点老了,但我想回答是因为我想探索同时使用 Immutable JS 和聚合物,因为:

      1. 我的团队使用聚合物来构建应用程序,这是无法改变的
      2. 不可变使得实现撤消和重做变得微不足道,这是一项要求

      我找到了一种在聚合物中绑定不可变 js 对象的方法,但请注意,它很丑陋而且很hacky。


      绑定到不可变对象

      为了让聚合物访问不可变的 js 属性,您需要在元素原型上创建一个 Object 类型的属性,作为不可变 js 和聚合物之间的一种“适配器”。

      该对象属性必须使用javascript getters(或Object.defineProperty),这样聚合物API才能以object.property的形式访问对象,但实际实现将访问不可变对象。

      let immutableMap = Immutable.map({count: 0, text: 'world'});
      
      Polymer({
        // ...
        properties: {
          /**
           * This property is an 'adapter' to assign a property path to access the immutablejs object.
           * The property starts with an underscore because we don't want the user to set it
           */
          _adapter: {
            type: Object,
            value: {
              /**
               * the properties of this object use
               * [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
               * so that polyer can access the object like `obj.count`
               */
              get count() { return immutableMap.get('count'); },
              get text() { return immutableMap.get('text'); }
            }
          }
        }
        // ...
      });
      

      所以在上面的例子中,我创建了一个Object 类型的属性,它有两个方法:counttext。因为这些方法以get 开头,所以它们应该只能通过方法名称来访问——它是getter

      这样做可以让聚合物绑定到_adapter 对象,然后该对象从immutableMap 中“获取”值。

      更改不可变对象

      要使这项工作正常进行,第二件事情是更改不可变对象。由于对象是不可变的,因此双向绑定不是一种选择。使用不可变对象时,您必须添加将重新分配不可变对象并重新渲染的事件侦听器。

      为此,您必须override dirty checking in polymer我试过使用notifyPath,但我发现它不能正常工作。notifyPath应该可以工作。

      警告

      由于我们覆盖脏检查并且我们没有使用虚拟 dom 库(如 react),因此使用不可变对象需要重新渲染任何使用 _adapter 的元素,每次更改发生在不可变对象中目的。不过,这可以通过使用notifyPath 来改善。

      完成演示(带撤消)

      这里是不可变 js 和聚合物的完整演示。我添加了一个撤消功能,以激励使用不可变的——不可变 JS 使撤消和重做变得非常简单:D

      <base href="https://cdn.rawgit.com/download/polymer-cdn/1.7.0/lib/">
      <script src="webcomponentsjs/webcomponents-lite.min.js"></script>
      <link rel="import" href="polymer/polymer.html">
      <script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script>
      
      <!-- Defines element markup -->
      <dom-module id="hello-world">
        <template>
          <div>
            <!--use only one-way binding because the object is immutable-->
            <button on-tap="onButtonClick">clicks <span>[[_adapter.count]]</span></button>
          </div>
          <div>
            <input on-input="onInput" type="text" value="[[_adapter.text]]">
            <h1>hello, <span>[[_adapter.text]]</span>!</h1>
          </div>
          <div>
            <button on-tap="onUndo">Undo</button>
          </div>
        </template>
      
        <!-- Registers custom element -->
        <script>
          // immutable map is defined out of the scope of the polymer object
          let immutableMap = Immutable.Map({
            count: 0,
            text: 'world'
          });
          // add undos
          let undos = [];
      
          Polymer({
            is: 'hello-world',
            /**
             * every event should reassign the `immutableMap` then manually `notifyPath`
             */
            onButtonClick: function () {
              undos.push(immutableMap);
              immutableMap = immutableMap.update('count', count => count + 1);
              this.notifyPath('_adapter.count');
            },
            onInput: function (event) {
              undos.push(immutableMap);
              immutableMap = immutableMap.set('text', event.target.value);
              this.notifyPath('_adapter.text');
            },
            onUndo: function () {
              if (undos.length) {
                immutableMap = undos.pop();
              }
              const obj = this._adapter;
              this._adapter = {};
              this._adapter = obj;
            },
            properties: {
              /**
               * This property is an 'adapter' to assign a property path to access the immutablejs object.
               * The property starts with an underscore because we don't want the user to set it
               */
              _adapter: {
                type: Object,
                value: {
                  /**
                   * the properties of this object use
                   * [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get)
                   * so that polymer can access the object like `obj.count`
                   */
                  get count() { return immutableMap.get('count'); },
                  get text() { return immutableMap.get('text'); }
                }
              }
            }
          });
        </script>
      </dom-module>
      
      <hello-world></hello-world>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多