C# 如何避免在ASP.NET代码中编写凌乱的JavaScript?
我在问,在ASP.NET中使用Javascript的最佳实践是什么 我不知道这是否是最佳实践,但我在codebehind中添加了javascript客户端事件。它工作正常,但这是最佳实践吗 例如,我得到了一个单选按钮控件,并在页面_Init中添加了Javascript客户端事件。页面初始化可以多次调用,因此每次调用页面时都会呈现Javascript 此外,调试长Javascript字符串也很困难。怎样才能更干净。。。有办法吗 让我们看一个包含Javascript的变量示例:C# 如何避免在ASP.NET代码中编写凌乱的JavaScript?,c#,javascript,asp.net,webforms,code-behind,C#,Javascript,Asp.net,Webforms,Code Behind,我在问,在ASP.NET中使用Javascript的最佳实践是什么 我不知道这是否是最佳实践,但我在codebehind中添加了javascript客户端事件。它工作正常,但这是最佳实践吗 例如,我得到了一个单选按钮控件,并在页面_Init中添加了Javascript客户端事件。页面初始化可以多次调用,因此每次调用页面时都会呈现Javascript 此外,调试长Javascript字符串也很困难。怎样才能更干净。。。有办法吗 让我们看一个包含Javascript的变量示例: scripts.Te
scripts.Text += "<script type='text/javascript'>function ValidateDdl" + metachamp.ID +
"(sender, args) { if(" + txtReason.ClientID + ".GetText() != '' ||" +
dynamicControl.ClientID +
".style.display == 'none' || HiddenFieldSaveError.Contains('" + metachamp.ID +
"') ){" + dynamicControl.ClientID + ".className='';HiddenFieldError.Remove(" +
metachamp.ID + ");" + errorImage.ClientID +
".SetClientVisible(false);args.IsValid = true;}else{var comboVal = document.getElementById('" +
Image9.ClientID + "'.substring(0,'" + Image9.ClientID +
"'.length - 6) + 'ddl').value ;if (comboVal != '0' ) {args.IsValid = true;HiddenFieldError.Remove(" +
metachamp.ID + ");" + validImage.ClientID +
".SetClientVisible(false);HiddenField.Remove('Bypass-' + '" +
metachamp.ID.ToString() + "');HiddenFieldSaveError.Remove(" + metachamp.ID +
");" + dynamicControl.ClientID + ".className='';" + errorImage.ClientID +
".SetClientVisible(false);}";
scripts.Text+=“function ValidateDdl”+metachamp.ID+
“(发送方,args){if(“+txtReason.ClientID+”.GetText()!=”||”+
dynamicControl.ClientID+
.style.display==“无”| | HiddenFieldSaveError.Contains(“+metachamp.ID+
“){”+dynamicControl.ClientID+”.className='';HiddenFieldError.Remove(“+
metachamp.ID+”;“+errorImage.ClientID+
“.SetClientVisible(false);args.IsValid=true;}else{var comboVal=document.getElementById(””+
Image9.ClientID+“”。子字符串(0,“+Image9.ClientID+
“'.length-6)+'ddl').value;if(comboVal!=“0”){args.IsValid=true;HiddenFieldError.Remove(”+
metachamp.ID+”;“+validImage.ClientID+
.SetClientVisible(false);HiddenField.Remove('Bypass-'+')”+
metachamp.ID.ToString()+”);HiddenFieldSaveError.Remove(“+metachamp.ID+
”;“+dynamicControl.ClientID+”。类名=”;“+errorImage.ClientID+
“.SetClientVisible(false);}”;
第一步是将JavaScript从代码隐藏和值插值中分离出来。这种方法不是动态构建JavaScript,而是使用一个给定参数的JavaScript函数
在第一个阶段之后,我们最终得到了如下内容(请原谅部分翻译,它伤了我的头)。注意闭包生成器模式的使用;在实际代码中,我将进一步将其作为一个单独的模块
函数makeValidator(champId,opts){
返回函数(发送方,参数){
//现在是时候了,哈利。。
//
//在ASP.NET中使用$get(和$find),尤其是在
//处理ASP.NET AJAX集成以按ID查找控件。
//
//但是,代码使用的似乎是某些DevExpress
//控件,因此必须以不同的方式进行访问,主要是
//1.`window[clientId]`或
//2.`ASPxClientControl.GetControlCollection().GetByName(id)`
//这只是需要处理的令人讨厌的事情之一;我已经展示了它的用法
//它可能也需要应用于其他控件。
//
var reasonControl=window[opts.reasonId];//DX控件
var dynamicControl=$get(opts.dynamicControlId);//普通ASP.NET/DOM
var errorImage=window[opts.errorImageId];//DX控件
if(reasonControl.GetText()!=“dynamicControl.style.display=”无”){
dynamicControl.className='';
errorImage.SetClientVisible(false);
args.IsValid=true;
}
//等等。
}
}
应该清楚的是,JavaScript代码与任何字符串插值都是分开的。这是一个正常的函数,当使用某些参数(由API定义)调用时,具有特定的行为。虽然“加载/注入”此JavaScript有不同的方法(当UpdatePanel和嵌套/复杂层次结构发挥作用时,这一点很重要),让我们假设它当前位于页面标记中的
中
现在,让我们将验证器连接到控件-这完全是虚构的,但它显示了数据绑定的用法,并在后面的代码中实际创建了JavaScript“调用”,稍后我们将了解原因。(正确使用数据绑定实际上很重要,因为它会延迟调用CreateValidator函数,直到分配了控件的clientID。)
对于任何技术堆栈来说,这都是一个经典问题。要回答这个问题,我需要记住以下几点:
不要重复你自己(使用WebForms可能会更困难)
做一件事,把它做好
我发现客户端功能分为两类:
- 表单验证,通常是应该在后端代码中管理的业务规则的扩展
- 可用性增强,如下拉菜单,当焦点从文本字段移开时自动大写文本,等等
- 用户交互管理,这可能是由后端不容易完成的业务规则驱动的
(注意:下面的代码可能有一些bug,但它应该能告诉您主要的想法)
使用ASP.NET WebForms进行表单验证
这是给我带来最大痛苦的领域。我目前正在尝试使用WebForms,实际上进展很顺利。关于验证,我最好的建议是:不要使用
验证器!这就是人们抱怨WebForms是一个复制粘贴框架的原因。它不起作用我们必须这样做。在快速编写代码示例之前,也不要使用数据[Set | Table | Row]。您获得了所有数据,但没有任何行为。请使用类似ORM的实体框架或NHibernate,并让所有ASP页面处理实体类,因为这样您就可以使用FluentValidation:
应用程序代码/模型/实体/Post.cs
namespace Project.Models.Entities
{
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? ModifiedAt { get; set; }
}
}
using FluentValidation;
using Project.Models.Entities;
namespace Project.Models.Validators
{
public class PostValidator : AbstractValidator<Post>
{
public PostValidator()
{
RuleFor(p => p.Title)
.NotEmpty()
.Length(1, 200);
RuleFor(p => p.Body)
.NotEmpty();
}
}
}
namespace Project.UserControls
{
public class PostControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
PostValidator validator = new PostValidator();
Post entity = new Post()
{
// Map form fields to entity properties
Id = Convert.ToInt32(PostId.Value),
Title = PostTitle.Text.Trim(),
Body = PostBody.Text.Trim()
};
ValidationResult results = validator.Validate(entity);
if (results.IsValid)
{
// Save to the database and continue to the next page
}
else
{
BulletedList summary = (BulletedList)FindControl("ErrorSummary");
// Display errors to the user
foreach (var failure in results.Errors)
{
Label errorMessage = FindControl(failure.PropertyName + "Error") as Label;
if (errorMessage == null)
{
summary.Items.Add(new ListItem(failure.ErrorMessage));
}
else
{
errorMessage.Text = failure.ErrorMessage;
}
}
}
}
else
{
// Display form
}
}
...
}
}
function FooWidget(element) {
this.$element = $(element);
this.fillOptions = this.fillOptions.bind(this);
this.$element.on("click", "[data-action=fillOptions]", this.fillOptions);
}
FooWidget.prototype = {
constructor: FooWidget,
fillOptions: function(event) {
// make ajax request:
var select = this.$element.find("select:first")[0],
option = null;
option = document.createElement("option");
option.value = "...";
option.text = "...";
select.appendChild(option);
...
},
focus: function() {
this.$element.find(":input:first").focus();
}
};
应用程序代码/Models/validator/PostValidator.cs
namespace Project.Models.Entities
{
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? ModifiedAt { get; set; }
}
}
using FluentValidation;
using Project.Models.Entities;
namespace Project.Models.Validators
{
public class PostValidator : AbstractValidator<Post>
{
public PostValidator()
{
RuleFor(p => p.Title)
.NotEmpty()
.Length(1, 200);
RuleFor(p => p.Body)
.NotEmpty();
}
}
}
namespace Project.UserControls
{
public class PostControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
PostValidator validator = new PostValidator();
Post entity = new Post()
{
// Map form fields to entity properties
Id = Convert.ToInt32(PostId.Value),
Title = PostTitle.Text.Trim(),
Body = PostBody.Text.Trim()
};
ValidationResult results = validator.Validate(entity);
if (results.IsValid)
{
// Save to the database and continue to the next page
}
else
{
BulletedList summary = (BulletedList)FindControl("ErrorSummary");
// Display errors to the user
foreach (var failure in results.Errors)
{
Label errorMessage = FindControl(failure.PropertyName + "Error") as Label;
if (errorMessage == null)
{
summary.Items.Add(new ListItem(failure.ErrorMessage));
}
else
{
errorMessage.Text = failure.ErrorMessage;
}
}
}
}
else
{
// Display form
}
}
...
}
}
function FooWidget(element) {
this.$element = $(element);
this.fillOptions = this.fillOptions.bind(this);
this.$element.on("click", "[data-action=fillOptions]", this.fillOptions);
}
FooWidget.prototype = {
constructor: FooWidget,
fillOptions: function(event) {
// make ajax request:
var select = this.$element.find("select:first")[0],
option = null;
option = document.createElement("option");
option.value = "...";
option.text = "...";
select.appendChild(option);
...
},
focus: function() {
this.$element.find(":input:first").focus();
}
};
UserControls/PostControl.ascx
<asp:BulletedList ID="ErrorSummary" runat="server" CssClass="Error-Summary" />
<p>
<asp:Label ID="PostTitleLabel" AssociatedControlID="PostTitle" runat="server">* Title:</asp:Label>
<asp:TextBox ID="PostTitle" runat="server" />
<asp:Label ID="PostTitleError" runat="server" CssClass="Error" />
</p>
<p>
<asp:Label ID="PostBodyLabel" AssociatedControlID="PostBody" runat="server">* Body:</asp:Label>
<asp:TextBox ID="PostBody" runat="server" TextMode="MultiLine" />
<asp:Label ID="PostBodyError" runat="server" CssClass="Error" />
</p>
<asp:HiddenField ID="PostId" runat="server" />
<dx:ASPxRadioButtonList runat="server" ID="rblistComment">
<Items>
<dx:ListEditItem Text="Nouvelle information" Value="0" />
<dx:ListEditItem Text="Correction de valeurs" Value="1" />
<dx:ListEditItem Text="Autre" Value="2" />
</Items>
<ClientSideEvents SelectedIndexChanged="rblistComment_SelectIndexChanged" />
</dx:ASPxRadioButtonList>
复杂的多字段验证
任何需要来自多个字段的数据的验证都不应在客户端上处理。请在C#中执行此操作。尝试将其拼凑在一起
<dx:ASPxRadioButtonList runat="server" ID="rblistComment">
<Items>
<dx:ListEditItem Text="Nouvelle information" Value="0" />
<dx:ListEditItem Text="Correction de valeurs" Value="1" />
<dx:ListEditItem Text="Autre" Value="2" />
</Items>
<ClientSideEvents SelectedIndexChanged="rblistComment_SelectIndexChanged" />
</dx:ASPxRadioButtonList>
function rblistComment_SelectIndexChanged(s,e) {
var btnOk = eval($("[id$=btnOK]").attr("id"));
var txtCommentPopup = eval($("[id$=txtCommentPopup]").attr("id"));
btnOk.SetEnabled(s.GetValue() != null);
txtCommentPopup.SetVisible(s.GetValue() == '2');
const string csname = "ClientEvents";
const string csurl = "~/js/EtudeCliniqueScript/ClientEvents.js";
Type cstype = this.GetType();
ClientScriptManager cs = Page.ClientScript;
if (!cs.IsClientScriptIncludeRegistered(cstype, csname))
{
cs.RegisterClientScriptInclude(cstype, csname, ResolveClientUrl(csurl));
}