目录
在上一篇文章的结尾,我说过我们将开始研究如何在网页上使用TypeScript。在这篇文章中,我们将创建一个简单的基于Web的计算器,展示如何让TypeScript与网页的内容进行交互。在开发代码时,我想展示我们如何在TypeScript中使用“老式”JavaScript风格的函数。
与往常一样,本文的代码可在GitHub上找到。
设计
我想限制计算器的输入,以便按钮按下仅限于一组众所周知的输入。基本上,我希望每个按钮触发一个数字或某种描述的运算符。为了选择我们要使用的运算符,我决定创建一个名为calculator.ts的TypeScript文件并向其中添加一个Operator枚举。我已将此文件添加到我的解决方案的根目录中。
enum Operator {
add = '+',
subtract = '-',
multiply = '*',
divide = '/',
period = '.'
}
在为我们的计算器展示TypeScript之前,让我们设置HTML页面。
<!DOCTYPE html>
<head>
<title>100 Days of TypeScript - Calculator</title>
</head>
<body>
<table border="1">
<th colspan="4"><input id="display"></input></th>
</table>
<script src="scripts/calculator.js"></script>
</body>
我们这里有一个HTML页面,它显示了一个带有标题行的表格,4列宽,将显示任何计算的结果。我还添加了一个script标签来加载计算器脚本。您会注意到,尽管计算器TypeScript文件的源位于根文件夹中,但src指向的是脚本文件夹。为了让TypeScript写入这个文件夹,我们需要调整tsconfig.json文件。我已将我的tsconfig文件精简到此。
{
"compilerOptions": {
"lib": [
"DOM", "ES2015"
],
"outDir": "./scripts",
"strict": true,
"noUnusedLocals": true,
}
}
这些条目中有两件事真正令人感兴趣。outDir关键是我们设置输出目录的位置;这就是我将calculator.js文件写入脚本目录的方式。该lib条目也很有趣,因为我在那里添加了一个DOM条目。当我们在lib中指定条目时,我们是在告诉TypeScript它需要引入哪些库,因此ES2015等条目会引入ECMA功能。我们希望我们的计算器做的一件事是将输入复制到剪贴板,这是对名为window.navigator的东西的JavaScript 操作。为了访问导航器,我们必须引入允许我们与浏览器文档对象模型进行交互的DOM库。可能有点令人困惑的是,您不需要DOM库来与其他标准功能(例如窗口文档)进行交互。
在我将计算器按钮添加到我的表之前,我还可以进行一项设置。我知道我的表有一个名为display输入字段。稍后我想与它进行交互,因此我将编写一个小辅助函数,以使我在使用它时更轻松。我要做的是创建一个值来存储对输入的引用。因为我要绑定到这个值,所以我要添加一个辅助方法,它会在第一次访问时懒惰地填充这个引用,并在后续调用中返回存储的版本。
let displayElement: HTMLInputElement | null = null;
function getDisplay(): HTMLInputElement {
if (!displayElement) {
displayElement = <HTMLInputElement>document.getElementById('display');
}
return displayElement;
}
这段代码的意思是它displayElement可以是类型HTMLInputElement,也可以是null表示我们还没有将它连接到显示元素。TypeScript非常有用,因为我们可以对网页输入进行强类型化,因此JavaScript中的object内容可以限制为实际类型。这很有用,因为它告诉我们可以使用哪些属性和操作。为了填充displayElement,我们使用document.getElementById。这是一种标准的浏览器方法,它允许我们根据元素的id选择元素(这是在网页的实际标签中设置的)。现在,返回document.getElementById类型是object,所以我们需要使用一种称为类型转换的技术将其设置为适当的类型。TypeScript提供了几种不同的方法来转换对象,但在我们的例子中,我们使用<>来指定适当的类型。
让我们添加表格的其余部分。
<tr>
<td colspan="2"><input type="button" onclick="clearAll()" value="Clear" /></td>
<td colspan="2"><input type="button" onclick="copyToClipboard()" value="Mem" /></td>
</tr>
<tr>
<td><input type="button" value="1" onclick="display(1)"/> </td>
<td><input type="button" value="2" onclick="display(2)"/> </td>
<td><input type="button" value="3" onclick="display(3)"/> </td>
<td><input type="button" value="/" onclick="display(Operator.divide)"/> </td>
</tr>
<tr>
<td><input type="button" value="4" onclick="display(4)"/> </td>
<td><input type="button" value="5" onclick="display(5)"/> </td>
<td><input type="button" value="6" onclick="display(6)"/> </td>
<td><input type="button" value="-" onclick="display(Operator.subtract)"/> </td>
</tr>
<tr>
<td><input type="button" value="7" onclick="display(7)"/> </td>
<td><input type="button" value="8" onclick="display(8)"/> </td>
<td><input type="button" value="9" onclick="display(9)"/> </td>
<td><input type="button" value="+" onclick="display(Operator.add)"/> </td>
</tr>
<tr>
<td><input type="button" value="." onclick="display(Operator.period)"/> </td>
<td><input type="button" value="0" onclick="display(0)"/> </td>
<td><input type="button" value="=" onclick="solve()"/> </td>
<td><input type="button" value="*" onclick="display(Operator.multiply)"/> </td>
</tr>
每个按钮都连接到四个功能之一,具体取决于我们要做什么。让我们从与我们之前添加到枚举中的数字和运算符相关联的显示函数开始。
function display(value: number | Operator): void {
const htmlElement = getDisplay();
htmlElement.value = htmlElement.value.trim() + value;
}
此函数接受一个数字或一个运算符。我喜欢TypeScript为我们提供联合运算符来表示值可以是一种或另一种类型的事实,使用|。
在函数内部,我们使用上面编写的函数获取对输入元素的引用。一旦我们有了这个元素,我们就可以从中获取值并将数字或运算符添加到它上面。我在htmlElement.value上调用trim()操作,以防用户在输入的末尾放了一个空格。
如果我想清除输入,我可以使用以下方法:
function clearAll(): void {
const htmlElement = getDisplay();
htmlElement.value = '';
}
这与我们的display函数非常相似,因为它获取 html 输入元素(事实上,我们将看到我们所有的函数都会这样做)。一旦它有了引用,它就直接与value交互并将其设置为一个空字符串。
您可能认为该solve函数会很复杂,解析我们的输入并对其执行计算。现实情况是,借助一个名为eval的标准JavaScript函数,它评估输入的结果,这个函数是微不足道的。
function solve(): void {
const htmlElement = getDisplay();
const output = eval(htmlElement.value);
htmlElement.value = output;
}
最后,我们继续我们的代码,将输入复制到剪贴板上。正如我之前提到的,我们将使用navigator对象,这需要我们导入DOM库。
function copyToClipboard(): void {
const htmlElement = getDisplay();
navigator.clipboard.writeText(htmlElement.value);
}
你可能还记得我说过在window.navigator中的navigator对象。 为方便起见,我们的代码通常不需要指定窗口部分,因此我们可以直接进入navigator。
最后说明
你可能想知道为什么我选择使用input type="button"而不是仅仅使用一个button元素。我选择输入路由是因为人们会熟悉它,而且我不需要在按钮内设置其他内容(例如显示图像)的能力。
结论
仅此而已。这是我们的第一个网页,由TypeScript提供支持。我希望您对将TypeScript连接到HTML的简单程度印象深刻。在第9天,我们将继续我们的Web开发世界之旅,着眼于创建一个简单的共享报告应用程序。
本文最初发布于100 Days of TypeScript (Day 8) – Confessions of a coder
https://www.codeproject.com/Articles/5330658/100-Days-of-TypeScript-Day-8