VB6 - Exchange rates example
File(s): frmExchangeRatesDemo.frm, html\exchangeRates.html, html\exchangeRates.js
The Exchange Rates demo has the following html:
<html>
<head>
<link rel="StyleSheet" href="exchangeRates.css" type="text/css">
<script language="JavaScript" src="https://cdn.jsdelivr.net/npm/chart.js@3.3.2/dist/chart.min.js" type="text/javascript"></script>
</head>
<body>
<div class="card">
<h1>Exchange Rate</h1>
<form class="hiddencontainer">
<div class="row">
<div class="value" id="currencyRate"></div>
<label for="result">Result =</label>
<div id="result"></div>
<div id="resultCurrency"></div>
</div>
</form>
<div class="container">
<canvas id="graph" width="300" height="300"></canvas>
</div>
</div>
<script language="JavaScript" src="exchangeRates.js" type="text/javascript"></script>
</body>
</html>
as you can see there's a bit of javascript involved as well. Let us have a look at what is in the exchangeRates.js file.
var currencyChart;
function configureChart() {
const ctx = document.getElementById("graph").getContext("2d");
currencyChart = new Chart(ctx, {
type: "line",
data: {
labels: "",
datasets: []
}
})
}
function queryRates(firstValue,firstCurrency,secondCurrency) {
let typingTime = null;
const url = 'api.frankfurter.app';
clearTimeout(typingTime);
typingTime = setTimeout(() => {
async function fetchCurrency() {
let res = await fetch(`https://${ url }/latest?from=${ firstCurrency }`);
let dataRes = await res.json();
for (const [key, value] of Object.entries(dataRes.rates)) {
if (secondCurrency === key) {
document.querySelector(".value").innerText = value;
document.querySelector("#result").innerText = (firstValue * value).toFixed(2);
document.querySelector("#resultCurrency").innerText = secondCurrency;
}
}
}
fetchCurrency();
}, 1000)
return document.querySelector(".value").innerText;
}
function displayGraph(fromCHis,toCHis) {
const url = 'api.frankfurter.app';
async function fetchHistory() {
let resHistory = await fetch(`https://${ url }/2020-01-04..?from=${fromCHis}&to=${toCHis}`);
let dataResHistory = await resHistory.json();
const xlabels = [];
const ylabels = [];
for (const [key, value] of Object.entries(dataResHistory.rates)) {
xlabels.push(key);
ylabels.push(value);
}
const yScale = [];
ylabels.forEach(y => {
yScale.push(y[`${toCHis}`]);
})
currencyChart.data = {
labels: xlabels,
datasets: [{
label: 'Exchange rate',
data: yScale,
borderColor: '#0000ff',
}]
}
currencyChart.update();
}
fetchHistory();
}
configureChart();
OK.. that's a bit too much to explain line by line, but the gist is as follows.
The first thing it runs is at the bottom.
The configureChart function is called to configure the chart as is defined via the npm chart.min.js javascript include.
Then there's two functions.
1. The queryRates function that asks the api.frankfurter.app API for the current exchange rate for the value in firstValue and from currency firstCurrency in currency secondCurrency.
2. The displayRates function, also queries the api.frankfurter.app API, but this time for drawing the history chart.
As you can imagine, these API calls take time to execute and you don't want your application to hang if there's no timely response (if at all)
Now have a look at the Visual Basic side of things.
The "Get Rates" button has the initial logic.
Private Sub GetRatesButton_Click()
Dim nValue As Variant
Dim sCurFrom As String
Dim sCurTo As String
nValue = CDec(AmountTextBox.Text)
If nValue <> 0 Then
sCurFrom = FromCombo.Text
sCurTo = ToCombo.Text
If sCurFrom <> sCurTo Then
RateTextBox.Text = "working.."
ResultTextBox.Text = ""
QueryRatings
Else
MsgBox "That's a rate of 1, no need to ask that."
End If
Else
MsgBox "Enter an amount first."
End If
End Sub
First we get the amount value from the amount TextBox control and also the comboform values "from" and "to" for the currencies.
Once you enter an amount and change the "from" and "to" currencies so that they don't match, the QueryRatings function is called.
Private Sub QueryRatings()
Dim nValue As Variant
Dim sCurFrom As String
Dim sCurTo As String
Dim jScript As String
Dim varParams(3) As Variant
jScript = "(amount,curFrom,curTo) => {" & vbCrLf
jScript = jScript & " displayGraph(curFrom,curTo);" & vbCrLf
jScript = jScript & " let obj = document.getElementById('currencyRate');" & vbCrLf
jScript = jScript & " if (obj !== undefined) {" & vbCrLf
jScript = jScript & " obj.innerText = '-1'" & vbCrLf ' -1 is initialized..
jScript = jScript & " }" & vbCrLf
jScript = jScript & " queryRates(amount,curFrom,curTo);" & vbCrLf
jScript = jScript & "}" & vbCrLf
nValue = CDec(AmountTextBox.Text)
sCurFrom = FromCombo.Text
sCurTo = ToCombo.Text
varParams(0) = nValue
varParams(1) = sCurFrom
varParams(2) = sCurTo
Document.RunAnonymousFunction 2, varParams, jScript
'
' We use a timer to poll for the results
Timer.Enabled = True
End Sub
We're taking the currency from, currency to and the amount and pass that to the anonymous javascript function, we also identify this as function "2".
The javascript itself calls both displayGraph as well as the queryRates function and it starts a timer.
As the API does not return immediately, we want to check the currencyRate html element for the result.
Again and again, until there's a result.
Let's look at the function the timer runs:
Public Sub OnTimer()
Dim jScript As String
Dim varParams(0) As Variant
jScript = " () => {" & vbCrLf
jScript = jScript & " let rate = document.getElementById('currencyRate');" & vbCrLf
jScript = jScript & " if (rate !== undefined) {" & vbCrLf
jScript = jScript & " let data = rate.innerText;" & vbCrLf
jScript = jScript & " return data;" & vbCrLf
jScript = jScript & " } else {" & vbCrLf
jScript = jScript & " return '-2';" & vbCrLf ' error "-2" == currency rate not found
jScript = jScript & " }" & vbCrLf
jScript = jScript & "}" & vbCrLf
Document.BrowserDispatch EdgeWebBrowser.IDispatchPointer
Document.RunAnonymousFunction 3, varParams, jScript
Debug.Print "Tick"
End Sub
As you can see the timer checks the html element and returns either the value in the html or an error (-2). This function is identified as function "3". We don't pass any parameters to the anonymous function.
The OnRunAnonymousFunction is where we handle the event.
We had 2 different javascript functions, identified by "2" and "3".
Private Sub Document_OnRunAnonymousFunction(ByVal Id As Long, ByVal Params As Variant, ByVal Data As String, ByVal Error As Long)
Dim nRate As Variant
Dim nAmount As Variant
Dim nResult As Variant
Dim sCurTo As String
Dim sDecSep As String
If Id = 2 Then
' do nothing
ElseIf Id = 3 Then
' What's the current symbol for the decimal separator according to windows?
sDecSep = LoadLocaleValue(LOCALE_SDECIMAL)
If sDecSep = "," Then
Data = Replace(Data, ".", ",")
End If
nRate = CDec(Data)
If nRate <> -1 Then
sCurTo = ToCombo.Text
nAmount = CDec(AmountTextBox.Text)
nResult = nAmount * nRate
RateTextBox.Text = Data
ResultTextBox.Text = CStr(nResult) & " " & sCurTo
Timer.Enabled = False
Debug.Print "Tock"
End If
End If
End Sub
If you're not in the US then your decimal separate might be a comma (",") instead of a dot ("."), so we need to take that into account.
The timer keeps on running until it returns the correct data and the currencyRate html element no longer contains -1.
The moment it no longer contains -1, it has the actual currency Rate, which is returned in the -pass by reference- variable data.
We can now show the result in the resultTextBox.
Once the result is no longer -1, we stop the timer.
AntView - The MS Edge WebView2 ActiveX control Date last changed: 09/30/2024