2

我想创建一种可重用的方式,将出色的jQuery数据表插件与 .Net MVC 一起用于我所有包含表的视图。现在,我已经弄清楚了这个插件的服务器端处理是如何工作的,以及我需要如何编写我的控制器逻辑来提供适当的响应。

问题从渲染 html 开始。例如,我需要在“人员表”中每个人的名字旁边渲染一张图片。但是,当我在搜索框中输入内容时,它仍应按这些人的姓名正确搜索。

现在,使用 Razor 视图引擎,这很容易,您可以编写如下内容:

@foreach (var person in people)
{
    <tr>
        <td>
            <span>@person.Name</span>
            <img src="@person.ImgUrl" title="@person.Name"/>
        </td>
    </tr>
}

问题是这需要在服务器端呈现为 JSON。您将有一个控制器来处理 jQuery 数据表 AJAX 请求并返回包含数据表所需的所有内容的有效 JSON 对象。但是,您无法从控制器中的视图访问片段。我在这里看到的唯一选择是为需要呈现某些特定 HTML 的每一列创建部分视图。(对于常见的用例,比如操作按钮,我可以编写 HTML 助手)

这就是问题所在。我很犹豫是否为每个 HTML 列的每个表创建部分视图。

这里的问题是控制器需要以某种方式访问​​显示逻辑来创建 JSON,而显示逻辑通常写在视图中。我的选择仅限于使用部分视图并将其呈现为控制器中的字符串,或者将我的显示逻辑(它们是 lambda 表达式)存储在某种服务器会话或缓存中并从控制器中获取它。最后一种方法的最大缺点是会话或缓存似乎太不稳定而无法依赖。

所以,重申一下:

  1. 我想使用带有 AJAX服务器端处理的 jQuery 数据表插件来显示一个实体表。
  2. 我想使用 Razor 视图引擎在我的视图中编写我的显示逻辑
  3. 我想在我的控制器中处理jQuery 数据表 AJAX请求
  4. 我想以某种方式**访问在我的控制器中写入视图中的每列显示逻辑**,以构建 JSON 响应。这是因为某些列可以具有自定义 HTML、加载部分内容等。

我真的很坚持这一点,如果在 Stackoverflow 上有类似的问题,请指出我,因为我还没有找到它们。

预先感谢您的帮助。

4

2 回答 2

3

我不确定我是否完全遵循这个问题,但我认为你可以很容易地解决这个问题。与其尝试让控制器引用不起作用的视图,不如让 jQuery 在 Ajax 请求中将控制器所需的参数传递回。然后控制器可以使用这些参数来发送正确的 JSON 响应。

我认为你被困在第 2 点:

我想使用 Razor 视图引擎在我的视图中编写我的显示逻辑

这对于 datatables.net 来说很难做到,因为显示逻辑是在 javascript 中的。您可以使用 JS 模板插件(如mustache)并将其写入您的视图中。然后在您的 datatables.net js 中,您可以将 JSON 插入模板。

于 2013-04-12T12:22:07.787 回答
0

这个问题很老,但它可能会帮助其他也会解决这个问题的人。我有很好的解决方案。

我有 html 助手(AjaxTable,Column),它在页面加载时创建一个描述表(AjaxTable)的对象。该对象存储在会话中。所有 ajax 请求都在通用控制器中处理,该控制器使用存储的对象来创建响应。它支持分页、排序和过滤。

示例用法:

在控制器中:

public ActionResult Odeslane()
{
    return View(db.Dotaz.Where( ... /* select all data which should be displayed in the table */ ));
}

在视图中:

@model IQueryable<Dotaz>

@using (Html.AjaxTable(Model, new { id = "dotazTable" }))
{
    Html.Column(Model, @<th>@Html.DisplayNameFor(model => item.Autor.JmenoPrijmeniAUzivatelskeJmeno)</th>, 
    @<td>
        <input type="hidden" name="id" value="@item.DotazId" />
        @if (item.AutorId != null) { 
            @Html.DisplayFor(model => item.Autor.JmenoPrijmeniAUzivatelskeJmeno)
        }
        else
        {
            @Html.DisplayFor(model => item.AnonymMail)
        }
    </td>, 
    d => d.AutorId != null ? d.Autor.Jmeno + " " + d.Autor.Prijmeni + " (" + d.Autor.UserName + ")" : d.AnonymMail);

    Html.Column(Model, @<th>@Html.DisplayNameFor(model => item.Nazev)</th>, @<td>@Html.DisplayFor(model => item.Nazev)</td>, d => d.Nazev );

    Html.Column(Model, @<th>@Html.DisplayNameFor(model => item.DatumVzniku)</th>, @<td>@Html.DisplayFor(model => item.DatumVzniku)</td>, d => d.DatumVzniku );
}

执行:

