Front-end and back-end separation projects based on Node.js and express frameworks and native JS

Front-end and back-end separation projects based on Node.js and express frameworks and native JS: One step closer!

Before Start

Ha ha, it’s me again! As you can see, this is second assignment for the course Software Engineering.

First let me record some basic information.

Course for This AssignmentSoftware Engineering
Assignment RequirementsActual Program(A calculator) and a blog
Objectives of This AssignmentImplement a calculator based on web
MU ID and FZU ID21126135 | 832102117

Description

This blog will show you how to write front-end and back-end code to implement a web calculator that can recover history.

Technology stack

Front-end: native html+JS+css

Back-end: Node.js+express

Git Repository Link and Code Standards Link

Front-end

Deployed on Github Pages

Back-end

CodeStyle

PSP Table

Personal Software Process StagesEstimated Time(minutes)Actual Time(minutes)
Planning157
• Estimate1510
Development300500
• Analysis105
• Design Spec105
• Design Review105
• Coding Standard55
• Design55
• Coding220410
• Code Review1010
• Test3055
Reporting5050
• Test Repor1013
• Size Measurement1525
• Postmortem & Process Improvement Plan2513
Sum365557

Presentation of the Finished Product

演示

Design and Implementation Process

Implemented features

✔️On the basis of the last experiment, we added and improved a variety of functions, including the remainder (%), power, root and other operations in addition to the basic addition, subtraction, multiplication and division

✔️More like a scientific calculator, supporting sin, cos, log and root operations

✔️Use Node.js and express to build a basic local server to receive and send http requests

✔️The historical record of the last calculation can be restored

✔️Better looking UI

Design idea

  1. Add more calculator buttons on the basis of the original, improve the existing functions

  2. Change the overall layout of the calculator design

  3. Use the browser’s own asynchronous ajax request api to try to connect with the server running on Node.js locally

  4. Record the calculation process and save it as a text file

Code Explanation

Front-end

Project structure

img

Code rendering

First of all, I want to state again: this assignment will not repeat the code explanation of the first assignment, if you are confused by the following code, please go to my first assignment.

This is the front end, and I’ll just explain the core code, which is the native JS that I added based on the first assignment.

/*
 * @Descripttion: 计算器核心逻辑
 * @Author: Chen Zhengyi
 * @Date: 2023-10-10 19:36:13
 * @Latest Update: 2023-10-22 20:13
*/

//js主线程
var panel = document.getElementsByClassName("operation-zone")[0];
var btnNameList = ["C", "B", "/", "*", "ans", "7", "8", "9", "-", "%", "4", "5", "6", "+", "√", "1", "2", "3", "log", "=", ".", "0", "^", "sin", "cos"];
initButton(25);
var baseURL = "http://127.0.0.1:8081"
// 以下是各绑定函数

