Введение
В этой статье рассказывается, как создать научный калькулятор с использованием VueJS.
Вот визуальное отображение конечного результата:
Исходный код этой статьи доступен здесь, на Github.
Давайте начнем.
Настроить VueJS
В рамках этого руководства мы будем использовать Vue CLI для установки и настройки, альтернативой будет ссылка на Vue из CDN.
https://unpkg.com/vue
Мы можем установить Vue CLI с помощью этой команды:
$ npm install --global vue-cli
Затем давайте создадим новый проект:
$ vue init webpack-simple vueCalulator
После ответа на несколько запросов терминала мы бы все настроили.
Давайте перейдем в рабочий каталог, установим зависимости и запустим наше приложение:
$ cd vueCalulator
$ npm install
$ npm run dev
Определение компонентов Vue
Поскольку мы создаем калькулятор с двумя режимами [базовый и расширенный], давайте определим два компонента для представления каждого из них.
Во-первых, давайте создадим папку components
в каталоге src
[в этом нет необходимости, но мне нравится поддерживать чистый рабочий каталог].
Затем мы создаем два новых файла Vue
в каталоге components
. Мы будем называть эти файлы:
Логика, лежащая в основе различных режимов этого калькулятора:
Корневой компонент [App.vue] будет содержать поле ввода, в то время как компоненты
Basic
иAdvanced
будут переключаться в пространстве под полем ввода.
А теперь приступим к делу!
Создайте базовый компонент
Давайте напишем шаблон, данные и методы, которые будут находиться в Basic
компоненте.
Шаблон Basic
будет содержать кнопки калькулятора, которые прослушивают события нажатия на любую из кнопок.
<template> <div @click="buttonClick"> <button class="btn btn-primary">7</button> <button class="btn btn-primary">8</button> <button class="btn btn-primary">9</button> <button class="btn btn-primary">/</button> <button class="btn btn-primary">←</button> <button class="btn btn-primary">C</button> <button class="btn btn-primary">4</button> <button class="btn btn-primary">5</button> <button class="btn btn-primary">6</button> <button class="btn btn-primary">*</button> <button class="btn btn-primary"> ( </button> <button class="btn btn-primary"> ) </button> <button class="btn btn-primary">1</button> <button class="btn btn-primary">2</button> <button class="btn btn-primary">3</button> <button class="btn btn-primary">-</button> <button class="btn btn-primary">x²</button> <button class="btn btn-primary">±</button> <button class="btn btn-primary">0</button> <button class="btn btn-primary">.</button> <button class="btn btn-primary">%</button> <button class="btn btn-primary">+</button> <button class="equals btn btn-primary">=</button> </div> </template>
Мы видим, что кнопки заключены в корневой тег div, который вызывает метод buttonClick
при каждом нажатии кнопки.
Затем давайте экспортируем и определим методы в нашем Basic
компоненте, мы просто сделаем это, написав тег скрипта:
<script> export default { data () { return {} }, methods: { buttonClick: function(e){ let value = e.target.innerText; this.$emit('addNumber', value) } } } </script>
В этом теге скрипта мы не определили никаких данных [нам они не нужны для этого компонента], мы определили единственный buttonClick
метод, который улавливает события кликов, получает значение innerText
и отправляет событие корневому компоненту.
Нам нужен этот метод, чтобы корневой компонент мог воздействовать на кнопки, которые нажимаются, когда калькулятор находится в базовом режиме.
Создайте расширенный компонент
Расширенный компонент очень похож на базовый компонент по структуре и логике.
Во-первых, давайте напишем шаблон, который будет удерживать Advanced
кнопки калькулятора и прослушивать события нажатия на любую из кнопок.
<template> <div @click="buttonClick"> <button class="btn btn-primary">sin</button> <button class="btn btn-primary">cos</button> <button class="btn btn-primary">tan</button> <button class="btn btn-primary">x^</button> <button class="btn btn-primary">←</button> <button class="btn btn-primary">C</button> <button class="btn btn-primary">log</button> <button class="btn btn-primary">ln</button> <button class="btn btn-primary">e</button> <button class="btn btn-primary">∘</button> <button class="btn btn-primary">rad</button> <button class="btn btn-primary">√</button> <button class="btn btn-primary">7</button> <button class="btn btn-primary">8</button> <button class="btn btn-primary">9</button> <button class="btn btn-primary">/</button> <button class="btn btn-primary">x²</button> <button class="btn btn-primary">x!</button> <button class="btn btn-primary">4</button> <button class="btn btn-primary">5</button> <button class="btn btn-primary">6</button> <button class="btn btn-primary">*</button> <button class="btn btn-primary">(</button> <button class="btn btn-primary">)</button> <button class="btn btn-primary">1</button> <button class="btn btn-primary">2</button> <button class="btn btn-primary">3</button> <button class="btn btn-primary">-</button> <button class="btn btn-primary">%</button> <button class="btn btn-primary">+</button> <button class="btn btn-primary">±</button> <button class="btn btn-primary">0</button> <button class="btn btn-primary">.</button> <button class="btn btn-primary">π</button> <button class="btn btn-primary">+</button> <button class="btn btn-primary equals">=</button> </div> </template>
Как и в случае с базовым компонентом, мы не будем определять какие-либо данные в объекте данных, мы также будем передавать событие корневому компоненту при каждом нажатии кнопки.
<script> export default { name: 'Advanced', data () { return {} }, methods: { buttonClick: function(e){ let value = e.target.innerHTML; this.$emit('addNumber', value) } } } </script>
Мы видим, что buttonClick
method в расширенном компоненте точно такой же, как в базовом компоненте.
Давайте углубимся в корневой компонент!
Наконец, давайте соединим все приложение вместе через корневой компонент [App.vue].
Структура кода корневого компонента довольно проста.
Так же, как мы сделали с двумя дочерними компонентами, нам нужно определить шаблон, который будет содержать поле ввода и включать переключение двух других компонентов [дочерние компоненты в основном содержат кнопки для калькулятора].
В корневом шаблоне будут:
- Механизм переключения между расширенным и основным режимами [с использованием v-show]
2. Поле ввода для отображения перфорированных цифр.
3. Тег корпуса для дочерних компонентов [в данном случае это тег div с классом, называемым кнопками].
Вот визуальное отображение кода шаблона:
<template> <div id="this"> <div class="container box"> <p> {{ mode }} </p> <div class="row input-data"> <div class="col-lg-12"> <div class="input-group"> <span class="input-group-addon"> <input class="input-data" @click="changeToggle" type="checkbox" aria-label="Checkbox for following text input"> </span> <input type="text" class="form-control text-right" v-model="current" aria-label="Text input with checkbox" disabled> </div> </div> </div> <hr> <div class="buttons"> <Basic v-show="!toggle" v-bind:current="current" @addNumber="doStuff($event)"></Basic> <Advanced v-show="toggle" v-bind:current="current" @addNumber="doStuff($event)"></Advanced> </div> </div> </div> </template>
Из этого кода мы видим, что компоненты Basic и Advanced вложены в тег div [с классом «buttons»].
Корневой компонент регистрирует прослушиватель событий, чтобы захватывать события, генерируемые дочерними компонентами, и действовать в соответствии с ними.
Давайте посмотрим, как все работает в разделе сценария корневого компонента [:
<template> <div id="this"> <div class="container box"> <p> {{ mode }} </p> <div class="row input-data"> <div class="col-lg-12"> <div class="input-group"> <span class="input-group-addon"> <input class="input-data" @click="changeToggle" type="checkbox" aria-label="Checkbox for following text input"> </span> <input type="text" class="form-control text-right" v-model="current" aria-label="Text input with checkbox" disabled> </div> </div> </div> <hr> <div class="buttons"> <Basic v-show="!toggle" v-bind:current="current" @addNumber="doStuff($event)"></Basic> <Advanced v-show="toggle" v-bind:current="current" @addNumber="doStuff($event)"></Advanced> </div> </div> </div> <script> import Basic from './components/Basic'; import Advanced from './components/Advanced'; export default { name: 'Calculator', components: { 'Basic' : Basic, 'Advanced' : Advanced }, data () { return { current: 0, toggle: false, mode: 'Basic' } }, methods: { changeToggle: function(){ this.toggle = !this.toggle; if (this.mode == 'Basic'){ this.mode = 'Advanced' } else { this.mode = 'Basic' } }, doStuff: function(data){ let temp; if(Number(data) || data == 0){ temp = data if( this.current === 0 ) this.current = ""; this.current += "" + temp; } if( !Number(data) ) { switch ( data ) { case "/" : { this.divide(); break; } case "←" : { this.backspace(); break; } case "C" : { this.clear(); break; } case "*" : { this.multiply(); break; } case "-" : { this.minus(); break; } case "+" : { this.plus(); break; } case "x²" : { this.square(); break; } case "(" : { this.openbracket(); break; } case ")" : { this.closebracket(); break; } case "=" : { this.equals(); break; } case "±" : { this.plusMinus(); break; } case "%" : { this.percent(); break; } case "." : { this.decimal(); break; } case "sin" : { this.sin(); break; } case "cos" : { this.cos(); break; } case "tan" : { this.tan(); break; } case "ln" : { this.ln(); break; } case "e" : { this.exp(); break; } case "∘" : { this.degrees(); break; } case "x!" : { this.factorial(); break; } case "rad" : { this.radians(); break; } case "√" : { this.squareRoot(); break; } case "x^" : { this.exponent(); break; } case "π" : { this.pi(); break; } case "log" : { this.log(); break; } } } }, clear: function() { this.current = 0; }, backspace: function() { this.current = this.current.substring(0, this.current.length - 1); }, multiply: function() { this.current += "*"; }, divide: function() { this.current += "/"; }, openbracket: function() { this.current += "("; }, closebracket: function() { this.current += ")"; }, plus: function() { this.current += "+"; }, minus: function() { this.current += "-"; }, decimal: function() { this.current += "."; }, plusMinus: function() { if ( (this.current !== 0) && this.current.charAt(0) === "-" ) { this.current = this.current.slice(1); } else { this.current = "-" + this.current; } }, equals: function() { if ((this.current).indexOf("^") > -1) { var base = (this.current).slice(0, (this.current).indexOf("^")); var exponent = (this.current).slice((this.current).indexOf("^") + 1); this.current = eval("Math.pow(" + base + "," + exponent + ")"); } else { this.current = eval(this.current); } }, factorial: function () { var number = 1; if (this.current === 0) { this.current = "1"; } else if (this.current < 0) { this.current = NaN; } else { var number = 1; for (var i = this.current; i > 0; i--) { number *= i; } this.current = number; } }, pi: function() { this.current = (this.current * Math.PI); }, square: function() { this.current = (this.current * this.current); }, squareRoot: function () { this.current = Math.sqrt(this.current); }, percent: function() { this.current = this.current / 100; }, sin: function () { this.current = Math.sin(this.current); }, cos: function () { this.current = Math.cos(this.current); }, tan: function () { this.current = Math.tan(this.current); }, log: function () { this.current = Math.log10(this.current); }, ln: function () { this.current = Math.log(this.current); }, exponent: function () { this.current += "^"; }, exp: function () { this.current = Math.exp(this.current); }, radians: function () { this.current = this.current * (Math.PI / 180); }, degrees: function () { this.current = this.current * (180 / Math.PI); } } } </script>
Сначала мы импортируем базовый и расширенный компоненты в корневой компонент, потому что нам нужно на них ссылаться.
Затем мы объявляем имя приложения и создаем объект компонентов (здесь мы регистрируем компоненты).
В разделе данных регистрируем три key/value
пары:
- current - отслеживает текущее входное значение
- toggle - сохраняет текущее значение переключения.
- mode - сохраняет текущий режим
Далее регистрируем несколько методов:
changeToggle
отвечает за переключение между базовым и расширенным режимами, а также за обновление значения mode
.
Метод doStuff
обрабатывает события, которые генерируются дочерними компонентами. Он получает параметр data
и обрабатывает его в нескольких случаях. Когда регистр соответствует, он вызывает правильную функцию для обработки математических вычислений.
Для правильной работы этого приложения определено намного больше математических функций, хотя они не включены в снимок.
Вывод
Потрясающий! 🔥 Это все, что нужно для создания научного калькулятора с VueJS.
Чтобы запустить этот проект локально, введите в терминале следующие команды.
-- clone the repository
git clone https://github.com/Jordanirabor/Scientific-Calculator-With-VueJS
-- navigate into the directory --
cd Scientific-Calculator-With-VueJS
-- install dependencies --
npm install
-- serve with hot reload at localhost:8080 --
npm run dev
-- build for production with minification --
npm run build
Конец!
Первоначально опубликовано на dev.to.