Попытка извлечь данные из компонентов Vue в отдельные компоненты файла - PullRequest
0 голосов
/ 12 сентября 2018

Я впервые экспериментирую с Vue.js, поэтому вполне возможно, что я упускаю что-то очень очевидное.

По сути, у меня есть компонент, который рассчитывает количество коробок, необходимое для определенного количества печатного материала (я работаю в полиграфической промышленности).

При необходимости у меня есть кнопка для создания дополнительного компонента, если напечатанный фрагмент состоит из нескольких частей.

Мне бы хотелось иметь реактивный способ обновления общего количества коробок, необходимых для всех деталей, но я просто не могу туда добраться.

Вот ссылка на мой репозиторий Gitlab с кодом: https://gitlab.com/dsross/printutils

Любая помощь будет оценена.

Я также использую Browserify для записи файлов build.js и build.css, на которые есть ссылки в index.html.

Вот мои файлы, на случай, если никто не захочет посмотреть репо:

App.vue

<template>
  <div id="app">
    <div>
    </div>
    <div>
      <calculator v-for="(part, index) in parts" :key="index"></calculator>
      <br />
      <br />
      <div class="card shadow-sm">
        <div class="card-body">
          <button type="button" class="btn" @click="addPart()">Add Part</button>
          <button type="button" class="btn" @click="totalBoxes">Total Boxes</button>
          <span>Box Total (all parts): </span><span id="grandtotal"></span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  // import Hello from './components/Hello.vue'
  import Calculator from './components/Calculator.vue'

  export default {
    name: 'app',
    components: {
      Calculator
    },
    methods: {
      addPart: function () {
        console.log("Adding part");
        this.parts.push(Calculator);
      },
      totalBoxes: function () {
        console.log("totalBoxes called");
        let totalBoxes = 0;
        let partTotals = document.querySelectorAll("#partBoxTotal");
        for (var i = 0; i < partTotals.length; i++) {
          totalBoxes += parseInt(partTotals[i].innerHTML);
        }
        this.totalBoxCount = totalBoxes;
        document.getElementById("grandtotal").innerHTML = totalBoxes;
      }
    },
    data: function () {
      return {
        parts: [Calculator],
        totalBoxCount: 0
      }
    },
  }
</script>

Calculator.vue

<template>
    <div class="card shadow-sm" id="boxCalculator">
        <div class="card-body">
            <form>
                <div class="form-group">
                    <p>Paper:
                        <select class="custom-select" v-model="paperWeight">
                            <option v-for="(mweight, paper) in mweights" :key="mweight" v-bind:value="paper">{{paper}}</option>
                        </select>
                    </p>
                    <br />
                    <br />
                    <p>Final Width:
                        <input class="form-control" type="text" v-model="finalWidth" id="finalWidth" value="">
                    </p>
                    <p>Final Height:
                        <input type="text" class="form-control" v-model="finalHeight" id="finalHeight" value="">
                    </p>
                    <p>Sheets Per Unit:
                        <input type="text" class="form-control" v-model="numberOfSheets" id="numberOfSheets" name="numberOfSheets"
                            value="">
                    </p>
                    <p>Quantity:
                        <input type="text" class="form-control" v-model="quantity" id="quantity" name='quantity'>
                    </p>
                    <p>Stitched:
                        <input type="checkbox" v-model="stitched" name="stitched" id="stitched" value="">
                    </p>
                </div>
            </form>
            <div class="card">
                <div class="card-body">
                    <div id='results'>
                        <p id="partWeightTotal">Part Total Weight: {{ totalWeight }}</p>
                        <p><span>Part Box Total: </span><span id="partBoxTotal">{{ boxQuantity }}</span></p>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import {
        mWeights,
        stitchedMultiplier,
        maxArea
    } from "../constants.js"

    module.exports = {
        data: function () {
            return {
                paperWeight: this.selected,
                paperType: "",
                finalWidth: "",
                finalHeight: "",
                numberOfSheets: "",
                quantity: "",
                stitched: "",
                boxes: "",
                mweights: mWeights
            }
        },
        computed: {
            squareInches: function () {
                return this.finalHeight * this.finalWidth;
            },
            squareInchWeight: function () {
                let mWeight = mWeights[`${this.paperWeight}`];
                return (mWeight / 1000) / maxArea;
            },
            totalWeight: function () {
                return ((this.squareInches * this.squareInchWeight) * this.numberOfSheets) * this.quantity;
            },
            boxQuantity: function () {
                let boxes = this.totalWeight / 35;
                if (this.stitched) {
                    this.boxes = Math.ceil(boxes * stitchedMultiplier);
                    // this.$root.$emit('box-change', this.boxes);
                    return this.boxes
                } else {
                    this.boxes = Math.ceil(boxes);
                    // this.$root.$emit('box-change', this.boxes);
                    return Math.ceil(this.boxes);
                };
            },
        },
    }
