如何在.net MVC中使用jQuery数据表和控制器内的HTML渲染实现服务器端处理

本文关键字:HTML 控制器 处理 服务器端 实现 数据表 net MVC jQuery | 更新日期: 2023-09-27 18:08:53

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

问题从呈现html开始。例如,我需要在"people table"中每个人的名字旁边渲染一张图片。然而,当我在搜索框中输入时,它仍然应该正确地按这些人的名字搜索。

现在,有了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 datatable插件显示一个实体表。我想在我的视图中编写我的显示逻辑,使用Razor视图引擎
  2. 我想处理 jQuery数据表AJAX 请求在我的控制器
  3. 我想以某种方式**访问显示逻辑每列**写在我的视图从我的控制器构造JSON响应。这是因为一些列可以自定义HTML、加载部分等。

我真的很困在这个问题上,如果在Stackoverflow上有类似的问题,请告诉我,因为我还没有找到它们。

提前感谢您的帮助

如何在.net MVC中使用jQuery数据表和控制器内的HTML渲染实现服务器端处理

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

我认为你被第2条卡住了:

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

这在datatable .net中很难做到,因为显示逻辑是用javascript编写的。你可以使用JS模板插件(比如mustache)并在视图中编写。然后在datatable .net js中,你可以将JSON插入到模板中。

这个问题很老了,但它可能会帮助那些也在与这个问题作斗争的人。我有一个很好的解决方案。

我有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;
    }

AjaxTableDisposable:

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();
}

UnifyParametersVisitor:

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",例如:

    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;
    }