Полимер: dom-repeat как локальная переменная для элементов

Я хотел бы создать элемент управления, который преобразует данные в то, что мне нужно.

На данный момент я решаю это с помощью глобальной переменной. Код выглядит примерно так: (Функциональность в нижнем регистре предназначена только для демонстрации этого простым способом. Обычно я хочу использовать его для массивов объектов. Например, для получения различных значений определенных имен и идентификаторов)

<dom-module id="my-tolower">
  <script>
    "use strict";
    Polymer({
      is: 'my-tolower',
      properties: {
        input: {
          type: String,
          value: "",
        },
        output:{
          type: String,
          value: "",
          notify: true,
        }
      },
      observers:[
        "_inputChanged(input)",
      ],
      _inputChanged: function(newInput, oldInput){
        this.set("output", newInput.toLowerCase());
      }
    });
  </script>
</dom-module>

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

<my-tolower input="[[output.name]]" output="{{lower}}">[[lower]]</my-tolower>

Это решение прекрасно работает, если я использую только переменную lower только однажды. Внутри <dom-repeat> У меня проблема.

Как я могу легко сделать пользовательскую переменную, которая доступна только внутри my-tolower? Точно так же, как у Полимера dom-repeat делает?

Я взглянул на код у Полимера <dom-repeat> источники, но я понятия не имею, как это работает. Это возможно даже в пользовательском элементе? Нужно ли создавать собственный шаблон?


Чтобы лучше объяснить мою проблему, я добавил пример большего размера, который подробно объясняет мою проблему.

HTMLImports.whenReady(() => {
  Polymer({
    is: 'my-app',
    ready: function(){
      //In my real Problem this value comes from a websocket...
      this.devices = [{
        name: "PC 1",
        components: [
          {
            name: "HDD1",
            processors: [
              {
                type: "Processor1",
                usage: "Dont Know 1"
              },
              { type: "Processor1", usage: "DontKnow2"},
              { type: "Processor2", usage: "DontKnow3"}
            ]
          },
          {
            name: "Another Piece Of Hardware",
            processors: [
              {
                type: "Processor4",
                usage: "Dont Know 1"
              },
              { type: "Processor3", usage: "DontKnow2"},
              { type: "Processor4", usage: "DontKnow3"}
            ]
          }
        ]
      },
      {
        name: "PC 2",
        components: [
          {
            name: "My third piece of hardware",
            processors: [
              {
                type: "Processor1",
                usage: "Dont Know 1"
              },
              { type: "Processor2", usage: "DontKnow2"},
              { type: "Processor3", usage: "DontKnow3"}
            ]
          }
        ]
      }];
      //this.devices must not be changed!
    }
  });
  
  
  Polymer({
    is: 'my-distinct',
    properties: {
      inputs: {
        type: String
      },
      outputs:{
        computed: '_getDistincts(inputs, path)',
        notify: true
      },
      path: {
        type: String,
        value: ""
      }
    },
    _getDistincts(inputs, path){
      let result = [];
      
      for(let key in inputs){
        if(inputs.hasOwnProperty(key)){
          let x = inputs[key];
          if(path && path != ""){
            x = x[path];
          }
          
          if(result.indexOf(x) < 0){
            result.push(x);
          }
          else{
            //Already Exists
          }
        }
      }
      
      return result;
    }
    
  });
  
 
});
<head>
  <base href="https://polygit.org/polymer+1.8.1/components/">
  <script src="webcomponentsjs/webcomponents-lite.js"></script>
  <link rel="import" href="polymer/polymer.html">
</head>
<body>
  <my-app></my-app>
  As you can see, there is always "Processor1", "Processor2" and "Pocessor3" available although this is only the result of the last computers component. You can see the right result (but with duplicates) if you use the comment I made instead.
  
  <dom-module id="my-app">
    <template>
      <ul>
        <template is="dom-repeat" items="[[devices]]" as="device">
          <li>[[device.name]]
          <ul>
            <template is="dom-repeat" items="[[device.components]]" as="component">
              <li>[[component.name]]
                <ul>
                  <!-- This is my code with using distinct -->
                  <my-distinct inputs="[[component.processors]]" 
                      outputs="{{distinctProcessorNames}}" 
                      path="type">
                    <template is="dom-repeat" items="[[distinctProcessorNames]]" as="processorName">
                      <li>[[processorName]]
                        <!-- Here I could iterate over all processors (filtered) and list their usages-->
                      </li>
                    </template>
                  </my-distinct>
                  
                  <!-- This is my code without using distinct. -->
                  <!--template is="dom-repeat" items="[[component.processors]]" as="processor">
                    <li>[[processor.type]]
                      <ul>
                        <li>Used for [[processor.usage]]</li>
                      </ul>
                    </li>
                  </template-->
                </ul>
              </li>
            </template>
          </ul>
          </li>
        </template>
      </ul>
    </template>
  </dom-module>