</script>

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>boxcalculator2</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">
  <link rel="icon" href="../favicon.png">
  <title>Box Calculator</title>
  <!-- Bootstrap core CSS -->
  <link href="dist/css/bootstrap.min.css" rel="stylesheet">
  <!-- Custom styles for this template -->
  <link href="dist/sticky-footer.css" rel="stylesheet">
  <link rel="stylesheet" href="dist/build.css">
</head>

<body>
  <div class="container">
    <div class='row'>
      <div class='col'>
        <div id="app"></div>
      </div>
    </div>
  </div>
  <script src="dist/build.js"></script>
</body>

</html>

1 Ответ

0 голосов
/ 13 сентября 2018

Если я правильно понимаю, вы бы хотели, чтобы общее количество ящиков App обновлялось автоматически всякий раз, когда отдельные Calculator определяют количество своих ящиков.Один из способов сделать это - выдать событие из Calculator при изменении количества ящиков, которое можно отслеживать с помощью watcher .

Есть парапроблемы, которые мы рассмотрим ниже:

  1. Не имеет смысла (и неэффективно) хранить Calculator - определение отдельного компонента файла - в this.parts[].Вместо этого он может хранить значимые точки данных, такие как выходные данные Calculator.
  2. Вместо манипулирования DOM (т. Е. Запроса DOM к элементу для получения / установки его значения), выберите моделирование данныхв Vue и с использованием интерполяция в шаблоне.Это позволяет Vue автоматически отображать обновленное значение в целевом элементе.Он также устраняет присвоения идентификаторов элементов (при условии, что они использовались исключительно для манипулирования DOM), упрощая шаблон для улучшения читабельности.

Сохранение вывода калькулятора

В App мы должныиспользуйте this.parts[] для отслеживания результатов каждого вычисления part (которое мы запишем ниже).Мы определим каждый элемент массива (т. Е. «Часть») следующим образом:

{
  boxes: 0 // box count for this part
}

Это определение допускает свойство computed (которое мы определим позже) на основе .boxes,быть реактивным.

Итак, в addPart() и опции data:

// App.vue
export default {
  // ...
  methods: {
    addPart() {
      this.parts.push({ boxes: 0 });
    }
  },
  data() {
    return {
      parts: [{ boxes: 0 }]
    }
  }
}

Уведомление App о Calculator Вывод

Как правило, родителипередать данные детям через props, и дети передадут данные родителям с событиями .Альтернативы включают использование библиотеки управления состояниями, такой как Vuex , но для простоты мы будем использовать здесь события.

В Calculator мы хотим уведомить родителя (App) изменений значения boxes, поэтому мы добавим watcher , который генерирует событие (например, называется boxes-changed) при каждом изменении boxes:

// Calculator.vue
export default {
  //...
  watch: {
    boxes(value) {
      this.$emit('boxes-changed', value);
    }
  }
}

В App мы прослушаем boxes-changed событие и скопируем значение детали события в текущую переменную part boxes,где part - текущий элемент массива parts[], который повторяется.

// App.vue
<calculator v-for="(part, index) in parts" @boxes-changed="part.boxes = $event" :key="index"></calculator>

Разбивка @boxes-changed="part.boxes = $event":

  • @boxes-changed="..." -прослушивать boxes-changed событие, отправленное из <calculator>
  • part.boxes = $event - установить part.boxes на значение детализации события

Создание totalBoxCount реактивного

С учетом вышеуказанных изменений у нас есть инструменты, необходимые для того, чтобы App totalBoxCount реагировал:

  1. Измените totalBoxCount на вычисляемое свойство , которое суммируетдо .boxes полей this.parts[].Это свойство будет вычисляться автоматически при изменении элементов массива this.parts[].

     // App.vue
     export default {
       computed: {
         totalBoxCount() {
           // Note: Alternatively, use a simple for-loop to sum .boxes
           return this.parts
                   .filter(p => p.boxes && !Number.isNaN(p.boxes) // get only parts that have a positive `.boxes` value
                   .map(p => p.boxes)          // map object array into an integer array of `.boxes` values
                   .reduce((p,c) => p + c, 0); // sum all array elements
         },
       },
       data() {
         return {
           parts: [],
           // totalBoxCount: 0   // CHANGED INTO COMPTUED PROP ABOVE
         }
       }
     }
    
  2. В шаблоне App используйте интерполяцию строк для отображения totalBoxCount:

    <!-- 
    <span>Box Total (all parts): </span><span id="grandtotal"></span>
    --> <!-- DON'T DO THIS -->
    <span>Box Total (all parts): {{totalBoxCount}}</span>
    
  3. Мы можем также удалить кнопку Всего ящиков (ранее использовавшуюся для ручного запуска вычисления) из шаблона:

    <!--
    <button type="button" class="btn" @click="totalBoxes">Total Boxes</button>
    --> <!-- DELETE -->
    

    и связанный с ним click -обработчик:

    // App.vue
    export default {
      methods: {
        // totalBoxes: function() { /* .. */ } // DELETE THIS
      }
    }
    

демо

...