//1.cal()函数负责对输入内容进行基本计算,包括计算顺序、取值,涵盖加减乘除和幂运算等
function calc(value) {
    //定义运算符数组
    let operators = [];
    //使用正则表达式匹配基本运算符号,即加减乘除幂,并将计算数存入数组
    let nums = value.split(/[\+\-\*\/\^%]/);
    for (let i = 0; i < value.length; i++) {
        if ((value[i] == '+' || value[i] == '-' || value[i] == '*' || value[i] == '/' || value[i] == '^' || value[i] == '%')) {
            if ((i != 0 && i != value.length - 1) && (value[i + 1] != '+' && value[i + 1] != '-' && value[i + 1] != '*' && value[i + 1] != '/') && value[i + 1] != '^' && value[i + 1] != '%') {
                operators.push(value[i]);
            }
            else if (i == 0 && (value[i] == '+' || value[i] == '-')) {
                nums[0] = '0';
                //对出现在第一位的运算符号判断合理性
                operators.push(value[i]);
            }
            //若不满足匹配条件,则返回error
            else {
                return "error";
            }
        }
    }
    //根据数组和运算符号进行运算,先乘除幂余,后加减
    for (let i = 0; i < operators.length; i++) {
        if (numSwitch(nums[i]) != "error" && numSwitch(nums[i + 1]) != "error") {
            if (operators[i] == '*') {
                let product = numSwitch(nums[i]) * (numSwitch(nums[i + 1]))
                nums[i] = "" + product;
                nums.splice(i + 1, 1);
                operators.splice(i, 1);
                i = -1;
            }
            else if (operators[i] == '/') {
                if (numSwitch(nums[i + 1]) != 0) {
                    let division = numSwitch(nums[i]) / (numSwitch(nums[i + 1]));
                    nums[i] = "" + division;
                    nums.splice(i + 1, 1);
                    operators.splice(i, 1);
                    i = -1;
                }
                else { return "error" }
            }
            else if (operators[i] == '^') {
                let power = Math.pow(numSwitch(nums[i]), numSwitch(nums[i + 1]));
                nums[i] = "" + power;
                nums.splice(i + 1, 1);
                operators.splice(i, 1);
                i = -1;
            }
            else if (operators[i] == '%') {
                if (numSwitch(nums[i + 1]) != 0) {
                    let remain = numSwitch(nums[i]) % (numSwitch(nums[i + 1]));
                    nums[i] = "" + remain;
                    nums.splice(i + 1, 1);
                    operators.splice(i, 1);
                    i = -1;
                }
                else { return "error" }
            }
        }
        else { return "error" }
    }

    for (let i = 0; i < operators.length; i++) {
        if (operators[i] == '+') {
            let addition = numSwitch(nums[i]) + (numSwitch(nums[i + 1]))
            nums[i] = "" + addition;
            nums.splice(i + 1, 1);
            operators.splice(i, 1);
            i = -1;

        }
        else if (operators[i] == '-') {
            let substraction = numSwitch(nums[i]) - (numSwitch(nums[i + 1]))
            nums[i] = "" + substraction;
            nums.splice(i + 1, 1);
            operators.splice(i, 1);
            i = -1;
        }
    }
    if (operators[0] == null) {
        return numSwitch(nums[0]);
    }
    else return "error";
}

//3.adjustBtn() 调整按钮的数量布局和大小
function adjustBtn(btn) {
    btn.style.height = btn.offsetWidth + "px";
}

//4.initButton() 初始化按钮
function initButton(btnNum) {
    for (let i = 0; i < btnNum; i++) {
        let btn = document.createElement("button");
        panel.appendChild(btn);
        btn.setAttribute("class", "btn");
        //设置DOM元素属性并转化为实际html元素渲染
        btn.setAttribute("id", btnNameList[i]);
        let id = btn.getAttribute("id");
        btn.setAttribute("value", id);
        btn.innerHTML = id;
        let addFun = "oper('" + id + "')";
        btn.setAttribute("onclick", addFun);
        adjustBtn(btn);
    }
}

