Polymer 1.0'数组样式'路径访问器,替代表达式中的括号表示法

miy*_*oto 2 polymer polymer-1.0

Polymer 1.0文档说明:

路径语法不支持数组样式的访问器(例如users [0] .name).但是,您可以直接在路径中包含索引(users.0.name).

如何动态设置路径,并使用Polymer 0.5获得与以下示例相同的行为?这特别是在为Object定义的模型生成表单的上下文中.

<template repeat="{{row in fieldset.rows}}">
<div layout horizontal flex>
    <template repeat="{{field in row}}" flex>
        <paper-field field="{{model.fields[field]}}" value="{{obj[field]}}">
        </paper-field>
    </template>
</div>
</template>
Run Code Online (Sandbox Code Playgroud)

编辑:

https://github.com/Polymer/polymer/issues/1504:

没有近期计划支持这一点.Polymer 0.5具有用于绑定的复杂表达式解析器,我们为简单性和性能而消除了这些解析器.您现在可以使用其他模式来实现类似的结果,只需要您更明确.

实现双向数据绑定的替代模式仍然不清楚.

Ada*_*ian 14

是的,Polymer 1.0确实不再支持myObject[key]绑定表达式.但是,在您的特定用例中,有一些方法可以回避这个问题.

单向数据绑定

在涉及单向数据绑定时,克服此限制非常简单.只需使用一个同时接受对象和密钥的计算属性:

<my-element value="[[getValue(obj, key)]]"></my-element>
Run Code Online (Sandbox Code Playgroud)
getValue: function(obj, key) {
  return obj[key];
}
Run Code Online (Sandbox Code Playgroud)

双向数据绑定

在双向数据绑定的情况下,仍然可以{{obj[key]}}在Polymer 1.0中创建绑定表达式的功能替代.但是,它需要考虑您希望实现绑定的特定用例.

考虑到您的问题中的示例,您似乎正在使用某种表或字段集.出于此处示例的目的,我将使用略有不同但非常相似的结构.

假设我们有一个fieldset对象,并且该对象的结构如下:

