-->

Simple Polymer 1.0 data table

2020-02-15 04:44发布

问题:

I am trying to create a simple Polymer component that renders a table from an array of data. Example of the intended usage of said component would be the following:

<my-table data="{{someArray}}">
  <my-column header="Id"><template>{{item.id}}</template></my-column>
  <my-column header="Name"><template>{{item.name}}</template></my-column>
</my-table>

And the render should look like this:

However, upon creating a semi-working prototype, things get complicated. The prototype can be found here: http://jsbin.com/sirutusupu/edit?html,console,output. Disclaimer: it doesn't work unless you download it an run it through a local http-server.

My first question: why does the prototype only work via local http-server?

My second question: when running locally and when I wrap the custom element with a dom-bind, it also stops working. Local code (that is also not working):

<template is="dom-bind">
  <my-table>
    <my-column header="Id"><template>{{item.id}}</template></my-column>
    <my-column header="Name"><template>{{item.name}}</template></my-column>
  </my-table>
</template>

My third question: using functions to format output doesn't work. Consider this extended example:

<script>
  function concat(a, b) {
    return a + "-" + b;
  }
</script>
<my-table>
  <my-column header="Id"><template>{{item.id}}</template></my-column>
  <my-column header="Name"><template>{{item.name}}</template></my-column>
  <my-column header="Computed"><template>{{concat(item.id, item.name)}}</template></my-column>
</my-table>

The resulting error is polymer.html:1660 [undefined::_annotatedComputationEffect]: compute method 'concat' not defined.

Is there a way to get around this without defining Computed bindings? Otherwise the custom formatting of cell values is not possible.

回答1:

You have an interesting construction here!

  • I don't know why it doesn't work for you in the jsbin, it might have something to do with rawgit, I used polygit instead.
  • Formatting will work if you put the computation function on the column's ctor.prototype after Templatizing.
  • dom.bind is going to mess up the interior template, I would avoid it.

This one seems to work:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>My table</title>      
  <base href="//polygit.org/components/">
  <link rel="import" href="polymer/polymer.html" >     
</head>
<body>
  <my-table>
    <my-column header="Id"><template>{{item.id}}</template></my-column>
    <my-column header="Name"><template>{{item.name}}</template></my-column>
    <my-column header="Computed"><template>{{concat(item.id, item.name)}}</template>
  </my-table>
  
  <dom-module id="my-table">
    <template>
      <table border="1">
        <tr>
          <template is="dom-repeat" items="{{columns}}" as="column">
            <th>{{column.header}}</th>
          </template>
        </tr>
        <template is="dom-repeat" items="{{items}}" as="row">
          <tr>
            <template is="dom-repeat" items="{{columns}}" as="column">
              <td>
                <my-cell column="{{column}}" row="{{row}}"></my-cell>
              </td>
            </template>
          </tr>
        </template>
      </table>
    </template>
  </dom-module>
  
  <script>
    Polymer({
      is: 'my-table',
      ready: function() {
        this.columns = Array.prototype.slice.call(Polymer.dom(this).querySelectorAll('my-column'));
        this.items = [
          {id: 1, name: 'John'},
          {id: 2, name: 'Jane'},
        ];
      }
    });

    Polymer({
      is: 'my-column',
      properties: {
        header: String
      },
      ready: function() {
        this.templatize(Polymer.dom(this).querySelector('template'));
        this.ctor.prototype.concat = function(id, name) {
          return name + '(' + id + ')';
        }
      },
      stampCell: function(row) {
        return this.stamp({item: row});
      },
      behaviors: [Polymer.Templatizer]
    });

    Polymer({
      is: 'my-cell',
      ready: function() {
        Polymer.dom(this).appendChild(this.column.stampCell(this.row).root);
      }
    });
  </script>    
</body>
</html>



回答2:

I think one problem you have is the definition of columns. It is OUTSIDE of the properties object

It should look like this

Polymer({
  is: 'my-table',
  properties: {
    items: Array,
    columns: {
      type: Array,
      value: function(){return [];}
    }

  },
  ready: function() {
    this.columns = Polymer.dom(this).querySelectorAll('my-column');

    this.items = [
      {id: 1, name: 'John'},
      {id: 2, name: 'Jane'},
    ];
  },
});

The second issue is the use of <template> inside the <template is="dom-bind"> which is why it stops working. Not sure why you are doing that.

When you say local http-server how is that configured?. It is often necessary to make sure that the imports are referencing the right place when then have hrefs like ../polymer/polymer.html The tools that Polymer provides (such as polyserve and web-component-tester) all map bower_components is special ways so you can refer to the elements with the ../ style. Are you using one of their local http-server or something you crafted? You can do it with other web servers (like apache or ngnix) you just have to realise that you need to change the url mapping to file directory mapping so it works.

EDIT

Right at the bottom of your jsbin you do this

  <my-table>
    <my-column header="Id"><template>{{item.id}}</template></my-column>
    <my-column header="Name"><template>{{item.name}}</template></my-column>
  </my-table>

However your <my-table> element doesn't use the <content> tag which is what will pull that content in. I am not sure its necessary - I thought your top level element template for <my-table> already contains the columns.



回答3:

The solution for question #2 (data binding does not work when the component is wrapped by dom-bind) is the following. You need to implement the following methods, as defined by the Templatizer behaviour:

  • _forwardParentProp: function(prop, value)
  • _forwardParentPath: function(path, value)
  • _showHideChildren: function(shouldHide)

You also need to set the _instanceProps to define any instance-related properties.