Power Apps, How many ways to make a Calculator
Tags: powerapps, powerplatform, lowcode, howto
Published: 2023-04-26 08:13:55

LowCode is sometimes thought of as simple and repeatable. There are fewer building blocks so there is only one way to solve a problem. And although this is kind of true, it's not quite. 9 times out of 10 there will be an out of the box solution, that is quick and easy to implement, but there are times and ways to be more creative. And that is where most of the fun comes from 😎 

One of the few missing out of the box solutions in Power Apps is calculators, so I thought I would look at all the different ways we could create a calculator, and what I can learn.

So how many ways can you create a calculator, well I found at least 5:

1. [Standard Buttons](#standard)
2. [Gallery](#gallery)
3. [Code](#code)
4. [Timer](#timer)
5. [Abacus](#abacus) 🧮

---

## 1. Standard <a name="standard"></a>

![Animated gif of Power App calculator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/twx1fmdutgoudlmf5qk7.gif)

The idea is pretty simple, each number and operator is its own button component. We have 5 variables:#

- vsOne = First number
- vsTwo = Second number (set to 99999999999999999999 so we know it's not used)
- vsCalc = Operator
- vsEquals = Show result
- viValue = result

In a nut shell we hold first value in vsOne and on operator we store operator and set vsTwo to "" and store next numbers in there. Then on equals we use switch based on operator to complete the action.

**Reset Button**
```
Set(vsOne,"0");
Set(vsTwo,"99999999999999999999");
Set(vsCalc,"");
Set(vbEquals,false);
Set(viValue,0);
```
**Number Button**
```
If(vsTwo="99999999999999999999",
    Set(vsOne,Value(vsOne& Self.Text)&"")
,
    Set(vsTwo,Value(vsTwo& Self.Text)&"")
)
```
Operator Button (+)
```
Set(vsCalc,Self.Text);   
If(vsTwo="99999999999999999999",       
    Set(vsTwo,"0");
,
    Set(viValue,Value(vsOne) + Value(vsTwo));
    Set(vsOne,viValue&"");
    Set(vsTwo,"0");
)
```
**Equals Button**
```
Switch(vsCalc,
    "+",Set(viValue,Value(vsOne) + Value(vsTwo)),
    "-",Set(viValue,Value(vsOne) - Value(vsTwo)),
    "*",Set(viValue,Value(vsOne) * Value(vsTwo)),
    "/",Set(viValue,Value(vsOne) / Value(vsTwo))
);
Set(vsOne,viValue&"");
Set(vsTwo,"99999999999999999999");
```

## 2. Gallery<a name="gallery"></a>
![Gallery based caculator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qhv2ozbc3ptvi4yo3lqc.png)

The Gallery approach is similar to the buttons, but has the advantage of automatically positioning all the buttons.

The gallery requires the following array of items:
["1","2","3","4","5","6","7","8","9","0","+","-","/","*","=","C"]

We then roll all the OnSelect code as they all the buttons use the same code.

**All Buttons**
```
If(IsNumeric(Self.Text),
    If(vsTwo="99999999999999999999",
        Set(vsOne,Value(vsOne& Self.Text)&"")
    ,
        Set(vsTwo,Value(vsTwo& Self.Text)&"")
    )
,
    If(Self.Text="=",
        Switch(vsCalc,
            "+",Set(viValue,Value(vsOne) + Value(vsTwo)),
            "-",Set(viValue,Value(vsOne) - Value(vsTwo)),
            "*",Set(viValue,Value(vsOne) * Value(vsTwo)),
            "/",Set(viValue,Value(vsOne) / Value(vsTwo))
        );
        Set(vsOne,viValue&"");
        Set(vsTwo,"99999999999999999999");
    ,
        If(Self.Text="C",
            Set(vsOne,"0");
            Set(vsTwo,"99999999999999999999");
            Set(vsCalc,"");
            Set(vbEquals,false);
            Set(viValue,0);
            Set(sPrint,"");
        ,
            Set(vsCalc,Self.Text);   
            If(vsTwo="99999999999999999999",       
                Set(vsTwo,"0");
            ,
                Switch(Self.Text,
                    "+",Set(viValue,Value(vsOne) + Value(vsTwo)),
                    "-",Set(viValue,Value(vsOne) - Value(vsTwo)),
                    "*",Set(viValue,Value(vsOne) * Value(vsTwo)),
                    "/",Set(viValue,Value(vsOne) / Value(vsTwo))
                );
                Set(vsOne,viValue&"");
                Set(vsTwo,"0");
            )
        )           
    )
)
```

## 3. Code<a name="code"></a>

![Animited gif of keyboard based calculator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13bifggpz7wlvbxlc4qs.gif)

This one I personally think is pretty cool, instead of using buttons why not user the keyboard.

We have a similar approach but this time we don't save to variables but a single row in a collection

Unfortunately there was no easy way to use a Regex (well one I could find) so my approach was to split the input into an array and then loop over each character, saving/appending to the single row array (which we use as an object to store variables as you can't set in a ForAll loop). The logic is

- If is number and no operator saved append to key one (as this is the first number in the formula)
- If is number but operator saved append to key two (as this is the second number in the formula)
- Else set to operator (as we know its not a number)
- After ForAll finished use operator to select formula and display results

![Loop logic diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1wjh5rmg0wombmdqbi59.png)
  
**Equals Button**
```
ClearCollect(colInput,Split(inCalcCode.Text,""));
Set(viValue,0);
ClearCollect(colResult,{one:"",operator:"",two:"",value:0,id:1});
ForAll(colInput,
    If(IsNumeric(ThisRecord.Value)&&Index(colResult,1).operator="",
        Patch(colResult,{id:1},{
            one:Index(colResult,1).one&ThisRecord.Value}
        )
    ,
        If(IsNumeric(ThisRecord.Value),
            Patch(colResult,{id:1},{
                two:Index(colResult,1).two&ThisRecord.Value}
            )
        ,
            Patch(colResult,{id:1},{
                operator:ThisRecord.Value}
            )
        )
    )
);
Switch(Index(colResult,1).operator,
    "+",Set(viValue,Value(Index(colResult,1).one) + Value(Index(colResult,1).two)),
    "-",Set(viValue,Value(Index(colResult,1).one) - Value(Index(colResult,1).two)),
    "*",Set(viValue,Value(Index(colResult,1).one) * Value(Index(colResult,1).two)),
    "/",Set(viValue,Value(Index(colResult,1).one) / Value(Index(colResult,1).two))
);
Set(vbResult,true);

```

## 4.Timer <a name="timer"></a>

![Timer based power app calculator](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/49bs8tik42pkdlmz9ua5.png)

As I have mentioned before, Timers are another more flexible way to do loops in Power Apps.
The approach is similar to the Code (ForAll loop) approach with a few small differences.

As this is more a For loop we don't increment over each item but count and do an index comparison. And as we can now Set variables we don't need our single row collection.

**Equals Button**
```
ClearCollect(colInput,Split(inCalcTimer.Text,""));
Set(vsOne,"");
Set(vsNumber,"");
Set(viValue,0);
Set(i,1);
Set(vbResult,false);
Set(vsCalc,"");
Set(vbCalc,true);
```
This time we are going to:
- Incement over every item from the split string. When we hit the length we stop the loop, select the formula and show the result.
- If less then length we check if is number and vsCalc is blank (as then we know if first or second part of formula). 
- If it is both we append to vsNumber
- If it is number but vsCalc is not blank then we check to see if vsOne is blank, if it is we copy vsNumber over and clear vsNumbner (ready for second number in formula). Then we append again to vsNumber.
- Finally if it is not numeric then we save is as vsCal as we know it is the operator.

![Timer Loop process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uk6o2jlwoepv9krzviad.png)
**Start**
```
vbCalc
```
**Repeat**
```
vbCalc
```
**OnTimerEnd**
```
If(i>CountRows(colInput),
    Set(vbCalc,false);
    Set(vbResult,true);
    Switch(vsCalc,
        "+",Set(viValue,Value(vsOne) + Value(vsNumber)),
        "-",Set(viValue,Value(vsOne) - Value(vsNumber)),
        "*",Set(viValue,Value(vsOne) * Value(vsNumber)),
        "/",Set(viValue,Value(vsOne) / Value(vsNumber))
    );
    Reset(inCalcTimer);
,
    If(IsNumeric(Index(colInput,i).Value),
        If(vsCalc="",
            Set(vsNumber,vsNumber&Index(colInput,i).Value);
        ,
            If(vsOne="",Set(vsOne,vsNumber);Set(vsNumber,""));
            Set(vsNumber,vsNumber&Index(colInput,i).Value);          
        )
    ,
          Set(vsCalc,Index(colInput,i).Value)  
    )
);
Set(i,i+1);
```

## 5.Abacus<a name="abacus"></a>

Want to go truly innovative, then lowCode and Power Apps has got your back. Why use a calculator when you can use an abacus.

![Animated gif of abacus app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wn8vxywwiydli5vqupfe.gif)

The abacus is built by aligning 2 gallery's, with the right the unused beads and the left used.
The gallery is populated by 2 matching collections

**Screen OnVisible / Clear Button OnSelect**
```
ClearCollect(colSelected,
    {type:"ones",show:"|",val:""},
    {type:"tens",show:"X",val:""},
    {type:"hundreds",show:"C",val:""},
    {type:"thousands",show:"M",val:""},
    {type:"tenthousnads",show:"̅X̅",val:""}
);
ClearCollect(colUnSelected,
    {type:"ones",show:"|",val:"||||||||||"},
    {type:"tens",show:"X",val:"XXXXXXXXXXX"},
    {type:"hundreds",show:"C",val:"CCCCCCCCCC"},
    {type:"thousands",show:"M",val:"MMMMMMMMMM"},
    {type:"tenthousnads",show:"̅X̅",val:"X̅X̅X̅X̅X̅X̅X̅X̅X̅X̅"}
)
```
The gallery has a lable showing the collection val, on Select of the label with patch the value with length -1 and patch the other collection with its val and the show.

**Gallery label OnSelect**
```
If(ThisItem.val<>"",
    Patch(colUnSelected,{type:ThisItem.type},
        {val:Left(ThisItem.val,Len(ThisItem.val)-1)}
    );
    Patch(colSelected,{type:ThisItem.type},
        {val:LookUp(colSelected,type=ThisItem.type).val&ThisItem.show}
    );
)
```

The last step is converting the colSelected item val's to a number.

**Text of Result label**
```
(Len(Index(colSelected,1).val)*1)+
(Len(Index(colSelected,2).val)*10)+
(Len(Index(colSelected,3).val)*100)+
(Len(Index(colSelected,4).val)*1000)+
(Len(Index(colSelected,5).val)*10000/2)
```

---

As you can see there are loads of different ways to solve any App solution, it shows how important a good design review is to help plan before development.

A copy of the app can be found [here](https://github.com/wyattdave/Power-Platform/tree/main/Power%20App%20Artifacts)