帮手:

    public static IDisposable AjaxTable<T>(this HtmlHelper htmlHelper, IQueryable<T> items, object htmlAttributes) where T : new()
    {
        var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
        // TODO: replace CommonAjaxActionUrl with url of the common ajax action
        return new AjaxTableDisposable<T>(htmlHelper, items, CommonAjaxActionUrl, attrs);
    }

    public static MvcHtmlString Column<T, TKey>(this HtmlHelper htmlHelper, IEnumerable<T> items, Func<T, HelperResult> th, Func<T, HelperResult> td, Expression<Func<T, TKey>> keySelector) where T : new()
    {
        var obj = htmlHelper.ViewContext.TempData["AjaxTable"];
        AjaxTableDisposable<T> table = obj as AjaxTableDisposable<T>;
        table.Column(th, td, keySelector);
        return MvcHtmlString.Empty;
    }

AjaxTable一次性:

public class AjaxTableDisposable<T> : IDisposable where T : new()
{
    private HtmlHelper htmlHelper;
    private ViewContext viewContext;
    private AjaxTable<T> table;

    public AjaxTableDisposable(HtmlHelper htmlHelper, IQueryable<T> items, string ajaxUrl, RouteValueDictionary attrs)
    {
        // akce na zacatku
        this.htmlHelper = htmlHelper;
        viewContext = htmlHelper.ViewContext;
        viewContext.TempData["AjaxTable"] = this;
        if (!attrs.ContainsKey("id")) attrs["id"] = "AjaxTable" + Guid.NewGuid().ToString("N");
        table = new AjaxTable<T>() { AjaxUrl = ajaxUrl, Attrs = attrs, Items = items };
    }

    public void Column<TKey>(Func<T, HelperResult> th, Func<T, HelperResult> td, Expression<Func<T, TKey>> keySelector)
    {
        AjaxTableColumn<T> col = new AjaxTableColumn<T>() { Th = th, Td = td, KeySelector = keySelector, KeyType = typeof(TKey) };
        col.OrderData = (IQueryable<T> data, bool asc) => asc ? data.OrderBy(keySelector) : data.OrderByDescending(keySelector);
        table.Columns.Add(col);
    }

    // When the object is disposed (end of using block), write "end" function
    public void Dispose()
    {
        // akce na konci
        viewContext.TempData.Remove("AjaxTable");
        viewContext.Writer.Write(htmlHelper.Partial("DisplayTemplates/AjaxTable", table));
        string tableId = table.Attrs["id"].ToString();
        StoreInSession(table);  // TODO: you have to implement the StoreInSession method
    }
}

AjaxTable 模板:

@model IAjaxTable
@{
    RouteValueDictionary attrs = Model.GetAttrs();
    string tableId = attrs["id"].ToString();
    string cls = attrs.ContainsKey("class") ? " " + attrs["class"] : "";
}

<table id="@tableId" class="@{ ViewContext.Writer.Write(cls); }"@{
    foreach (var attr in attrs)
    {
        if (attr.Key != "id" && attr.Key != "class")
        {
            ViewContext.Writer.Write(" "+attr.Key+"="+"\""+attr.Value+"\"");
        }
    }
}>
    <thead>
        <tr>
            @for (int i = 0; i < Model.GetColumnsCount(); i++ )
            {
                Model.GetTh(i).WriteTo(ViewContext.Writer);
            }
        </tr>
    </thead>
    <tbody></tbody>
</table>

<script type="text/javascript">
    $(document).ready(function () {

        var dt = $('#@tableId').DataTable({
        serverSide: true,
        ajax: function (data, callback, settings) {
                data["tableId"] = "@tableId";
                $.ajax({
                    dataType: 'json',
                    url: "@Model.GetAjaxUrl()",
                    type: "POST",
                    success: callback,
                    data: data
                });
        },
        ...
    });
});
</script>

AjaxTable 类:

public class AjaxTable<T> : IAjaxTable where T : new()
{
    public List<AjaxTableColumn<T>> Columns { get; set; }
    public T Row { get; set; }
    public String AjaxUrl { get; set; }
    public RouteValueDictionary Attrs { get; set; }
    public IQueryable<T> Items { get; set; }


    public AjaxTable()
    {
        Columns = new List<AjaxTableColumn<T>>();
        Row = new T();
    }

    public HelperResult GetTh(int column)
    {
        return Columns[column].Th.Invoke(Row);
    }

    public string GetAjaxUrl()
    {
        return AjaxUrl;
    }

    public RouteValueDictionary GetAttrs()
    {
        return Attrs;
    }


    public int GetColumnsCount()
    {
        return Columns.Count;
    }

    public object DefaultAjaxAction()
    {
        var total = Items.Count();
        IQueryable<T> data = Search(Items, ParamSearchValue, ParamSearchRegex);
        var filtered = data.Count();
        data = Columns[ParamOrderColumn].OrderData(data, ParamOrderDirAscending);
        data = data.Skip(ParamStart).Take(ParamLength);
        return CreateAjaxResponse(data, total, filtered);
    }