</body>

демонстрация

2 ответа

Как вы обнаружили, свойства объявлены внутри <dom-repeat> (То есть, lower в данном случае) не относятся исключительно к <dom-repeat> или его итерации. Таким образом, каждая итерация перезаписывает предыдущую lower значение и lower остается доступным за пределами <dom-repeat>,

Тем не менее, вы можете достичь аналогичного эффекта видимости, прикрепив свойство output к каждому item итератор в <dom-repeat> если item является Object,

Например, рассмотрим <x-foo> элемент, который принимает входной массив Object s и передает каждый вход <my-tolower>, который записывает новое значение в _output (прикрепленное свойство итератора):

<template is="dom-repeat" items="[[inputs]]" as="x">
  <!-- Attach output to a new property on item (i.e., "_output") -->
  <my-tolower input="[[x.input]]" output="{{x._output}}"></my-tolower>
</template>

HTMLImports.whenReady(() => {
  Polymer({
    is: 'x-foo',
    properties: {
      inputs: Array
    },

    _toObjArray: function(inputs) {
      // Map inputs into objects so that we can attach properties to each iterator in a dom-repeat
      return inputs.map(input => ({input}));
    }
  });

  Polymer({
    is: 'my-tolower',
    properties: {
      input: {
        type: String,
        value: "",
      },
      output: {
        computed: '_computeOutput(input)',
        notify: true,
      }
    },
    _computeOutput: function(input) {
      return input.toLowerCase();
    }
  });
});
<head>
  <base href="https://polygit.org/polymer+1.8.1/components/">
  <script src="webcomponentsjs/webcomponents-lite.js"></script>
  <link rel="import" href="polymer/polymer.html">
</head>
<body>
  <x-foo inputs='["aLPha", "brAVo", "CHarLiE", "DelTA", "epSiLoN"]'></x-foo>

  <dom-module id="x-foo">
    <template>
      <template is="dom-repeat" items="[[_toObjArray(inputs)]]">
        <!-- Attach output to a new property on item (i.e., "_output") -->
        <my-tolower input="[[item.input]]" output="{{item._output}}"></my-tolower>

        <div>
          <span>[[item.input]] -> [[item._output]]</span>
        </div>
      </template>
    </template>
  </dom-module>
</body>

демонстрация

В вашем коде у вас есть вложенный объект, используемый во вложенном dom-repeat s. Та же самая техника сверху может быть применена на каждом уровне вложенности, но вашему примеру она нужна только на самом внутреннем уровне. Вы могли бы дать <my-distinct>.outputs свою собственную "локальную" переменную, присоединяя вывод к итератору (то есть, component):

<my-distinct outputs="{{component.distinctProcessorNames}}" ...>

Тогда вы бы использовали это в своем внутреннем dom-repeat как это:

<template is="dom-repeat" items="[[component.distinctProcessorNames]]" ...>

HTMLImports.whenReady(() => {
  Polymer({
    is: 'my-app',
    ready: function(){
      this.devices = [{
        name: "PC 1",
        components: [
          {
            name: "HDD1",
            processors: [
              {
                type: "Processor1",
                usage: "Dont Know 1"
              },
              { type: "Processor1", usage: "DontKnow2"},
              { type: "Processor2", usage: "DontKnow3"}
            ]
          },
          {
            name: "Another Piece Of Hardware",
            processors: [
              {
                type: "Processor4",
                usage: "Dont Know 1"
              },
              { type: "Processor3", usage: "DontKnow2"},
              { type: "Processor4", usage: "DontKnow3"}
            ]
          }
        ]
      },
      {
        name: "PC 2",
        components: [
          {
            name: "My third piece of hardware",
            processors: [
              {
                type: "Processor1",
                usage: "Dont Know 1"
              },
              { type: "Processor2", usage: "DontKnow2"},
              { type: "Processor3", usage: "DontKnow3"}
            ]
          }
        ]
      }];
    }
  });
  
  
  Polymer({
    is: 'my-distinct',
    properties: {
      inputs: {
        type: String
      },
      outputs:{
        computed: '_getDistincts(inputs, path)',
        notify: true
      },
      path: {
        type: String,
        value: ""
      }
    },
    _getDistincts(inputs, path){
      let result = [];
      
      for(let key in inputs){
        if(inputs.hasOwnProperty(key)){
          let x = inputs[key];
          if(path && path != ""){
            x = x[path];
          }
          
          if(result.indexOf(x) < 0){
            result.push(x);
          }
          else {
            //Already Exists
          }
        }
      }
      
      return result;
    }
    
  });
  
 
});
<head>
  <base href="https://polygit.org/polymer+1.8.1/components/">
  <script src="webcomponentsjs/webcomponents-lite.js"></script>
  <link rel="import" href="polymer/polymer.html">
