Preface
First of all, let’s not talk about creating the basic project of angularjs. It is best to use the scaffolding tool yeoman to generate it directly. If there is no such environment, of course, you can also introduce the project by downloading the angularjs file yourself.
Detailed example explanation
main.js is the main js file of the project. All js are written in this file. After initialization, the js code of the file is as follows
angular .module('calculatorApp', [ 'ngAnimate', 'ngCookies', 'ngResource', 'ngRoute', 'ngSanitize', 'ngTouch' ]) .controller('MainCtrl', function ($scope) { $scope.result=""; $scope.data={ "1":["AC","+/-","%","÷"], "2":["7","8","9","×"], "3":["4","5","6","-"], "4":["1","2","3","+"], "5":["0",","="] }; });The result here is used to bind the calculation results in two-way binding, and data is the numbers and symbols on the calculator keyboard.
All css codes related to this project are as follows:
*{ margin:0; padding:0;}body { padding-top: 20px; padding-bottom: 20px;}h1{ text-align:center; color:#3385ff;}.main{ margin:20px auto; border:1px solid #202020; border-bottom: none; width:60%; height:600px;}.result{ display: block; width: 100%; height: 30%; background:#202020; box-sizing: border-box; border:none; padding: 0; margin: 0; resize: none; color: #fff; font-size: 80px; text-align: right; line-height: 270px; overflow: hidden; background-clip: border-box;}.row{ height: 14%; background: #d7d8da; box-sizing: border-box; border-bottom: 1px solid #202020; overflow: hidden;}.col{ height: 100%; box-sizing: border-box; border-right:1px solid #202020; float: left; color: #202020; font-size: 28px; text-align: center; line-height: 83px;}.normal{ width: 25%;}.end-no{ width: 25%; border-right: none; background: #f78e11; color: #fff;}.zero{width: 50%;}.history{ background:#3385ff ; color:#fff; font-size: 22px; text-align: center;}Then the html layout is as follows:
<body ng-app="calculatorApp" ><h1>calculator for ios8</h1> <hr/> <p>{{ history.join(" ") }}</p><div> <textarea ng-model="result" ></textarea> <div ng-repeat="item in data"> <div ng-repeat="a in item" ng-class="showClass($index,a)" ng-click="showResult(a)">{{ a }}</div> </div></div></body> Here, the p tag of the class history is used to display the input record, which means that all the keys you press will be displayed on it for easy viewing the results. History is an array below the current scope, which will be explained later. Here a textarea is used as the display screen for the calculation results, mainly to use the two-way binding feature. At the same time, each key and interface element of the calculator are generated by looping over the data object. showClass method is a method below scope, which is used to obtain the class attributes of the irregular interface display elements. It will be explained later. showResult method is the main method of responding to the key. All our responses to the keys are obtained through this method, and we will explain in detail later.
The showClass method code is as follows:
//Show calculator style $scope.showClass=function(index,a){ if(a==0){ return "zero"; } return index==3||a=="="?"end-no":"normal"; };This method mainly deals with the last column of each row to be displayed as orange and the keys that display 0 must occupy two cells for special processing.
So far, the calculator interface has been fully implemented
The renderings are as follows:
The following needs to realize the response to the keys. The keys include numeric keys, operator keys, and AC keys. Each key press will have different responses and there is a connection between the keys.
In order to make the code easier to explain, a method is used to give the code of the showResult method in segments and then explain it in detail.
First of all, we need to add several variables for control and storage.
//The stack of numbers used for calculation $scope.num=[]; $scope.history=[]; //The operator stack $scope.opt=[]; //The calculation result of the calculator $scope.result=""; //The result of the calculator $scope.result=""; //It means whether to start displaying again. True means not to display again. False means to clear the current output and redisplay the number $scope.flag=true; //It means whether the operator can be entered now. If it can be true, otherwise it is false $scope.isOpt=true;
The num array is actually a stack, used to receive the numbers entered by the user. The specific usage will be explained later. The history array is all the keys entered by the user. Each time you press it, the symbols or numbers on the key are put into the stack, and then it is displayed on the interface in real time using the binding. The opt array is another stack used to receive the operator input by the user. The specific usage will be explained later. Flag is a flag. When true, it means that the number pressed during the pressing of the number is part of the currently displayed number and needs to be displayed behind it. For example, the current interface is displayed 12, and then press 3 will be judged. If true, 123 will be displayed. Otherwise, the interface will be cleared and 3.isOpt is another flag. It is mainly to prevent the user from illegal input of operators during the input process. For example, the user enters 1+2+ in succession. When input is entered here, the input below should be a number, but the user enters an operator. By judging this flag, the calculator will ignore this illegal operator and keep the input still 1+2+.
The following code is given in segments, and the complete code is to connect them together.
$scope.init=function(){ $scope.num=[]; $scope.opt=[]; $scope.history=[]; $scope.flag = true; $scope.isOpt=true; } ; $scope.showResult=function(a){ $scope.history.push(a); var reg=//d/ig,regDot=//./ig,regAbs=///ig; //If clicking on a number if(reg.test(a)) { //Eliminate freezing if($scope.isOpt==false){ $scope.isOpt=true; } if ($scope.result != 0 && $scope.flag && $scope.result != "error") { $scope.result += a; } else { $scope.result = a; $scope.flag = true; } } The init method is used to initialize some variables and flags to return them to their original state. showResult method is the main method of displaying the interface to respond to user operations. The above code is an if branch in this method, indicating that if the input of the operator is entered, then if the input to the operator has been frozen (the operator is not currently allowed to be entered, and it will be ignored after input), then when entering the number, the freezing state is unblocked so that the operator stack will be entered next time the operator is entered. If the currently displayed result is not empty and the number pressed is part of the currently displayed number and no error occurs, then the displayed result is that the currently pressed number is connected to the end of the currently displayed number. Otherwise, it means that the number you enter next time needs to be displayed after this number when redisplaying.
js code (continued)
//If you click AC else if(a=="AC"){ $scope.result=0; $scope.init(); }If the clicked AC, it means initialization, let the display result be 0, and all states are cleared.
js code (continued)
//If you click on a decimal point else if(a=="."){ if($scope.result!=""&&!regDot.test($scope.result)){ $scope.result+=a; } }If the clicked is a decimal point, let the decimal point be connected to the end of the current display if the current display is not empty and there is no decimal point in the current display result.
js code (continued)
//If you click on an inverse operator else if(regAbs.test(a)){ if($scope.result>0){ $scope.result="-"+$scope.result; } else{ $scope.result=Math.abs($scope.result); } }If the clicked is an inverse operation, the current display result will be inversely
js code (continued)
//If you click on a percentage sign else if(a=="%"){ $scope.result=$scope.format(Number($scope.result)/100); } If you click on a percent sign, divide the current displayed result by 100 and then display it. Here is a format function
The code is as follows:
//Format result output $scope.format=function(num){ var regNum=/.{10,}/ig; if(regNum.test(num)){ if(//./.test(num)){ return num.toExponential(3); } else{ return num.toExponential(); } } else{ return num; } } } else{ return num; } }Its main function is that the calculator that comes with iOS8 will not display many digits infinitely. If it exceeds 10 digits (including decimal points), scientific calculations will be used to display. For simplicity, when using scientific calculations for display results containing decimal points and exceeding 10 digits, let it retain the display of 3 digits after the decimal point.
js code (showResult part is connected)
//If the clicked operator and the current display result is not empty and error else if($scope.checkOperator(a)&&$scope.result!=""&&$scope.result!="error"&&$scope.isOpt){ $scope.flag=false; $scope.num.push($scope.result); $scope.operation(a); //After clicking the operator once, you need to ignore the situation where the operator is clicked again. $scope.isOpt=false; }This branch is the most complex branch, which means that if the input is an operator, then the operation is to be performed. To enter this branch, you need to set flag to false first, which is to input the number next time, which is to re-enter the number instead of inputting the current display result.
Then, let the currently displayed number as the number being computed to enter the number stack first. operation method is the operation method. Because an operator has been clicked this time, the next time you click it, you must ignore this operator and set isOpt to false.
The operation code is as follows
//Compare the priority of the currently input operator and the operator stack top operator//If the priority of the top operator is small, the current operator is put on the stack and does not calculate it. // Otherwise, the top operator is put on the stack, and the number stack is put on the stack for two elements in succession, and calculate //Then the current operator is put on the stack. $scope.operation=function(current){ //If the operator stack is empty, directly put the current operator on the stack if(!$scope.opt.length){ $scope.opt.push(current); return; } var operator,right,left; var lastOpt=$scope.opt[$scope.opt.length-1]; //If the current operator priority is greater than the last operator, only the stack is entered if($scope.isPri(current,lastOpt)){ $scope.opt.push(current); } else{ operator=$scope.opt.pop(); right=$scope.num.pop(); left=$scope.num.pop(); $scope.calculate(left,operator,right); $scope.operation(current); } };This method accepts the currently input operator as a parameter. The core idea is that an operator is currently received. If the operator stack is empty, the current operator will be put on the stack, and then there is no need to do anything else in this case. If the current operator stack is not empty, then the top element of the current operator stack is popped up, so that the currently received operator and the top operator are given priority (the priority of multiplication and division is greater than addition and subtraction, and the top operator is given priority of the same priority because it is first entered). The isPri method is used to determine priority and receive two parameters. The first is the currently received operator and the second is the top-of-stack operator on the stack. If the current operator has a higher priority according to the rules mentioned above, then the operator will be directly put on the stack. If the priority of the current operator is smaller than the top of the stack operator, then you need to perform calculations and change the display of the calculator. The two elements on the top of the stack of the operation number are popped up in turn, as the two operating numbers for one operation, and then the top of the stack of the operator stack is popped up as the operator for this operation, and the calculation method is called for the calculation method for operation.
The method code is as follows
//Responsible for calculating the result function $scope.calculate=function(left,operator,right) { switch (operator) { case "+": $scope.result = $scope.format(Number(left) + Number(right)); $scope.num.push($scope.result); break; case "-": $scope.result = $scope.format(Number(left) - Number(right)); $scope.num.push($scope.result); break; case "×": $scope.result = $scope.format(Number(left) * Number(right)); $scope.num.push($scope.result); break; case "÷": if(right==0){ $scope.result="error"; $scope.init(); } else{ $scope.result = $scope.format(Number(left) / Number(right)); $scope.num.push($scope.result); } break; default:break; } };This method accepts three parameters, the left operation number, the middle operator and the right operation number, and changes the result to display the result after adding, subtracting, multiplication and division operation, and puts the calculation result into the stack of the calculation number. Here it is necessary to note that if the calculation is division and the divisor is 0, an error occurs, display errors, and all states are cleared, otherwise the calculation is normal.
After one operation is completed, the states in the operator stack and the number stack will be changed, and the current key current value has not been put into the stack. Therefore, the above process must be repeated for priority comparison and then the operation is performed. In fact, it is a recursive process until the operator stack is empty or the priority of the current operator is higher than the operator top of the operator stack. The isPri method is used to determine the priority of the operator.
The code is as follows:
//Judge whether the current operator has higher priority than last, if it is returned true // Otherwise, return false $scope.isPri=function(current,last){ if(current==last){ return false; } else { if(current=="×"||current=="÷"){ if(last=="×"||last=="÷"){ return false; } else{ return true; } } else{ return false; } } };The judgment rules have been described earlier.
In addition, there is a checkOperator method, which is to determine whether the input symbol is the four operator symbols of addition, subtraction, multiplication and division.
The code is as follows:
//Judge whether the current symbol is an operable symbol $scope.checkOperator=function(opt){ if(opt=="+"||opt=="-"||opt=="×"||opt=="÷"){ return true; } return false; }If so, return true, otherwise return false.
So far, there is still a branch with input equal to the number that has not
The code is as follows (connected to showResult method)
//If the clicked is the equal sign else if(a=="="&&$scope.result!=""&&$scope.result!="error"){ $scope.flag=false; $scope.num.push($scope.result); while($scope.opt.length!=0){ var operator=$scope.opt.pop(); var right=$scope.num.pop(); var left=$scope.num.pop(); $scope.calculate(left,operator,right); } } };If the input is an equal sign, first set flag to false, allowing the interface to redisplay the next time you enter the number, and the currently displayed number must be put into the stack as a calculation number. Then you need to perform continuous stacking operations until the operator stack is empty before it can stop.
Summarize
The above is the main code and process of the implementation. Since there are many branch codes, all branches are given at once and cannot be described in detail. Therefore, the showResult method is separated, which may not be adaptable. Since the writing was rush and did not take too much time to test, there may be some bugs, please point them out. At the same time, due to limited levels, this method may not be the best. Welcome to give a better plan to communicate and learn together~~The above is the entire content of this article. I hope it will bring some help to everyone's study or work.