    public IQueryable<T> Search(IQueryable<T> data, string search, bool regex)
    {
        if (search == null || search == "") return data;
        Expression orExpression = null;
        IReadOnlyCollection<ParameterExpression> parameters = null;
        foreach (var col in Columns)
        {
            if (col.KeyType == typeof(string))
            {
                Expression<Func<T, string>> keySelector = (Expression<Func<T, string>>) col.KeySelector;
                Expression compare = Expression.Call(keySelector.Body, typeof(String).GetMethod("Contains"), Expression.Constant(search));
                if (orExpression == null)
                {
                    orExpression = compare;
                    parameters = keySelector.Parameters;
                }
                else
                {
                    orExpression = Expression.OrElse(compare, orExpression);
                }
            }
        }
        if (orExpression != null)
        {
            Expression<Func<T, bool>> whereExpr = Expression.Lambda<Func<T, bool>>(orExpression, parameters);
            UnifyParametersVisitor visitor = new UnifyParametersVisitor();
            whereExpr = visitor.UnifyParameters(whereExpr);
            data = data.Where(whereExpr);
        }
        return data;
    }

    public object CreateAjaxResponse(IQueryable<T> data, int recordsTotal, int recordsFiltered)
    {
        Dictionary<string,object> obj = new Dictionary<string,object>();
        obj.Add("draw", HttpContext.Current.Request.Params["draw"]);
        obj.Add("recordsTotal", recordsTotal);
        obj.Add("recordsFiltered", recordsFiltered);
        List<T> dataList = data.ToList();
        String[][] cell = new String[dataList.Count()][];
        int rowIndex = 0;
        foreach (T row in dataList)
        {
            cell[rowIndex] = new String[Columns.Count];
            int colIndex = 0;
            foreach (var column in Columns)
            {
                StringWriter sw = new StringWriter();
                column.Td.Invoke(row).WriteTo(sw);
                cell[rowIndex][colIndex++] = sw.ToString();
                sw.Dispose();
            }
            rowIndex++;
        }
        obj.Add("data", cell);
        return obj;
    }

    public int ParamStart { get { return Int32.Parse(HttpContext.Current.Request.Params["start"]); } }
    public int ParamLength { get { return Int32.Parse(HttpContext.Current.Request.Params["length"]); } }
    public int ParamOrderColumn { get { return Int32.Parse(HttpContext.Current.Request.Params["order[0][column]"]); } }
    public bool ParamOrderDirAscending { get { return HttpContext.Current.Request.Params["order[0][dir]"] == "asc"; } }
    public string ParamSearchValue { get { return HttpContext.Current.Request.Params["search[value]"]; } }
    public bool ParamSearchRegex { get { return HttpContext.Current.Request.Params["search[regex]"] == "true"; } }
}

AjaxTableColumn 类:

public class AjaxTableColumn<T>
{
    public Func<T, HelperResult> Th { get; set; }
    public Func<T, HelperResult> Td { get; set; }

    public Expression KeySelector { get; set; }     // typ: Expression<Func<T,?>>, typicky neco jako: (T t) => t.Key

    public Type KeyType { get; set; }
    public OrderDataDelegate OrderData { get; set; }
    public delegate IQueryable<T> OrderDataDelegate(IQueryable<T> data, bool asc);

}

IAjaxTable 接口:

public interface IAjaxTable
{
    HelperResult GetTh(int column);
    string GetAjaxUrl();
    RouteValueDictionary GetAttrs();
    int GetColumnsCount();
    object DefaultAjaxAction();
}

统一参数访问者:

public class UnifyParametersVisitor : ExpressionVisitor
{
    private IEnumerable<ParameterExpression> parameters;

    public Expression<TDel> UnifyParameters<TDel>(Expression<TDel> expression)
    {
        parameters = expression.Parameters;
        return (Expression<TDel>) Visit(expression);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        foreach (var param in parameters)
        {
            if (param.Name == node.Name)
            {
                return param;
            }
        }
        return base.VisitParameter(node);
    }
}

用于创建 ajax 响应的控制器 - 想法:您必须通过 tableId 在会话中找到 AjaxTable 实例,然后返回 Json(ajaxTable.DefaultAjaxAction())。确保在用户注销时清除会话。

更新:今天我遇到了我的方法的一个限制:你不能在表格单元格中使用一些 html 助手(使用模板,渲染部分视图......),但它可以解决!您必须为每个 http 请求创建新的 htmlHelper 实例并使用它而不是标准的“Html”,例如使用这个 html 助手:

    public static HtmlHelper<object> CurrentHtml(this HtmlHelper html)
    {
        var ch = (HtmlHelper<object>)HttpContext.Current.Items["currentHtml"];
        if (ch == null)
        {
            var context = new HttpContextWrapper(HttpContext.Current);
            var routeData = new RouteData();
            routeData.Values["controller"] = "Home";    // you can use any controller here
            var controllerContext = new ControllerContext(new RequestContext(context, routeData), new HomeController());
            var view = ViewEngines.Engines.FindView(controllerContext, "Index", null);
            ch = new HtmlHelper<object>(new ViewContext(controllerContext, view.View, new ViewDataDictionary(), new TempDataDictionary(), TextWriter.Null), new ViewPage());
            HttpContext.Current.Items["currentHtml"] = ch;
        }
        return ch;
    }
于 2014-12-08T14:32:45.833 回答