Vue.js 2 — вычисляемое свойство в повторителе списка v-for

Я пытаюсь добавить в таблицу несколько вычисляемых столбцов (см. последние три столбца). У меня такое ощущение, что это связано с тем, что вычисляемое свойство неправильно ссылается на запись. Я уверен, что упустил что-то простое! Любые идеи? Благодарю вас!

Вот скрипка: https://jsfiddle.net/0770ct39/2/

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Vue.js Tutorial | More Computed Properties</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
  </head>

  <body>
    <div id="app" class="container">
      <div class="row">
            <table class="table table-hover">
                <thead>
                    <tr>
                        <th>Phase</th>
                        <th>Labour Budget</th>
                        <th>Labour Hours</th>
                        <th>Labour Cost Rate</th>
                        <th>Labour Cost</th>
                        <th>Overhead Cost</th>
                        <th>Net</th>
                    </tr>   
                </thead>
                <tbody>
                    <tr v-for="record in records">
                        <td>{{record.phase}}</td>
                        <td>{{record.labourBudget}}</td>
                        <td><input type="text" v-model="record.labourHours"></td>
                        <td><input type="text" v-model="record.labourCostRate"></td>
                        <td>{{record.labourCost}}</td>
                        <td>{{record.overheadCost}}</td>
                        <td>{{record.net}}</td>
                    </tr>
                </tbody>
            </table>
      </div>
    </div>
  </body>

  <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>

  <script>
    var app = new Vue({
      el: '#app',
      data: {
        records: [
            {phase:"Phase1", labourBudget: 100, labourHours: 4, labourCostRate: 45},
            {phase:"Phase2", labourBudget: 100, labourHours: 2, labourCostRate: 42}
        ]
      },
      computed: {
        labourCost: function(){
            return this.record.labourHours * this.record.labourCostRate;
        },
        overheadCost: function(){
            return this.record.labourCost * 1.36;
        },
        net: function(){
            return this.record.netRevenue-this.record.labourCost-this.record.overheadCost;
        }
      }
    })
  </script>
</html>


person FirstRedPepper    schedule 08.12.2016    source источник


Ответы (2)


Вам нужно будет сделать каждую строку своим собственным компонентом и передать record этому, чтобы это сработало. Если вы не хотите создавать компонент, вы можете использовать вместо него methods.

Вы могли бы по существу сделать:

methods: {
  laborCost: function(record) {
    return record.labourHours * record.labourCostRate
  },
  ...
}

Затем используйте его как

{{ laborCost(record) }}

Если вы хотите пойти по компонентному маршруту (что, я думаю, вам следует), вы должны сделать что-то вроде этого:

<record v-for="record in records" :record="record"></record>

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

person Bill Criswell    schedule 08.12.2016
comment
Спасибо Билл! Я получил его работу с методами, как вы предложили! jsfiddle.net/k72qkbtp Правильно ли предположить, что это не лучший подход, поскольку он не кэширует данные? как вычисляемые свойства, поэтому вы рекомендуете компонентный подход (иначе он может работать медленно при использовании большого количества строк и столбцов)? Я попытался пересмотреть его на компонентный подход и зашел так далеко — что мне не хватает? jsfiddle.net/h1ujpft0 - person FirstRedPepper; 09.12.2016
comment
Я думаю, что я получил его с помощью компонентов Билла! jsfiddle.net/sb0b36ju По какой-то причине он выбрасывает шаблон над таблицей - есть идеи, как это исправить? Кроме того, это лучший способ сделать это по сравнению с методами с точки зрения производительности? - person FirstRedPepper; 09.12.2016
comment
Вы только что ответили, когда я печатал свой первоначальный ответ. Это выглядит правильно. Однако, чтобы таблица отображалась правильно, вам нужно выполнить <tr is="data-row" v-for="record in records" :record="record"></tr>. - person Bill Criswell; 09.12.2016
comment
Спасибо Билл! Я так близок! jsfiddle.net/jn9Lf1dy Получение этой ошибки: [Предупреждение Vue]: шаблон компонента должен содержать ровно один корневой элемент: ‹td›{{record.phase}}‹/td› ‹td›{{record.labourBudget}}‹/td› ‹td›‹input type=text v-model=record.labourHours›‹/td› ‹td ›‹input type=text v-model=record.labourCostRate›‹/td› ‹td›{{labourCost}}‹/td› ‹td›{{overheadCost}}‹/td› ‹td›{{net}} ‹/тд› - person FirstRedPepper; 09.12.2016
comment
Вам также необходимо включить <tr> в свой шаблон. - person Bill Criswell; 09.12.2016
comment
УДИВИТЕЛЬНО!! Большое спасибо тебе Билл!! :) - person FirstRedPepper; 09.12.2016
comment
Совершенно никаких проблем! - person Bill Criswell; 09.12.2016
comment
Всем, кто это читает - вот рабочая скрипка с компонентами (спасибо Биллу)! jsfiddle.net/jn9Lf1dy/1 - person FirstRedPepper; 09.12.2016
comment
Билл, извините за беспокойство! Все работает, но заметил, что в консоли по-прежнему появляется следующая ошибка - есть советы, как ее исправить? [Vue warn]: шаблон компонента должен содержать ровно один корневой элемент: ‹tr› ‹td›{{record.phase}}‹/td› ‹td›{{record.labourBudget}}‹/td› ‹td›‹input type=text v-model=record.labourHours›‹/td› ‹td›‹input type=text v-model=record.labourCostRate›‹/td› ‹td›{{labourCost}}‹/td› ‹td› {{overheadCost}}‹/td› ‹td›{{net}}‹/td› ‹tr› - person FirstRedPepper; 09.12.2016

Причина, по которой ваши функции вычисляемых свойств не работают, заключается в том, что ключевое слово this относится к вашему экземпляру Vue. Например... если вы изменили свои вычисляемые функции вот так...

computed: {
  labourCost: function() {
    return app.record.labourHours * app.record.labourCostRate;
  }
}

... это было бы функционально эквивалентно тому, что у вас есть сейчас, потому что переменная app относится к вашему экземпляру Vue.

Итак, в вашем текущем состоянии, когда Vue обрабатывает ваши вычисленные свойства, он говорит: «Эй! Нет свойства данных с именем записи! Я имею в виду, что я вижу одно с именем records, но ни одно с именем record».

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

var app = new Vue({
  el: '#app',
  data: {
    records: [
        {phase:"Phase1", labourBudget: 100, labourHours: 4, labourCostRate: 45},
        {phase:"Phase2", labourBudget: 100, labourHours: 2, labourCostRate: 42}
    ]
  },
  computed: {
    rows: function() {
      return this.records.map(function(record) {
        return Object.assign({}, record, {
          labourCost : record.labourHours * record.labourCostRate,
          overheadCost : record.labourCost * 1.36,
          net : record.netRevenue-record.labourCost-record.overheadCost
        });
      });
    }
  }
})

Затем измените цикл в вашем html на

<tr v-for="record in rows">

ПРИМЕЧАНИЕ: Object.assign() это особенность ES2015. Но вы можете использовать этот polyfill. Или вы можете использовать альтернативную функцию слияния объектов, такую ​​как _.assign от lodash или jQuery.extend().

person jessegavin    schedule 08.12.2016
comment
Спасибо Джесси! Он работал для первого столбца при загрузке, но не для двух других и, похоже, не обновлялся при изменении значений. Я попытался добавить полифилл Object.assign — возможно, я использовал его неправильно? jsfiddle.net/wt178zbz - person FirstRedPepper; 09.12.2016