ASP.NET MVC 应用实战 - I (DailyJournal)






4.84/5 (15投票s)
使用 ASP.NET MVC 和 jQuery 构建的一个简单的可视化任务管理器应用程序。
目录
引言
我计划写一篇关于创建有用 ASP.NET MVC 应用程序的文章已经很久了。我给它起了代号“DailyJournal”。它是一个简单的应用程序,可以创建多个活动并将任务分配给这些活动。可以看作是“又一个任务/待办事项应用程序”。
该应用程序使用了 jQuery,因此需要您对 jQuery 有一定的了解。该应用程序还使用了 jQuery UI 插件。
关于 jQuery 的基础知识,您可以参考众多资源中的任何一个,或者我在这里 CodeProject 上的文章:与 jQuery 建立友好关系。
您可以用于附加演示应用程序的凭据如下所示
User Name : admin
Password : admin123
使用的框架/库
- ASP.NET MVC
- jQuery + jQuery UI (用于 AJAX 和 UI)
- ELMAH 用于错误日志记录
警告提示
这只是一个粗略的草稿,所以我在此列出一些已知限制。
在继续深入研究此应用程序之前,请注意一些警告。这只是一个早期原型。因此,许多设计原则都被忽略了。但是,一旦我弄清楚了,我将在下一个更新中努力弥补。
该应用程序在其当前状态下支持以下功能
- 创建用户
- 为用户分配活动
- 为活动分配任务
- 为任务分配状态
用户创建/身份验证由默认的 Membership Provider 完成。大多数活动都具有高度的可视性,即您可以将任务拖放到不同的区域,原地编辑任务详细信息等等。
以下是当前设计中的问题,我保证在第二版中进行重构
- 无验证。
- 控制器臃肿。
- XSS/CSS 漏洞。
- 尚未实现服务模型/抽象。此演示使用了 LINQ to SQL。
- 无层分离。
- UI 设计。
- 等等...
注意:第二个版本将涵盖以下几点
- 主要重构
- 以上所有问题
- 单元测试
- 添加可扩展的验证
- IOC (控制反转)
- 主题支持
实际操作截图
登录系统后,您将看到以下屏幕,其中列出了当前活动
您可以通过单击“新活动”按钮来添加新活动。它会弹出一个对话框。
jQuery UI 对话框
对话框是使用“jQuery UI Dialog”插件创建的。要使用 jQuery UI 创建对话框,请执行以下两个步骤
- 定义一个应显示为对话框的
div
并为其指定一个 ID(您也可以为其指定一个 CSS 类)。 - 要调用对话框,请在 jQuery ready 函数中添加以下代码
- 将“添加新活动”按钮的点击事件处理程序挂钩。
<div id="dlg-work-area"> <% Html.RenderAction("AddWorkArea", "Work"); %> </div>
$(function() {
$("#dlg-work-area").dialog( {
autoOpen: false,
title: "Add Activity",
height: 250,
width: 350,
modal:true
});
}
$("#btnNewActivity").click(function()
{
$("#dlg-work-area").dialog('open');
});
注意:我们将 autoOpen
设置为 false
,因为我们希望在单击“添加新活动”按钮时打开对话框。
选择一个活动后,您将进入任务屏幕。在这里,您可以创建新任务,并通过在可用列之间拖放来更改任务状态。此版本在拖放方面存在一个小问题,因为列标题也支持拖放。这将被修复。
原地编辑
您可以内联编辑任务。双击任务说明即可完成。它会弹出以下屏幕。您可以保存或取消更改。
原地编辑是通过下面概述的代码片段实现的。请参阅 /scripts/widget 文件夹下的 widget.js 文件。
在 jQuery 的 onready
函数中设置事件和原地编辑
$(function() { setEditable(); });
setEditable()
函数定义如下
function setEditable() { $('.edit').click(function() { var textarea = '<div><textarea>'+$(this).html()+'</textarea>'; var button = '<div><input type="button" value="SAVE" ' + 'class="smallButton saveButton" /> <input ' + 'type="button" value="CANCEL" ' + 'class="smallButton cancelButton" /></div></div>'; var revert = $(this).html(); $(this).after(textarea+button).remove(); $('.saveButton').click(function(){saveChanges(this, false);}); $('.cancelButton').click(function(){saveChanges(this, revert);}); }) .mouseover(function() { $(this).addClass("editable"); }) .mouseout(function() { $(this).removeClass("editable"); }); };
上述代码执行以下操作
- 查找所有类为“
edit
”的元素。 - 构建文本区域和按钮。
- 为“保存”和“取消”按钮挂钩事件。
有关详细信息,请参阅 widget.js 文件。
基本模型图
下面是简化的模型图
路由
此原型中的应用程序使用 global.asax.cs 中定义的路由,格式如下
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Dashboard", action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
默认“Controller”是“Dashboard”,默认的 action 是“Index”。路由遵循控制器/action/{可选 ID} 的基本约定模式。
有趣的代码片段
仪表板的拖放功能使用了 jQuery UI Sortable 插件。这可以在 widget.js 文件中找到。
function reBind(element) {
$(element).sortable({
connectWith: '.column',
receive: function(event, ui)
{
var target = event.target.id;
if (null != target)
target = target.substring(target.lastIndexOf("_")+1);
var taskId = ui.item.attr('id')
if (null != taskId)
taskId = taskId.substring(taskId.lastIndexOf("_")+1);
var postdata = "taskStatusId=" + target + "&taskId="+taskId ;
$.post("/work/savewidget?" + postdata, function(data){
});
}
});
}
它的作用是查找所有类为“column”的元素并对其应用可排序行为。保存小部件位置的代码位于“receive”事件处理程序中。数据被收集并发送到“Work”Controller 的“SaveWidget
”action。
用于渲染任务小部件的控制器代码如下所示。如果没有任何任务状态(因为任务是按状态分组的),用户将被带到“AddTaskStatus”屏幕。
[HttpGet]
public ActionResult Board(Guid workAreaId)
{
var allStatus = db.TaskStatus.Where(ts => ts.WorkAreaId == workAreaId).ToList();
if (allStatus.Count == 0)
{
return RedirectToAction("AddTaskStatus", new { workAreaId = workAreaId });
}
ViewData["AREA_ID"] = workAreaId.ToString();
ViewData["AllStatus"] = allStatus;
return View(allStatus);
}
内联任务编辑被传递到以下控制器操作
[HttpPost]
public void SaveTaskDesc(Guid TaskId, string desc)
{
var task = db.Tasks.Where(t => t.Id == TaskId).SingleOrDefault();
task.Description = desc;
db.SubmitChanges();
}
ELMAH 配置
<sectionGroup name="elmah">
<section name="errorLog" requirePermission="false"
type="Elmah.ErrorLogSectionHandler, Elmah" />
</sectionGroup>
<elmah>
<errorLog type="Elmah.XmlFileErrorLog, Elmah"
logPath="~/App_Data" />
</elmah>
<httpHandlers>
<add verb="POST,GET,HEAD" path="elmah.axd"
type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
首先,我们定义一个“elmah”部分。然后我们将日志设置为 XML 日志文件。然后最重要的事情是设置 HTTPHandler。通过设置 ELMAH,错误日志记录功能就得到了处理。访问错误的 URL 是:http://serverurl/elmah.axd。
我知道有很多限制,但我希望通过进一步的迭代,它会越来越好。
本文的第二版几乎准备就绪。我正在进行一些重构和清理,并加入一些最终的设计思路。
参考文献
历史
- 2010 年 6 月 8 日 - 更新和更正。
- 2010 年 5 月 20 日 - 早期原型。