65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (15投票s)

2010年5月20日

CPOL

5分钟阅读

viewsIcon

67721

downloadIcon

1290

使用 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 创建对话框,请执行以下两个步骤

  1. 定义一个应显示为对话框的 div 并为其指定一个 ID(您也可以为其指定一个 CSS 类)。
  2. <div id="dlg-work-area">
      <% Html.RenderAction("AddWorkArea", "Work"); %>
    </div>
  3. 要调用对话框,请在 jQuery ready 函数中添加以下代码
  4. $(function()  {
        $("#dlg-work-area").dialog( {
            autoOpen: false,  
            title: "Add Activity",
            height: 250,
            width: 350,
            modal:true
        });
    }
  5. 将“添加新活动”按钮的点击事件处理程序挂钩。
  6. $("#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");
    });
};

上述代码执行以下操作

  1. 查找所有类为“edit”的元素。
  2. 构建文本区域和按钮。
  3. 为“保存”和“取消”按钮挂钩事件。

有关详细信息,请参阅 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 日 - 早期原型。
© . All rights reserved.