Подтвердить что ты не робот

Полимер Dom-Repeat Привязка к контенту

В полимере 1.2.3. Возможно ли, чтобы dom-repeat использовал контент в качестве шаблона и привязал значения к предоставленным элементам?

Пользовательский элемент:

<dom-module id="modify-collection">
  <template>
    <div>
      <template is="dom-repeat" items="[[collection]]">
        <content></content>
      </template>
    </div>
  </template>
</dom-module>

Использование:

<modify-collection collection="{{things}}">
  <list-item resource="[[item]]"></list-item>
</modify-collection>

Я просмотрел следующие ресурсы без помощи:

Полимер: как наблюдать за изменением в <content> Свойства

Полимер 1.0 сопоставляет эквивалент ссылок

Связывание данных между вложенными полимерными элементами

https://github.com/grappendorf/grapp-template-ref

https://github.com/Trakkasure/dom-bindref

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

https://github.com/Polymer/polymer/pull/2196


Обновление 2016-02-03: из Полимерной команды (PR # 2196), в будущем будет обеспечена лучшая поддержка для этого, чтобы помочь адресуйте некоторые из недостатков.
4b9b3361

Ответ 1

TL;DR

Решение 2 в то время как немного более многословное, кажется, работает лучше всего. Он также позволяет динамически выбирать шаблон, возможно, на основе атрибутов модели. (См. Решение 2: расширенный пример)


Предыстория

В этом простом примере происходит много вещей, в том числе шаблонизация, "получение" контента между теневыми, тенистыми и светлыми домами и, конечно же, привязка данных.

Этот вопрос дал некоторое представление, но без Polymer 0.5 injectBoundHtml он не был действительно работоспособен (Использование шаблона, определенного в light dom внутри элемента Polymer). Проблема в том, что наши привязки данных, кажется, теряются (AFAIK), когда мы пытаемся скопировать элемент с помощью innerHTML в наш шаблон.

Таким образом, без этого мы не сможем создавать наш шаблон "на лету" с привязкой данных. Таким образом, оба решения завершают контент в шаблоне раньше времени; это заставило html быть инертным и позволяет Polymer связывать данные в соответствующее время (http://www.html5rocks.com/en/tutorials/webcomponents/template/).

Если вы действительно хотите все понять, я бы рекомендовал прочитать Polymer src для lib/template/dom-repeat.html, lib/template/templatizer.html, lib/annotations/annotations.html (~ 1500 строк).


Решение 1 -

См. btelle ниже для улучшения решения.

Обратите внимание: этот подход заставляет dom-repeat не отображать содержимое автоматически, поэтому мы вызываем рендеринг вручную.

Элемент

<dom-module id="modify-collection">
  <template>
    <div>
      <content></content>
      <template id="repeater" is="dom-repeat" items="[[collection]]"></template>
    </div>
  </template>

  <script>
    ...
    ready: function() {
      this.$.repeater.templatize(this.querySelector('#templ'));
    }
    _changeCollection: function(item) {
      this.push('collection', item);
      this.$.repeater.render();
    }
  </script>
</dom-module>

Использование

<modify-collection collection="{{things}}">
  <template id="templ"><list-item resource="[[item]]"></list-item></template>
</modify-collection>


Решение 2

Обратите внимание, что использование этого параметра зависит от элемента 1, поскольку <template> должен иметь атрибут is="dom-template".

Элемент-помощник

(Немного изменен из этого PR: https://github.com/Polymer/polymer/pull/2196, первоначально на основе https://github.com/grappendorf/grapp-template-ref)

!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<!--
  Portions of this code have been adapted from the `grapp-template-ref` element.

  The original copyright notices are below.
-->
<!--
MIT License
Copyright (c) 2014-2015 Dirk Grappendorf, www.grappendorf.net

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
 -->

<!--

 The `dom-ref` element is a custom `HTMLTemplateElement` type extension that
can be used to reference another template by id using the `ref` property.
`dom-bindref` accepts a `bind` property to bind an object to the referenced
template. By default the bound object can be accessed as `item`, this can be
changed using the `as` property.

Example:

```html
<template is="dom-template" id="template-bind"><span>[[item.key]]</span></template>
<template is="dom-ref" ref="template-bind" bind='{"key":"value"}'></dom-ref>
```

-->
<!-- <link rel="import" href="templatizer.html"> -->

<script>

  Polymer({

    is: 'dom-ref',
    extends: 'template',

    /**
     * Fired whenever DOM is added or removed by this template (by
     * default, rendering occurs lazily).  To force immediate rendering, call
     * `render`.
     *
     * @event dom-change
     */

    properties: {

      /**
       * Reference to another template id.
       */
      ref: {
        type: String,
        observer: '_refChanged'
      },

      /**
       * Object to be bound to referenced template.
       */
      bind: {
        type: Object,
        observer: '_bindChanged'
      },

      /**
       * The name of the variable to add to the binding scope for the
       * element associated with a given template instance.
       */
      as: {
        type: String,
        value: 'item'
      }
    },

    behaviors: [
      Polymer.Templatizer
    ],

    ready: function() {
      this.templatize(this);
    },

    attached: function() {
      return this._stamp();
    },

    detached: function() {
      return this._removeChildren();
    },

    _refChanged: function(newRef, oldRef) {
      if (oldRef) {
        this._removeChildren();
        return this._stamp();
      }
    },

    _bindChanged: function(newBind, oldBind) {
      if (oldBind) {
        this._removeChildren();
        return this._stamp();
      }
    },

    _stamp: function() {
      var root, template, templateRoot, bind = {};
      this._parent = Polymer.dom(this).parentNode;
      root = this._parent;

      while (Polymer.dom(root).parentNode) {
        root = Polymer.dom(root).parentNode;
      }
      template = Polymer.dom(root).querySelector("template#" + this.ref);

      // Check For Light Dom Elements that may be passed to this shadow root (Useful for: `<content></content>`)
      if (!template) {
        template = root.host.querySelector("template#" + this.ref);
      }

      // Check the whole document
      if (!template) {
        template = document.querySelector("template#" + this.ref);
      }
      bind[this.as] = this.bind;

      // templateRoot = template.stamp(bind).root;
      // Use this method until this lands: https://github.com/Polymer/polymer/pull/1889
      templateRoot = (new template.ctor(bind, template)).root;
      this._children = Array.prototype.slice.call(templateRoot.childNodes);
      return Polymer.dom(this._parent).insertBefore(templateRoot, this);
    },

    _removeChildren: function() {
      var child, i, len, ref, results;
      if (this._children) {
        ref = this._children;
        results = [];
        for (i = 0, len = ref.length; i < len; i++) {
          child = ref[i];
          results.push(Polymer.dom(this._parent).removeChild(child));
        }
        return results;
      }
    }

  });

</script>

Элемент

<dom-module id="modify-collection">
  <template>
    <div>
      <content></content>
      <template is="dom-repeat" items="[[collection]]">
          <template is="dom-ref" bind="[[item]]" ref="templ"></template>
      </template>
    </div>
  </template>
</dom-module>

Использование

<modify-collection collection="{{things}}">
  <template id="templ" is="dom-template"><list-item resource="[[item]]"></list-item></template>
</modify-collection>

Расширенный элемент

Использование не изменяется сверху.

Здесь мы вводим уровень косвенности, позволяющий нам обернуть шаблон, переданный нашему элементу (в нашем примере с наложением).

<dom-module id="modify-collection">
  <template>
    <div>
      <content></content>
      <template id="wrapper" is="dom-template">
        <div class="overlay">
          <template is="dom-ref" bind="[[item]]" ref="templ"></template>
        </div>
      </template>

      <template is="dom-repeat" items="[[collection]]">
        <template is="dom-ref" bind="[[item]]" ref="[[_templateRef(_overlayMode)]]"></template>
      </template>
    </div>
  </template>

  <script>
    ...
    properties: {
      _overlayMode: {
        type: Boolean,
        value: false
      }
    },
    _templateRef: function(overlayMode) {
      return overlayMode ? 'wrapper' : 'templ';
    }
  </script>
</dom-module>

Ответ 2

FWIW, похоже, что следующие работы так же хорошо, как и решение 1, и добавляет пути уведомления (и автоматическое привязку/изменение и т.д.) к нему:

<dom-module id="i-me">
  <template>
    <content></content>
    <template is="dom-repeat" id="repeater" items="[[collection]]"></template>
  </template>
  <script>
    Polymer({
      is: 'i-me',
      properties: {
        collection: {
          type: Array,
          value: [
            {id: 1, name: 'a'},
            {id: 2, name: 'b'}
          ],
          notify: true
        }
      },
      ready() {
        this.$.repeater.templatize(this.querySelector('#templ'));
        Polymer.Bind.prepareModel(this.$.repeater);
        Polymer.Base.prepareModelNotifyPath(this.$.repeater);
      }
    });
  </script>
</dom-module>

Тогда просто используйте это:

<i-me>
  <template id="templ">
  <p><span>[[item.id]]</span>: <span>[[item.name]]</span></p>
  </template>
</i-me>

Ответ 3

В качестве примера используйте элемент "Железный список". Здесь он захватывает переданный элемент шаблона https://github.com/PolymerElements/iron-list/blob/9909b73a00ecc91fb957232f7bc66b59435d66ad/iron-list.html#L830. Смеситель templatizer используется AFAIK для реализации привязки к прошедшему шаблону (он также используется <template is="dom-repeat">)