We see a lot of apps built with Calcapp that simply collect information and send it back to the office. But what if the user should only be allowed to send the data once per day?
Prior to our last release, it wasn’t possible for an app to enforce such a rule. Now, though, you can use formula buttons, in conjunction with persistent fields, to make this a reality.
The technique described in this post is applicable to many other kinds of apps. We will demonstrate a technique enabling buttons to store information — any information — for subsequent use when the user comes back to the app, even after restarting their device.
You’re encouraged to view the app and its formulas as you read along. It’s available as a template when you create a new app under the Sample: Daily yield report name.
Usage
Without further ado, here’s the app:
The user is expected to fill out the yield once per day. When the button is pressed, the data is emailed back to the office and a message appears. If the user has never submitted the yield before, a simple message informs the user that the data has been submitted. Otherwise, the yield difference is also displayed:
If the user tries to submit the yield again the same day, this message appears:
How it works
The key to the app is the use of a formula button, which runs formulas in response to being pressed. Refer to our blog post from August to learn more.
Action formulas have access to special functions. Here, we’ll use the EMAILREPORT function to email data back to the office.
Action formulas also have access to :=, which assigns values to
properties. For instance, :=
can be used to change the
values of fields.
The key to making this app work is that it has two hidden fields, which are also persistent. A persistent field stores its value to the user’s device, which is read back when the app is started again.
(Unfortunately, that means that the user is allowed to submit the yield on their cell phone and on their desktop computer, all on the same day.)
The field where the user enters the yield is named Yield. There’s also a hidden, persistent field named LastYield. A final hidden, persistent field is LastSubmissionDate.
The formula button is grayed-out if the user hasn’t filled in the Yield field. To achieve that, this formula is associated with the Enabled property of the button:
Next, here’s part of the action formula associated with the OnPress property of the formula button:
return tomorrow.", "Warning"), …)IF(ISDEFINED(LastSubmissionDate) && (TODAY() <= LastSubmissionDate); ALERT("A yield has already been reported today, please
return tomorrow."; "Warning"); …)
Above, we’re only looking at a part of the formula. The elided part
has been replaced with …
The formula above checks the LastSubmissionDate field and its value. If it is defined (meaning that it holds a value) and it is equal to the return value of TODAY, or greater than it, a warning message is displayed using the ALERT function, warning the user that they have already pressed the button today.
The first time the app is started, LastSubmissionDate does not hold a value, meaning that ISDEFINED(LastSubmissionDate)ISDEFINED(LastSubmissionDate) returns FALSE and the message isn’t shown. (&& means “and”.)
Today’s date should be stored in the LastSubmissionDate field when the button is pressed. To do so, this formula fragment must be run by the button:
The next time the button is pressed, ISDEFINED(LastSubmissionDate)ISDEFINED(LastSubmissionDate) returns TRUE and the next part of the formula, TODAY() <= LastSubmissionDateTODAY() <= LastSubmissionDate is evaluated. That part only returns TRUE if the value stored in LastSubmissionDate indicates that the button was last pressed on the same day (or in the future), which makes the button display the warning message.
We also need to store the yield when the button is pressed, so that we can show the change in the submission message:
Here’s the full formula fragment that was elided with …
in the action formula above, which is run if the warning message
isn’t displayed:
difference: " & FORMATNUMBER((Yield - LastYield) / Yield * 100, 0, 2) & "%"), "Success"); LastYield := Yield; LastSubmissionDate := TODAY()EMAILREPORT({ Yield }; "office@example.com");; ALERT("Submission successful." & IF(ISDEFINED(LastYield); NEWLINE() & "Yield
difference: " & FORMATNUMBER((Yield - LastYield) / Yield * 100; 0; 2) & "%"); "Success");; LastYield := Yield;; LastSubmissionDate := TODAY()
EMAILREPORT({ Yield }, "office@example.com")EMAILREPORT({ Yield }; "office@example.com") sends email to the office, containing just the yield. (A real app should probably also collect the user’s identity and send that as part of the report.)
The ALERT part of the formula shows a message to the user, informing them that the submission was successful. It also displays the yield difference using this formula fragment:
FORMATNUMBER above takes a number and formats it. In this case, it uses a minimum of zero decimal places, and a maximum of two decimal places.
& joins text strings together. Above, it adds a percentage sign to the end of the number displayed to the user.
Finally, the formula fragment assigns values to the hidden, persistent fields LastYield and LastSubmissionDate, which is the information that makes the app work.
Taking it all together, here’s the full action formula:
return tomorrow.", "Warning"), EMAILREPORT({ Yield }, "office@example.com"); ALERT("Submission successful." & IF(ISDEFINED(LastYield), NEWLINE() & "Yield
difference: " & FORMATNUMBER((Yield - LastYield) / Yield * 100, 0, 2) & "%"), "Success"); LastYield := Yield; LastSubmissionDate := TODAY())IF(ISDEFINED(LastSubmissionDate) && (TODAY() <= LastSubmissionDate); ALERT("A yield has already been reported today, please
return tomorrow."; "Warning"); EMAILREPORT({ Yield }; "office@example.com");; ALERT("Submission successful." & IF(ISDEFINED(LastYield); NEWLINE() & "Yield
difference: " & FORMATNUMBER((Yield - LastYield) / Yield * 100; 0; 2) & "%"); "Success");; LastYield := Yield;; LastSubmissionDate := TODAY())