1

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

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

但是,这只会更新appTitlenot abc.def.ghi。为了也更新此值,您需要执行以下操作:

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

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

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

这是一个示例片段:

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

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

4

2 回答 2

1

更新结构数据时,应使用 Polymer API。这将触发更改的事件,并且绑定的数据将更新。看看这篇关于路径更改通知的文章。在这种情况下,您需要将代码更改为:

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

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

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

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

于 2015-10-20T13:00:07.677 回答
1

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

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

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


绑定到不可变对象

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

此对象属性必须使用 javascript getter(或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.

更改不可变对象

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

为此,您必须覆盖 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>

于 2017-05-15T18:36:46.823 回答