Введение
В этой статье рассказывается, как создать научный калькулятор с использованием 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>
Мы видим, что buttonClickmethod в расширенном компоненте точно такой же, как в базовом компоненте.
Давайте углубимся в корневой компонент!
Наконец, давайте соединим все приложение вместе через корневой компонент [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.