//5.oper() 按钮绑定事件
function oper(value) {
    let inputZone = document.getElementById("input");
    let input = inputZone.value;

    if (value == '=') {
        const xhr = new XMLHttpRequest();

        //2.配饰请求方法,设置POST请求接口地址
        xhr.open('post', baseURL + "/api/addStorage");
        xhr.setRequestHeader('Content-Type', 'text/plain')
        //3.发送请求
        xhr.send(input);

        //4.网络请求返回的数据
        // xhr.readystate===4代表响应完成了,xhr.status === 200 代表请求成功
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                alert("ok");
            }
        }
        inputZone.value = calc(input);
    }
    else if (value == 'B') {
        let newVal = input.substring(0, input.length - 1);
        // document.getElementById("input").setAttribute("value", newVal);
        inputZone.value = newVal;
    }
    else if (value == 'C') {
        // document.getElementById("input").setAttribute("value", "");
        inputZone.value = "";
    }
    else if (value == 'ans') {
        // document.getElementById("input").setAttribute("value", "");
        //1.创建请求对象
        const xhr = new XMLHttpRequest();
        //2.配饰请求方法,设置GET请求接口地址
        xhr.open('get', baseURL + "/api/storage");
        //3.发送请求
        xhr.send();
        //4.网络请求返回的数据
        // xhr.readystate===4代表响应完成了,xhr.status === 200 代表请求成功
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                var res = xhr.responseText;
                inputZone.value = res;
            }
        }
    }
    else {
        let newVal = input + value;
        inputZone.value = newVal;
    }
}
//6.numSwitch() 对分离的操作数内部进行特殊计算
function numSwitch(num) {
    let result = 1;
    let regMatchSpecial = /sin|cos|√|log/;
    let nums = num.split(regMatchSpecial);
    let operators = num.match(regMatchSpecial);
    if (nums[0] != '') { result *= parseFloat(nums[0]) }
    for (let i = 1; i < nums.length; i++) {
        if (nums[i] == '' || nums[i].split('.').length == 3 || nums[i].split(".")[0] == '') { return "error" }
        switch (operators[i - 1]) {
            case "sin":
                result *= Math.sin(parseFloat(nums[i]) * Math.PI / 180)
                break;
            case "cos":
                result *= Math.cos(parseFloat(nums[i]) * Math.PI / 180)
                break;
            case "√": {
                result *= Math.sqrt(parseFloat(nums[i]))
                break;
            }
            case "log": {
                result *= Math.log(parseFloat(nums[i]))
                break;
            }
            default:
                return "error"
        }
    }
    return result;
}

Firstly, I added corresponding symbols and other special symbolic operations on the basis of the original, such as exponents and complementary operations, etc. I added them to the two functions oper () and numSwitch().

Second, the number of buttons has changed from 20 to 25.

Then there are the two more important buttons, one is “=” and the other is the new “ans” button.

They relate to GET and POST requests in http requests, and need to store the result of the calculation in the form of a string in the back-end server, and can be read at any time.

I used the browser’s built-in api for sending httpRequest requests, or ajax requests.

Data is received and sent by changing the corresponding request message and identifying the server status code.

There is no need to explain the rest, because it is too simple.

Back-end

Project structure

img

Code rendering

I used Node.js and express to build the server this time.

The express package provides a number of powerful apis, also supports handling json and urlencoded content types, and its syntax is relatively concise.

Since only the most recent history was required for this assignment, I did not use databases such as MySQL and MongoDB because there was really no need to add code to store a single piece of data.

Let’s continue to look at the code, as usual, first:

 npm i express

to install the package dependency of express.

The second is to solve the cross-domain problem, because I have a lot of relevant experience, so I took the method of adding cors request header to solve the browser same-origin policy.

Ok, now it’s a matter of sending and storing data.

According to the GET and POST requests from the front end, we write the corresponding interface and execute the corresponding callback function.

Then, for the POST request, I choose to save the record locally.

const express = require('express');
const fs = require('fs')
const serverApp = express();
var allowCors = function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    // 新增加cors 运行的请求头
    res.header('Access-Control-Allow-Headers', 'Content-Type,lang,sfopenreferer ');
    res.header('Access-Control-Allow-Credentials', 'true');
    next();
};
serverApp.use(allowCors);//使用跨域中间件
serverApp.use(express.urlencoded());
serverApp.use(express.json());
serverApp.use(express.text());
var port = 8081;

function getLastInfo(res) {
    fs.readFile('./Record.txt', 'utf8', (err, result) => {
        if (err) { return console.log('历史记录读取失败!') }
        res.send(result);
    })
}
var addRecent = function (recordInfo) {
    fs.writeFile('./Record.txt', recordInfo, (err) => {
        if (err) {
            return console.log('写入失败!')
        }
        console.log('计算结果写入成功!');
    })
}
serverApp.get('/api/storage', (err, res) => {
    getLastInfo(res);
})
serverApp.post('/api/addStorage', (req, res) => {
    let recordInfo = req.body;
    addRecent(recordInfo);
    res.send('ok');
})
serverApp.listen(port, () => { console.log(`服务器开启成功!端口号:${port}.`) });

Feel free to contact me if you don’t understand.

Personal Journey and Learnings

img

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值