JSON returned rather than HTML

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
12 anni tempo fa
I'm enhancing the Admin Category List page to include Search capability.  I've modeled the enhancements based on the product search page.  However, I must be missing some Telerik magic, because the page is returning JSON rather than HTML - it looks like this:

{"Data":[],"Total":24,"Aggregates":null}

The only real change I've made to the List.cshtml  section  Html.Telerik().Grid<CategoryModel>  is to add                          .ClientEvents(events => events.OnDataBinding("onDataBinding"))

I'm sure I'm missing something else.  I haven't worked much with Telerik.  Maybe someone can also explain how the non-HttpPost  ActionResult List() can return View(...), whereby the HttpPost List can "return new JsonResult {...} "
12 anni tempo fa
Could you post your CategoryController (only modified action methods) and the new view here?
12 anni tempo fa
CategoryController.cs
This is a work in progress, but in brief, the original code is in the 'then' part of if-then-else, and new code is in the 'else' part - depending on if a search string is passed in from form submit.  Doing my own "PagedList" via skip/take, because of desired sort.

...

        [HttpPost, GridAction(EnableCustomBinding = true)]
        public ActionResult List(GridCommand command, CategoryListModel categoryListModel)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            if (String.IsNullOrWhiteSpace(categoryListModel.SearchCategoryName))
            {
                var categories = _categoryService.GetAllCategories(command.Page - 1, command.PageSize, true);
                var gridModel = new GridModel<CategoryModel>
                {
                    Data = categories.Select(x =>
                    {
                        var model = x.ToModel();
                        model.Breadcrumb = x.GetCategoryBreadCrumb(_categoryService);
                        return model;
                    }),
                    Total = categories.TotalCount
                };
                return new JsonResult
                {
                    Data = gridModel
                };
            }
            else
            {
                var categories = _categoryService.SearchCategories(categoryListModel.SearchCategoryName, true);
                var data = categories.Select(x =>
                    {
                        var model = x.ToModel();
                        model.Breadcrumb = x.GetCategoryBreadCrumb(_categoryService);
                        return model;
                    })
                    .OrderBy(m => m.Breadcrumb)
                    .Skip((command.Page - 1) * command.PageSize).Take(command.PageSize);

                var gridModel = new GridModel<CategoryModel>
                {
                    Data = data,
                    Total = categories.Count()
                };
                return new JsonResult
                {
                    Data = gridModel
                };
            }
        }


\src\Presentation\Nop.Web\Administration\Views\Category\List.cshtml

@model CategoryListModel
@using Telerik.Web.Mvc.UI
@{
    var gridPageSize = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().GridPageSize;
}

@using (Html.BeginForm())
{
    <div class="section-header">
        <div class="title">
            <img src="@Url.Content("~/Administration/Content/images/ico-catalog.png")" alt="" />
            @T("Admin.Catalog.Categories.Manage") @Html.ActionLink("(" + T("Admin.Catalog.Categories.SwitchToTreeView") + ")", "Tree")
        </div>
        <div class="options">
            <a href="@Url.Action("Create")" class="t-button">@T("Admin.Common.AddNew")</a>
            <a href="@Url.Action("ExportXml")" class="t-button">@T("Admin.Common.ExportToXml")</a>
            <a href="@Url.Action("ExportExcelAll")" class="t-button">@T("Admin.Common.ExportToExcel")</a>
            <button type="submit" id="importexcel" name="importexcel" value="importexcel" class="t-button">@T("Admin.Common.ImportFromExcel")</button>
        </div>
    </div>

    <table width="100%">
        <tr>
            <td class="adminTitle">
                @Html.NopLabelFor(model => model.SearchCategoryName):
            </td>
            <td class="adminData">
                @Html.EditorFor(model => Model.SearchCategoryName)
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <button type="submit" id="search-categories" value="search-categories" class="t-button">@T("Admin.Common.Search")</button>
            </td>
        </tr>
    </table>    
        
    <table class="adminContent">
        <tr>
            <td>
                @(Html.Telerik().Grid<CategoryModel>(Model.Categories.Data)
                        .Name("categories-grid")
                        .Columns(columns =>
                        {
                            columns.Bound(x => x.Breadcrumb).Title(T("Admin.Catalog.Categories.Fields.Name").Text);
                            columns.Bound(x => x.Published)
                                .Width("100px")
                                .Template(x => x.Published.ToString().ToLower())
                                .Centered();
                            columns.Bound(x => x.DisplayOrder)
                                .Width("100px")
                                .Centered();
                            columns.Bound(x => x.Id)
                                .Width(50)
                                .Centered()
                                .Template(x => Html.ActionLink(T("Admin.Common.Edit").Text, "Edit", new { id = x.Id }))
                                .ClientTemplate("<a href=\"Edit/<#= Id #>\">" + T("Admin.Common.Edit").Text + "</a>")
                                .Title(T("Admin.Common.Edit").Text);
                        })
                        .Pageable(settings => settings.Total(Model.Categories.Total).PageSize(gridPageSize).Position(GridPagerPosition.Both))
                        .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "Category"))
                        .ClientEvents(events => events.OnDataBinding("onDataBinding"))
                        .EnableCustomBinding(true))
            </td>
        </tr>
    </table>
}

