如何在.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表达式)存储在某种服务器会话或缓存中并从控制器中获取它。最后一种方法的最大缺点是会话或缓存似乎太不稳定,无法依赖。
所以,重申一下:
- 我想用AJAX 服务器端处理的jQuery datatable插件显示一个实体表。我想在我的视图中编写我的显示逻辑,使用Razor视图引擎
- 我想处理 jQuery数据表AJAX 请求在我的控制器
- 我想以某种方式**访问显示逻辑每列**写在我的视图从我的控制器构造JSON响应。这是因为一些列可以自定义HTML、加载部分等。
我真的很困在这个问题上,如果在Stackoverflow上有类似的问题,请告诉我,因为我还没有找到它们。
提前感谢您的帮助
我不确定我是否完全遵循这个问题,但我认为你可以很容易地解决这个问题。与其尝试让控制器引用不起作用的视图,不如让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;
}