如何优化表格上表示组织为列的数据的数据绑定

时间:2018-01-28 20:56:13

标签: javascript vue.js vuejs2

在显示组织为带有表的列的数据时,我无法正确使用计算属性。 我的代码的完整示例位于jsfiddle,这是简短版本和说明。 我想将这些数据呈现为表格:

var vueData = {
  objects: [
    {
      name: "objectName",
      objectData: [
        {prop1: "val1", prop2: "val2"}
      ]
    }
  ]
}

在此vueData中,objectData数组的每个元素都应显示为一列(该列表示一个月或一天的数据)。 objectData元素中的道具不应按原样显示,而应显示为计算值。显示的值应反映vueData的变化。

所以我来到这个vue模板:

<table>
<tr>
  <th>Object Name</th>
  <th>Data Piece 1</th>
  <th>Data Piece 2</th>
</tr>
<template v-for="obj in objects">
<tr> 
  <th rowspan="2">{{ obj.name }}</th>
  <td v-for="dataPiece in obj.objectData">{{ compute(dataPiece.prop1) }}</td>
</tr>
<tr><td v-for="dataPiece in obj.objectData">{{ compute(dataPiece.prop2) }}</td></tr>
</template>
</table>

除了我使用的方法,而不是vue的计算属性外,一切正常。方法的问题在于它们的结果不会缓存,并且在单个支柱更改后所有单元格&#39;值被重新计算。

我可以为每个具有计算属性而不是td s的单元格使用vue组件,但它看起来有点过分,因为表格可能很大。

从vue的角度来看还有其他解决方案吗? (我的意思是不改变数据结构或表的外观等)。

3 个答案:

答案 0 :(得分:1)

我通过LinusBorg Generating computed properties on the fly看到了这篇文章,他在那里展示了一个将属性映射到计算机的函数。

我调整了objects变量的功能,因为原始版本更侧重于平面数据(也有点吓人)。

function mapObjectToComputed({objects}) {
  console.log(objects)
  let res = {};
  objects.forEach((obj,i) => {
    obj.objectData.forEach((dat,j) => {
      ['prop1', 'prop2'].forEach(prop => {
        const propModel = `objects_${i}_${j}_${prop}`;
        const computedProp = {
          get() {
            console.log(`Getting ${propModel}`)
            const val = this.objects[i].objectData[j][prop];
            return val;
          }
        }
        res[propModel] = computedProp;
      })
    })
  })
  return res;
}

这是模板的内部部分。我已将prop1更改为新语法并离开了prop2

<template v-for="(obj, i) in objects">
  <tr> 
    <th rowspan="2">{{ obj.name }}</th>
    <td v-for="(dataPiece, j) in obj.objectData">
      {{ fetch(i,j,'prop1') }}
    </td>
  </tr>
  <tr><td v-for="dataPiece in obj.objectData">
    {{ compute(dataPiece.prop2) }}
  </td></tr>
</template>

该组件

var vue = new Vue({
    el: document.getElementById("vue"),
  data: vueData,
  methods: {
    fetch: function(i,j,prop) {
      const propModel = `objects_${i}_${j}_${prop}`
      return this[propModel];
    },
    compute: function(prop) {
        console.log('computing ' + prop);
        return 'computed(' + prop + ')';
    }
  },
  computed: {
    firstProp: function() {
        return this.objects[0].objectData[0].prop1;
    },
    ...mapObjectToComputed(vueData)
  }
});

超时后的控制台

Getting objects_0_0_prop1  
computing 1-1-2  
computing 1-2-2  
computing 1-3-2  
computing 2-1-2  
computing 2-2-2  
computing 2-3-2  
computing 3-1-2  
computing 3-2-2  
computing 3-3-2  

所以只有prop2全面重新计算。

以下是Fiddle

答案 1 :(得分:1)

如您所述,您可以使用组件。这是我发现的最干净的解决方案。该组件非常简单,已针对此示例进行了调整。如果计算特别昂贵,可能值得使用。

&#13;
&#13;
var vueData = {
  objects: [{
      name: 'Object 1',
      objectData: [{
          prop1: '1-1-1',
          prop2: '1-1-2'
        },
        {
          prop1: '1-2-1',
          prop2: '1-2-2'
        },
        {
          prop1: '1-3-1',
          prop2: '1-3-2'
        }
      ]
    },
    {
      name: 'Object 2',
      objectData: [{
          prop1: '2-1-1',
          prop2: '2-1-2'
        },
        {
          prop1: '2-2-1',
          prop2: '2-2-2'
        },
        {
          prop1: '2-3-1',
          prop2: '2-3-2'
        }
      ]
    },
    {
      name: 'Object 3',
      objectData: [{
          prop1: '3-1-1',
          prop2: '3-1-2'
        },
        {
          prop1: '3-2-1',
          prop2: '3-2-2'
        },
        {
          prop1: '3-3-1',
          prop2: '3-3-2'
        }
      ]
    },
  ]
};