@*import categories form*@
@{Html.Telerik().Window()
        .Name("importexcel-window")
        .Title(T("Admin.Common.ImportFromExcel").Text)
        .Content(@<text>
    @using (Html.BeginForm("ImportExcel", "Category", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        <table style="text-align: left;">
            <tr>
                <td>
                    @T("Admin.Common.ExcelFile"):
                </td>
                <td>
                    <input type="file" id="importexcelfile" name="importexcelfile" />
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <button type="submit" class="t-button t-state-default">@T("Admin.Common.ImportFromExcel")</button>
                </td>
            </tr>
        </table>
    }
    </text>)
        .Width(400)
        .Draggable(true)
        .Modal(true)
        .Visible(false)
        .Render();
}

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

        //search button
        $('#search-products').click(function () {
            //search
            var grid = $('#categories-grid').data('tGrid');
            grid.currentPage = 1; //new search. Set page size to 1
            grid.ajaxRequest();
            return false;
        });

        $("#@Html.FieldIdFor(model => model.SearchCategoryName)").keydown(function (event) {
            if (event.keyCode == 13) {
                $("#search-categories").click();
                return false;
            }
        });

        function onDataBinding(e) {
            var searchModel = {
                SearchCategoryName: $('#@Html.FieldIdFor(model => model.SearchCategoryName)').val()
            };
            e.data = searchModel;
        }

        $("#importexcel").click(function (e) {
            e.preventDefault();
            $('#importexcel-window').data('tWindow').center().open();
        });
    });
</script>




CategoryListModel.cs

using System.Web.Mvc;
using Nop.Web.Framework;
using Nop.Web.Framework.Mvc;
using Telerik.Web.Mvc;

namespace Nop.Admin.Models.Catalog
{
    public class CategoryListModel : BaseNopModel
    {
        public GridModel<CategoryModel> Categories { get; set; }

        [NopResourceDisplayName("Admin.Catalog.Products.List.SearchCategoryName")]
        [AllowHtml]
        public string SearchCategoryName { get; set; }
    }
}
12 anni tempo fa
and here's my SearchCategories which is in CategoryService

        public virtual IList<Category> SearchCategories(string name, bool showHidden = false)

and I should mention everything worked fine in my first round of refactoring - i.e. prior to changing from CategoryModel to CategoryListModel  - search results were returned in the HttpPost List(), when I hard coded a search string in the controller
12 anni tempo fa
Here are my changes:

\Nop.Web\Administration\Controllers\CategoryController.cs

        
public ActionResult List()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
                return AccessDeniedView();

            var categories = _categoryService.GetAllCategories(0, _adminAreaSettings.GridPageSize, true);
            var model = new CategoryListModel();
            model.Categories = new GridModel<CategoryModel>
            {
                Data = categories.Select(x =>
                {
                    var categoryModel = x.ToModel();
                    categoryModel.Breadcrumb = x.GetCategoryBreadCrumb(_categoryService);
                    return categoryModel;
                }),
                Total = categories.TotalCount
            };
            return View(model);
        }
        