{
  "fields": [
    { "name": "Name", "prop": "name" },
    { "name": "E-mail", "prop": "email" },
    { "name": "Phone #", "prop": "phone" }
  ],
  "rows": [
    {
      "name": "John Doe",
      "email": "jdoe@example.com",
      "phone": "(555) 555-1032"
    },
    {
      "name": "Allison Dougherty",
      "email": "polymer.rox.1337@example.com",
      "phone": "(555) 555-2983"
    },
    {
      "name": "Mike \"the\" Pike",
      "email": "verypunny@example.com",
      "phone": "(555) 555-7148"
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)

如果我们想要创建表示此对象的某种类似表的输出,我们可以使用两个嵌套的重复模板:第一个遍历不同的行,第二个迭代不同的字段.使用上面的单向数据绑定替代方法可以很简单.

但是,在这种情况下,实现双向数据绑定是非常不同的.它怎么样?

为了理解如何提出解决方案,重要的是以一种有助于我们弄清楚应该实施哪些观察流程的方式来分解这种结构.

当您fieldset像这样可视化对象时,它变得简单:

fieldset
    -->  rows (0, 1, ...)
        -->  row
            -->  fields (name, email, phone)
                -->  value
Run Code Online (Sandbox Code Playgroud)

当您考虑元素/应用程序工作流程时,变得更加简单.

在这种情况下,我们将构建一个简单的网格编辑器:

+---+------+--------+-------+
|   | Name | E-mail | Phone | 
+---+------+--------+-------+
| 0 | xxxx | xxxxxx | xxxxx |
+---+------+--------+-------+
| 1 | xxxx | xxxxxx | xxxxx |
+---+------+--------+-------+
| 2 | xxxx | xxxxxx | xxxxx |
+---+------+--------+-------+
Run Code Online (Sandbox Code Playgroud)

数据将在此应用程序中流动的方式有两种,由不同的交互触发.

  • 预填充字段值:这很容易; 我们可以使用前面提到的嵌套模板轻松完成此任务.

  • 用户更改字段的值:如果我们查看应用程序设计,我们可以推断,为了处理这种交互,无论我们使用什么元素作为输入控件,它都必须有某种方式来了解:

    • 它将影响的
    • 它代表的领域

因此,首先让我们创建一个重复模板,它将使用我们将调用的新自定义元素basic-field:

<div class="layout horizontal flex">
  <div>-</div>
  <template is="dom-repeat" items="[[fieldset.fields]]" class="flex">
      <div class="flex">[[item.name]]</div>
  </template>
</div>
<template is="dom-repeat" items="{{fieldset.rows}}" as="row" index-as="rowIndex">
  <div class="layout horizontal flex">
    <div>[[rowIndex]]</div>
    <template is="dom-repeat" items="[[fieldset.fields]]" class="flex">
      <basic-field class="flex" field="[[item]]" row="{{row}}"></basic-field>
    </template>
  </div>
</template>
Run Code Online (Sandbox Code Playgroud)

(在上面的代码片段中,您会注意到我还添加了一个单独的重复模板来生成列标题,并为行索引添加了一列 - 这对我们的绑定机制没有影响.)

现在我们已经完成了这个,让我们创建basic-field元素本身:

<dom-module>
  <template>
    <input value="{{value}}">
  </template>
</dom-module>

<script>
  Polymer({
    is: 'basic-field',
    properties: {
      row: {
        type: Object,
        notify: true
      },
      field: {
        type: Object
      },
      value: {
        type: String
      }
    }
  });
</script>
Run Code Online (Sandbox Code Playgroud)

我们不需要在元素本身内修改元素的字段,因此该field属性没有notify: true.但是,我们将修改行的内容,因此我们确实拥有notify: truerow属性.

但是,此元素尚未完成:在当前状态下,它不执行任何设置或获取其值的工作.如前所述,该值取决于行和字段.让我们在元素的原型中添加一个多属性观察器,以观察何时满足这些要求,并value从数据集中填充属性:

observers: [
  '_dataChanged(row.*, field)'
],
_dataChanged: function(rowData, field) {
  if (rowData && field) {
    var value = rowData.base[field.prop];
    if (this.value !== value) {
      this.value = value;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这个观察者有几个部分可以使它工作:

  • row.*- 如果我们刚刚指定row,只有在设置row为完全不同的行时才会触发观察者,从而更新引用.row.*而是意味着我们正在注视着的内容row,以及row本身.
  • rowData.base[field.prop]- obj.*在观察者中使用语法时,这告诉Polymer我们正在使用路径观察.因此,这意味着rowData将返回具有三个属性的对象,而不仅仅返回对象本身:
    • path-对变化的路径,对我们来说,这可能是row.name,row.phone
    • value - 在给定路径上设置的值
    • base - 基础对象,在本例中是行本身.

但是,这段代码只处理第一个流程 - 用来自的数据填充元素fieldset.为了处理其他流程,用户输入,我们需要一种方法来捕获用户输入数据的时间,然后更新行中的数据.

首先,让我们修改<input>元素上的绑定{{value::input}}:

<input value="{{value::input}}">
Run Code Online (Sandbox Code Playgroud)

Polymer不会完全自动绑定到本机元素,因为它不会向它们添加自己的抽象.只是{{value}},Polymer不知道何时更新绑定.{{value::input}}告诉Polymer它应该更新本机元素input事件的绑定,只要元素的value属性<input>发生更改,就会触发该事件.

现在让我们为该value属性添加一个观察者:

value: {
  type: String,
  observer: '_valueChanged'
}

...

_valueChanged: function(value) {
  if (this.row && this.field && this.row[this.field] !== value) {
    this.set('row.' + this.field.prop, value);
  } 
}
Run Code Online (Sandbox Code Playgroud)

请注意,我们没有使用this.row[this.field.prop] = value;.如果我们这样做,Polymer不会意识到我们的变化,因为它不会自动进行路径观察(由于前面所述的原因).仍然会进行更改,但除此之外的任何元素都basic-field可能是观察fieldset对象不会被通知.使用this.set聚合物在肩膀上轻敲我们正在改变其内部的属性row,这允许它然后触发链中的所有适当的观察者.

总而言之,basic-field元素现在应该看起来像这样(我添加了一些基本样式以使<input>元素适合basic-field元素):

<link rel="import" href="components/polymer/polymer.html">

<dom-module id="basic-field">
  <style>
    input {
      width: 100%;
    }
  </style>
  <template>
    <input value="{{value::input}}">
  </template>
</dom-module>

<script>
  Polymer({
    is: 'basic-field',
    properties: {
      row: {
        type: Object,
        notify: true
      },
      field: {
        type: Object
      },
      value: {
        type: String,
        observer: '_valueChanged'
      }
    },
    observers: [
      '_dataChanged(row.*, field)'
    ],
    _dataChanged: function(rowData, field) {
      if (rowData && field) {
        var value = rowData.base[field.prop];
        if (this.value !== value) {
          this.value = value;
        }
      }
    },
    _valueChanged: function(value) {
      if (this.row && this.field && this.row[this.field] !== value) {
        this.set('row.' + this.field.prop, value);
      } 
    }
  });
</script>
Run Code Online (Sandbox Code Playgroud)

让我们继续前进并获取我们之前制作的模板,并将它们放入自定义元素中.我们称之为fieldset-editor:

<link rel="import" href="components/polymer/polymer.html">
<link rel="import" href="components/iron-flex-layout/iron-flex-layout.html">
<link rel="import" href="basic-field.html">

<dom-module id="fieldset-editor">
  <style>
    div, basic-field {
      padding: 4px;
    }
  </style>
  <template>
    <div class="layout horizontal flex">
      <div>-</div>
      <template is="dom-repeat" items="[[fieldset.fields]]" class="flex">
          <div class="flex">[[item.name]]</div>
      </template>
    </div>
    <template is="dom-repeat" items="{{fieldset.rows}}" as="row" index-as="rowIndex">
      <div class="layout horizontal flex">
        <div>[[rowIndex]]</div>
        <template is="dom-repeat" items="[[fieldset.fields]]" class="flex">
          <basic-field class="flex" field="[[item]]" row="{{row}}"></basic-field>
        </template>
      </div>
    </template>
    <pre>[[_previewFieldset(fieldset.*)]]</pre>
  </template>
</dom-module>

<script>
  Polymer({
    is: 'fieldset-editor',
    properties: {
      fieldset: {
        type: Object,
        notify: true
      }
    },
    _previewFieldset: function(fieldsetData) {
      if (fieldsetData) {
        return JSON.stringify(fieldsetData.base, null, 2);
      }
      return '';
    }
  });
</script>
Run Code Online (Sandbox Code Playgroud)

您会注意到我们正在使用与我们内部相同的路径观察语法basic-field来观察对其的更改fieldset.使用_previewFieldsetcomputed属性,我们将在对其进行任何更改时生成字段集的JSON预览,并将其显示在数据输入网格下方.

而且,使用编辑器现在非常简单 - 我们可以通过使用以下方式实例化它:

<fieldset-editor fieldset="{{fieldset}}"></fieldset-editor>
Run Code Online (Sandbox Code Playgroud)

你有它!我们使用Polymer 1.0使用括号表示法访问器完成了相当于双向绑定的功能.

如果您想实时使用此设置,我会将其上传到Plunker.