var vue = new Vue({
  el: document.getElementById("vue"),
  data: vueData,
  methods: {
    compute: function(prop) {
      console.log('computing ' + prop);
      return 'computed(' + prop + ')';
    }
  },
  components: {
    cacheResult: {
      props: {
        fn: Function,
        arg: String
      },
      template: '<td>{{fn(arg)}}</td>'
    }
  },
  computed: {
    firstProp: function() {
      return this.objects[0].objectData[0].prop1;
    }
  }
});

setTimeout(function() {
  vueData.objects[0].objectData[0].prop1 = 'changed on timeout';
}, 3000);
&#13;
th,
td {
  border: 1px solid black;
}
&#13;
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="vue">
  <table>
    <tr>
      <th>Object Name</th>
      <th>Data Piece 1</th>
      <th>Data Piece 2</th>
      <th>Data Piece 3</th>
    </tr>
    <template v-for="obj in objects">
      <tr>
        <th rowspan="2">{{ obj.name }}</th>
        <td v-for="dataPiece in obj.objectData"
          is="cacheResult"
          :fn="compute"
          :arg="dataPiece.prop1"
          >
        </td>
      </tr>
      <tr>
        <td v-for="dataPiece in obj.objectData"
          is="cacheResult"
          :fn="compute"
          :arg="dataPiece.prop2">
        </td>
      </tr>
    </template>
  </table>
  <span>Computed prop1 value = {{ firstProp }}</span>
</div>
&#13;
&#13;
&#13;

答案 2 :(得分:1)

我本来想用一个指令做这个,但没有好的方法传递函数和参数,直到我记得我可以使用vnode.context来获取上下文,我可以通过名称来查找函数而不是把它作为一个实际的功能。

所以这是一个使用函数调用的结果更新其元素textContent的指令。

var vueData = {
  objects: [{
      name: 'Object 1',
      objectData: [{
          prop1: '1-1-1',
          prop2: '1-1-2'
        },
        {
          prop1: '1-2-1',
          prop2: '1-2-2'
        },
        {
          prop1: '1-3-1',
          prop2: '1-3-2'
        }
      ]
    },
    {
      name: 'Object 2',
      objectData: [{
          prop1: '2-1-1',
          prop2: '2-1-2'
        },
        {
          prop1: '2-2-1',
          prop2: '2-2-2'
        },
        {
          prop1: '2-3-1',
          prop2: '2-3-2'
        }
      ]
    },
    {
      name: 'Object 3',
      objectData: [{
          prop1: '3-1-1',
          prop2: '3-1-2'
        },
        {
          prop1: '3-2-1',
          prop2: '3-2-2'
        },
        {
          prop1: '3-3-1',
          prop2: '3-3-2'
        }
      ]
    },
  ]
};

var vue = new Vue({
  el: document.getElementById("vue"),
  data: vueData,
  methods: {
    compute: function(prop) {
      console.log('computing ' + prop);
      return 'computed(' + prop + ')';
    }
  },
  directives: {
    cache(el, binding, vnode) {
      if (binding.value !== binding.oldValue) {
        el.textContent = vnode.context[binding.arg](binding.value);
      }
    }
  },
  computed: {
    firstProp: function() {
      return this.objects[0].objectData[0].prop1;
    }
  }
});

setTimeout(function() {
  vueData.objects[0].objectData[0].prop1 = 'changed on timeout';
}, 3000);
th,
td {
  border: 1px solid black;
}
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="vue">
  <table>
    <tr>
      <th>Object Name</th>
      <th>Data Piece 1</th>
      <th>Data Piece 2</th>
      <th>Data Piece 3</th>
    </tr>
    <template v-for="obj in objects">
      <tr>
        <th rowspan="2">{{ obj.name }}</th>
        <td v-for="dataPiece in obj.objectData"
          v-cache:compute="dataPiece.prop1"
          >
        </td>
      </tr>
      <tr>
        <td v-for="dataPiece in obj.objectData"
          v-cache:compute="dataPiece.prop2">
        </td>
      </tr>
    </template>
  </table>
  <span>Computed prop1 value = {{ firstProp }}</span>
</div>