\Nop.Web\Administration\Views\Category\List.cshtml
@model CategoryListModel
@using Telerik.Web.Mvc.UI
@{
    var gridPageSize = EngineContext.Current.Resolve<Nop.Core.Domain.Common.AdminAreaSettings>().GridPageSize;
}
@using (Html.BeginForm())
{
    <div class="section-header">
        <div class="title">
            <img src="@Url.Content("~/Administration/Content/images/ico-catalog.png")" alt="" />
            @T("Admin.Catalog.Categories.Manage") @Html.ActionLink("(" + T("Admin.Catalog.Categories.SwitchToTreeView") + ")", "Tree")
        </div>
        <div class="options">
            <a href="@Url.Action("Create")" class="t-button">@T("Admin.Common.AddNew")</a> <a href="@Url.Action("ExportXml")" class="t-button">@T("Admin.Common.ExportToXml")</a>
            <a href="@Url.Action("ExportExcelAll")" class="t-button">@T("Admin.Common.ExportToExcel")</a>
            <button type="submit" id="importexcel" name="importexcel" value="importexcel" class="t-button">@T("Admin.Common.ImportFromExcel")</button>
        </div>
    </div>

    <table width="100%">
        <tr>
            <td class="adminTitle">
                @Html.NopLabelFor(model => model.SearchCategoryName):
            </td>
            <td class="adminData">
                @Html.EditorFor(model => Model.SearchCategoryName)
            </td>
        </tr>
        <tr>
            <td colspan="2">
                <button type="submit" id="search-categories" value="search-categories" class="t-button">@T("Admin.Common.Search")</button>
            </td>
        </tr>
    </table>    
        
    <table class="adminContent">
        <tr>
            <td>
                @(Html.Telerik().Grid<CategoryModel>(Model.Categories.Data)
                        .Name("categories-grid")
                        .Columns(columns =>
                        {
                            columns.Bound(x => x.Breadcrumb).Title(T("Admin.Catalog.Categories.Fields.Name").Text);
                            columns.Bound(x => x.Published)
                                .Width("100px")
                                .Template(x => x.Published.ToString().ToLower())
                                .Centered();
                            columns.Bound(x => x.DisplayOrder)
                                .Width("100px")
                                .Centered();
                            columns.Bound(x => x.Id)
                                .Width(50)
                                .Centered()
                                .Template(x => Html.ActionLink(T("Admin.Common.Edit").Text, "Edit", new { id = x.Id }))
                                .ClientTemplate("<a href=\"Edit/<#= Id #>\">" + T("Admin.Common.Edit").Text + "</a>")
                                .Title(T("Admin.Common.Edit").Text);
                        })
                        .Pageable(settings => settings.Total(Model.Categories.Total).PageSize(gridPageSize).Position(GridPagerPosition.Both))
                        .DataBinding(dataBinding => dataBinding.Ajax().Select("List", "Category"))
                        .ClientEvents(events => events.OnDataBinding("onDataBinding"))
                        .EnableCustomBinding(true))
            </td>
        </tr>
    </table>
    
<script type="text/javascript">
    $(document).ready(function () {

        //search button
        $('#search-categories').click(function () {
            //search
            var grid = $('#categories-grid').data('tGrid');
            grid.currentPage = 1; //new search. Set page size to 1
            grid.ajaxRequest();
            return false;
        });

        $("#@Html.FieldIdFor(model => model.SearchCategoryName)").keydown(function (event) {
            if (event.keyCode == 13) {
                $("#search-categories").click();
                return false;
            }
        });

        $("#importexcel").click(function (e) {
            e.preventDefault();
            $('#importexcel-window').data('tWindow').center().open();
        });
    });

    function onDataBinding(e) {
        var searchModel = {
            SearchCategoryName: $('#@Html.FieldIdFor(model => model.SearchCategoryName)').val()
        };
        e.data = searchModel;
    }
</script>
}
@*import categories form*@
@{Html.Telerik().Window()
        .Name("importexcel-window")
        .Title(T("Admin.Common.ImportFromExcel").Text)
        .Content(@<text>
    @using (Html.BeginForm("ImportExcel", "Category", FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        <table style="text-align: left;">
            <tr>
                <td>
                    @T("Admin.Common.ExcelFile"):
                </td>
                <td>
                    <input type="file" id="importexcelfile" name="importexcelfile" />
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <button type="submit" class="t-button t-state-default">@T("Admin.Common.ImportFromExcel")</button>
                </td>
            </tr>
        </table>
    }
    </text>)
        .Width(400)
        .Draggable(true)
        .Modal(true)
        .Visible(false)
        .Render();
}




To summarize my changed:
1. Modified List() method (non-POST) in CategoryController
2. Replaced $('#search-products').click(function () { with $('#search-categories').click(function () {
3. onDataBinding java-script function should not be inside $(document).ready handler
P.S. Tested. Everything works fine
12 anni tempo fa
Thanks so much Andrei.
The cut/paste anti pattern strikes again!   And especially I thought I'd carefully replaced all "products" with "categories".
(P.S.  Sorry I forgot to also send you the non-post List() ; I'd already made those adjustments).

All is working nicely now.  Soon ready to release to you for possible inclusion in the core; some nice features I hope you'll want to include:

1) Search categories by name (contains text)
2) Export categories to Excel
3) Import categories from Excel
And maybe if I have time...  Additional "tree" Import/Export capability that would allow Excel sheet to look like
cat1
          cat2a
          cat2b
                     cat3
          cat2c
...
(would handle any # of levels)


Any thoughts on my if-then-else, and OrderBy breadcrumb.  I think it's correct that when searching, that the results return sorted alphabetically.

Actually, I do note that the existing GetAllCategories may not be quite right in regard to the orderby.  It uses ParentCategoryId, not the Parent's DisplayOrder.  Is that by design?

        public virtual IList<Category> GetAllCategories(bool showHidden = false)
        {
            var query = from c in _categoryRepository.Table
                        orderby c.ParentCategoryId, c.DisplayOrder
                        where (showHidden || c.Published) &&
                        !c.Deleted
                        select c;
         ...
        

In any case, when it's all sorted out (no pun intended) :), there are several files changed  - what would be the best way for me to get them to you?:

\src\Libraries\Nop.Services\Catalog\CategoryService.cs
\src\Libraries\Nop.Services\Catalog\ICategoryService.cs
\src\Libraries\Nop.Services\ExportImport\ExportManager.cs
\src\Libraries\Nop.Services\ExportImport\IExportManager.cs
\src\Libraries\Nop.Services\ExportImport\IImportManager.cs
\src\Libraries\Nop.Services\ExportImport\ImportManager.cs
\src\Presentation\Nop.Web\Administration\Controllers\CategoryController.cs
\src\Presentation\Nop.Web\Administration\Views\Category\List.cshtml
\src\Presentation\Nop.Web\Administration\Models\Catalog\CategoryListModel.cs
12 anni tempo fa
New York wrote:
there are several files changed  - what would be the best way for me to get them to you?:

Create a fork on codeplex (find more info here). Thanks
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.