2

テーブルを含むすべてのビューに対して、.Net MVC で優れたjQuery データテーブルプラグインを使用する再利用可能な方法を作成したいと考えています。これで、このプラグインを使用したサーバー側の処理がどのように機能するか、および適切な応答を提供するためにコントローラー ロジックをどのように記述する必要があるかについては既に理解できました。

ただし、問題は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 を作成する必要があることですが、表示ロジックは通常、ビューに記述されます。私のオプションは、部分ビューを使用してそれらをコントローラーの文字列にレンダリングするか、何らかのサーバーセッションまたはキャッシュに表示ロジック (ラムダ式) を保存してコントローラーからフェッチすることに制限されています。この最後のアプローチの大きな欠点は、セッションまたはキャッシュが不安定すぎて依存できないように見えることです。

繰り返しになりますが、

  1. AJAXサーバー側処理で jQuery データ テーブル プラグインを使用して、エンティティのテーブルを表示したいと考えています。
  2. Razor ビュー エンジンを使用して、ビューに表示ロジックを記述したい
  3. コントローラーでjQuery データテーブル AJAXリクエストを処理したい
  4. 何らかの方法で**列ごとの表示ロジック**にアクセスし、コントローラー内からビューに記述して、JSON 応答を構築したいと考えています。これは、一部の列にカスタム HTML やロード パーシャルなどを含めることができるためです。

これについて Stackoverflow に同様の質問がある場合は、私がそれらを見つけていないので、それらを指摘してください。

よろしくお願いいたします。

4

2 に答える 2

3

問題を完全にフォローしているかどうかはわかりませんが、これは非常に簡単に解決できると思います。コントローラーが機能しないビューを参照しようとする代わりに、コントローラーに必要なパラメーターを jQuery に Ajax リクエストで渡す必要があります。次に、コントローラーはこれらのパラメーターを使用して、適切な JSON 応答を送信できます。

2番で行き詰まっていると思います:

Razor ビュー エンジンを使用して、ビューに表示ロジックを記述したい

表示ロジックが JavaScript であるため、これを datatables.net で行うのは困難です。JS テンプレート プラグイン ( mustacheなど) を使用して、それをビューに書き込むことができます。次に、datatables.net js で、JSON をテンプレートにプラグインできます。

于 2013-04-12T12:22:07.787 に答える
0

この質問は古いですが、この問題と戦う他の誰かを助けるかもしれません. 私は非常に良い解決策を持っています。

ページの読み込み中にテーブル (AjaxTable) を記述するオブジェクトを作成する html ヘルパー (AjaxTable、Column) があります。このオブジェクトはセッションに保存されます。すべての 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」の代わりに使用する必要があります。たとえば、次の 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 に答える