</head>
<body>
  <my-app></my-app>
  
  <dom-module id="my-app">
    <template>
      <ul>
        <template is="dom-repeat" items="[[devices]]" as="device">
          <li>[[device.name]]
            <ul>
              <template is="dom-repeat" items="[[device.components]]" as="component">
                <li>[[component.name]]
                  <ul>
                    <my-distinct inputs="[[component.processors]]" outputs="{{component.distinctProcessorNames}}" path="type">
                    </my-distinct>

                    <template is="dom-repeat" items="[[component.distinctProcessorNames]]" as="processorName">
                      <li>[[processorName]]</li>
                    </template>                  
                  </ul>
                </li>
              </template>
            </ul>
          </li>
        </template>
      </ul>
    </template>
  </dom-module>
</body>

ваше демо с обновлениями

Вы прокомментировали, что не хотите клонировать какие-либо объекты или изменять ввод. К сожалению, это невозможно при использовании метода итератора, описанного выше. Лучшим вариантом в этом случае является предоставление шаблона для <my-distinct>, который будет инкапсулировать любые преобразования, не влияя на ввод.

Вы используете 2 разных пользовательских элемента Polymer <my-app> а также <my-distinct>, Поэтому вы должны объявить второй с его собственными <dom-module> заявление:

<dom-module id="my-distinct">
    <template>
        <template is="dom-repeat" items="[[outputs]]" as="processorName">
            <li>[[processorName]]
        </template>
    </template>
</dom-module>

Затем используйте вычисляемое свойство (на основе значения атрибута) outputs как items ценность <template is="dom-repeat">,

Демо ниже:

HTMLImports.whenReady(() => {
  Polymer({
    is: 'my-app',
    ready: function(){
      //In my real Problem this value comes from a websocket...
      this.devices = [{
        name: "PC 1",
        components: [
          {
            name: "HDD1",
            processors: [
              {
                type: "Processor1",
                usage: "Dont Know 1"
              },
              { type: "Processor1", usage: "DontKnow2"},
              { type: "Processor2", usage: "DontKnow3"}
            ]
          },
          {
            name: "Another Piece Of Hardware",
            processors: [
              {
                type: "Processor4",
                usage: "Dont Know 1"
              },
              { type: "Processor3", usage: "DontKnow2"},
              { type: "Processor4", usage: "DontKnow3"}
            ]
          }
        ]
      },
      {
        name: "PC 2",
        components: [
          {
            name: "My third piece of hardware",
            processors: [
              {
                type: "Processor1",
                usage: "Dont Know 1"
              },
              { type: "Processor2", usage: "DontKnow2"},
              { type: "Processor3", usage: "DontKnow3"}
            ]
          }
        ]
      }];
      //this.devices must not be changed!
    }
  });
  
  Polymer({
    is: 'my-distinct',
    properties: {
      inputs: {
        type: String
      },
      outputs:{
        computed: '_getDistincts(inputs, path)', notify: true
      },
      path: {
        type: String,
        value: ""
      }
    },
    _getDistincts(inputs, path){
      let result = [];
      
      for(let key in inputs){
        if(inputs.hasOwnProperty(key)){
          let x = inputs[key];
          if(path && path != ""){
            x = x[path];
          }
          
          if(result.indexOf(x) < 0){
            result.push(x);
          }
        }
      }
      //console.log( result )
      return result;
    } 
  });  
});
<head>
  <base href="https://polygit.org/polymer+1.8.1/components/">
  <script src="webcomponentsjs/webcomponents-lite.js"></script>
  <link rel="import" href="polymer/polymer.html">
</head>
<body>
  <my-app></my-app>
  
  <dom-module id="my-app">
    <template>
      <ul>
        <template is="dom-repeat" items="[[devices]]" as="device">
          <li>[[device.name]]
          <ul>
            <template is="dom-repeat" items="[[device.components]]" as="component">
              <li>[[component.name]]
                <ul>
                  <my-distinct inputs="[[component.processors]]" path="type">
                  </my-distinct>
                
                </ul>
              </li>
            </template>
          </ul>
          </li>
        </template>
      </ul>
    </template>
  </dom-module>

  <dom-module id="my-distinct">
    <template>
        <template is="dom-repeat" items="[[outputs]]" as="processorName">
            <li>[[processorName]]
        </template>
    </template>
  </dom-module>
</body>

Другие вопросы по тегам