使用 KnockoutJS 进行用户凭证入门






4.64/5 (7投票s)
使用 KnockoutJS 批量上传新用户凭证到 Web 应用程序的酷炫方法。
引言
我最近在阅读一篇关于为企业增值的文章,其中一个关键点是自动化。概念很简单,如果你发现自己一遍又一遍地做某件事,就找到一种自动化它的方法,这样你就可以节省持续的时间,从而为企业增值。将一批新用户引入系统是扼杀 DevOps 时间的事情之一,所以我决定尝试 KnockoutJS,看看它是否能提供帮助。事实证明是可以的!代码以 C# MVC 项目的形式呈现和下载,但可以轻松剥离并独立使用。网上有足够多的教程可以解释如何使用 Knockout - 去谷歌搜索细节。本文重点介绍了我如何使用 Knockout 提供一个简单的入门验证机制,希望它能帮助到处于类似情况的人。
下面的截图显示了完成的项目,红色线条表示需要修复的主要问题。我还包含了一些正则表达式代码来对电子邮件地址进行基本验证。
这是示例 CSV 文件,请注意,我们所需的所有数据都不在那里,这就是问题所在!
背景
项目的目标是允许用户上传 CSV 文件,在客户端(在发送到服务器之前)解析它,并向用户显示在上传文件进行正确集成之前需要更正的数据。
工作流程如下
- 用户选择一个 CSV 文件(提供样本)
- 用户点击一个按钮(哇哦……)
- 客户端代码获取 CSV,解析它,并将其加载到 KnockoutJS 数组中
- KnockoutJS 可观察对象发挥其魔力,向用户显示绿色/前进、红色/停止
使用代码
为了保持整洁,我将 JavaScript 和 CSS 保留在各自独立的文件中。我们将处理的三个主要文件是 HTML (index.cshtml)、JavaScript (ViewScript.js) 和 CSS (KnockoutStyles.css)。
在 KO 中要设置的第一件事是模型。在这种情况下,我正在捕获基本用户信息。您会注意到我有一个密码字段 - 在生产版本中,我没有导入明文密码,而是使用了哈希(样本数据有所不同,但用于说明目的)。
function userModel() {
this.UserName = ko.observable();
this.Password = ko.observable();
this.FirstName = ko.observable();
this.LastName = ko.observable();
this.Email = ko.observable();
下一步是定义 ViewModel 数组,并链接绑定
// Define ViewModel which is an array of user details model
var userVM = {
userModel: ko.observableArray([])
}
};
// Bind the VM to the UI
ko.applyBindings(userVM);
然后将 KO data-bind 标记添加到 HTML
<table border="1">
<thead>
<tr>
<th class="Pad">Username</th>
<th class="Pad">Password</th>
<th class="Pad">First name</th>
<th class="Pad">Last name</th>
<th class="Pad">Email address</th>
<th class="Pad"> </th>
</tr>
</thead>
<tbody data-bind="foreach: userModel">
<tr data-bind="css: ValidCredentials">
<td class="Pad"><input data-bind="value: UserName" class="Standard"/></td>
<td class="Pad"><input data-bind="value: Password" class="Standard" /></td>
<td class="Pad"><input data-bind="value: FirstName" class="Standard"/></td>
<td class="Pad"><input data-bind="value: LastName" class="Standard"/></td>
<td class="Pad"><input data-bind="value: Email" class="Wide"/> <br /></td>
<td class="Pad"></td>
</tr>
</tbody>
</table>
在文件顶部,我添加了一个 FILE 输入控件和一个锚链接。
<form>
<input type="file" id="UserFile" />
<a href="#" id="lnkUpload">Upload CSV</a>
</form>
下一步是在锚的 `Click` 事件中放置一些代码,以(在客户端)获取用户选择的 CSV 文件,并将其解析到 KO 数组中。
$('#lnkUpload').click(function () {
var FileToRead = document.getElementById('UserFile');
if (FileToRead.files.length > 0) {
var reader = new FileReader();
// assign function to the OnLoad event of the FileReader
// non synchronous event, therefore assign to other method
reader.onload = Load_CSVData;
// call the reader to capture the file
reader.readAsText(FileToRead.files.item(0));
}
});
有一个让我抓耳挠腮的陷阱,我曾尝试使用 jQuery 选择器来获取 `FileToRead` 变量 - 但这不起作用,所以我回到了纯 JavaScript,一切又顺利进行。
读取文件内容的 `FileReader` 的 "`OnLoad`" 事件不是同步的,所以我将其分配给另一个函数(`Load_CSVData`),然后调用 `readAsText` 事件,该事件会反馈回该函数。
现在,这里有一点需要注意,`FileReader` **只**在您从 Web 服务器运行(或在 IDE 中使用集成 Web 服务器等)时才有效;如果您只是从桌面上运行 txt 文件,它则**无效**。
`Load_CSVData` 方法的逻辑如下
- 清除 KO 可观察数组中现有的任何项
- 使用从 `FileReader` 传入的文本数据加载本地数组(`CSVLines`),并使用 "`Split`" 函数(分隔符 = 新行标记)将每一行分开
- 对于正在加载的每一行,进一步分割(分隔符 = 逗号)
- 对于 CSV 行中的每一项,将值推送到 VM 数组中的一个新模型
我稍微分解了下面的代码,使其更易于阅读。请注意,如果 CSV 行中没有值,我添加了一个空字符串以避免以后出现问题。
function Load_CSVData(e) {
userVM.userModel.removeAll();
CSVLines = e.target.result.split(/\r\n|\n/);
$.each(CSVLines, function (i, item) {
var element = item.split(","); // builds an array from comma delimited items
var LUserName = (element[0] == undefined) ? "" : element[0].trim();
var LPassword = (element[1] == undefined) ? "" : element[1].trim();
var LFirstName = (element[2] == undefined) ? "" : element[2].trim();
var LLastName = (element[3] == undefined) ? "" : element[3].trim();
var LEmailAddress = (element[4] == undefined) ? "" : element[4].trim();
userVM.userModel.push(new userModel()
.UserName(LUserName)
.Password(LPassword)
.FirstName(LFirstName)
.LastName(LLastName)
.Email(LEmailAddress)
)
});
}
这一切都很好,数据完美显示……
下一步是利用可观察模式的力量,根据接收和更改的数据调整 UI……
有许多改进之处
- 用红色突出显示有问题的行
- 明确数据何时是必需的(我们将使输入框呈黄色)
- 用绿色显示已完成或接近完成的行
- 如果提供的电子邮件地址无效,则显示一条消息
在每个表行上,我添加了一个 `data-bind` 的 CSS,它调用了一个计算函数。该函数检查所需字段*(用户名、密码、电子邮件)*是否有值,如果没有,则将行的 CSS 背景颜色设置为红色。
.Red {
border: thin dotted #FF6600;
background-color: red;
}
<tr data-bind="css: ValidCredentials">
<td class="Pad"><input...
`ValidCredentials` 是一个计算函数
this.ValidCredentials = ko.computed(function () {
var ValidCreds = (this.UserName() != "" &&
this.Password() != "" && this.Email() != "");
return !ValidCreds ? "Red" : "Green";
}, this);
这工作正常,所以我进一步扩展了逻辑,不仅问题行会显示为红色,而且需要额外数据的输入字段也会显示为黄色。有三个字段,所以我为每个字段创建了单独的定义(CSS:`USR_Required`、`PWD_Required`、`EML_Required`)。
<td class="Pad"><input data-bind="value: UserName, css: USR_Required" class="Standard"/></td>
<td class="Pad"><input data-bind="value: Password, css: PWD_Required" class="Standard" /></td>
<td class="Pad"><input data-bind="value: FirstName" class="Standard"/></td>
<td class="Pad"><input data-bind="value: LastName" class="Standard"/></td>
<td class="Pad"><input data-bind="value: Email, css: EML_Required" class="Wide"/>
我创建了三个几乎相同的方法来计算(值得重新审视以进行重构!)
this.USR_Required = ko.computed(function () {
var rslt = (this.UserName() != "")
return rslt == true ? "NR" : "Required";
}, this);
this.PWD_Required = ko.computed(function () {
var rslt = (this.Password() != "")
return rslt == true ? "NR" : "Required";
}, this);
this.EML_Required = ko.computed(function () {
var rslt = (this.Email() != "")
return rslt == true ? "NR" : "Required";
}, this);
并在我的 *KnockoutStyles.css* 文件中添加了相应的 CSS
.Required {
background-color: #FFFF00;
}
.NR {
background-color: #FFFFFF;
}
到目前为止一切顺利,下一步是添加代码来对电子邮件地址进行简单检查。我为此借用了一些来自 stack 的代码
this.InValidEmail = ko.computed(function () {
if (this.Email() == "") // dont test if no value present
{
return false;
}
var rslt = !validateEmail(this.Email());
return rslt;
}, this);
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)
*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.
[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
快完成了,最后一步是让用户能够删除整行数据,如果它不相关(想想多年来离开组织的 Active Directory 中的所有账户……)
为此,我在每行的末尾添加了一个 `Click` `data-bind`,它调用了一个 `removeUser` 方法,并在 ViewModel 中添加了代码,将该项从 KO 数组中删除。
<a href="#" data-bind="click: $parent.removeUser"><span class="White">Remove user</span></a>
function RemoveUserFromList(data) {
userVM.userModel.remove(data);
}
var userVM = {
userModel: ko.observableArray([]),
removeUser: function (data) {
RemoveUserFromList(data)
}
};
就是这样……
这里有足够的内容为正在寻找类似解决方案的人提供一个好的开端。可能的改进包括:一个仅在数据更正后启用的按钮,另一个将数据转换为 JSON 并发送到服务器的按钮等。
(附注:如果您喜欢阅读这篇文章,请通过给页面顶部的文章评分来告诉我!)