一个基于ASP.NET Core MVC、模仿Windows资源管理器界面的网页应用,使用jstree和表格,但我不知道在选中节点变化时如何更新并显示树和表格
我有一个带产品类别表和产品表的SQL数据库,我正尝试创建一个页面:左侧显示一个从产品类别表填充的树状视图,右侧是一个表。这个表应该使用来自products数据库表的查询,并以树状视图中选中的节点ID作为参数,仅显示所选类别的产品。
我在使用Entity Framework Core,按常规覆盖 DbContext 类来获取数据。ASP.NET Core MVC没有树状控件,所以我使用jQuery的 jsTree插件,这给我带来了所有问题。
这是用于产品类别的 ProductFolder 类:
public class ProductFolder
{
[Key]
public int ProductFolderId { get; set; } = 0;
public int ParentFolderId { get; set; } = 0;
public int ProductFolderLevel { get; set; }
[Required]
public string ProductFolderName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public ProductFolder()
{
ProductFolderId = 0;
ParentFolderId = 0;
ProductFolderLevel = 0;
ProductFolderName = string.Empty;
}
}
这是 Product 类:
public class Product
{
[Key]
public int ProductId { get; set; } = 0;
public int ParentFolderId { get; set; } = 0;
public int ProductLevel { get; set; }
[Required]
[Display(Name = "Product Name")]
public string ProductName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
[Display(Name = "Width [DX]")]
public decimal DX { get; set; } = 0.0M;
[Display(Name = "Height [DY]")]
public decimal DY { get; set; } = 0.0M;
[Display(Name = "Depth [DZ]")]
public decimal DZ { get; set; } = 0.0M;
public int CNCMachineId { get; set; } = 1;
[Display(Name = "CNC Machine")]
public string CNCMachineName { get; set; } = string.Empty;
[NotMapped]
ProductFaceCollection ProductFaceCollection;
[NotMapped]
ProductInteriorCollection ProductInteriorCollection;
[NotMapped]
public decimal materialThickness { get; set; } = 18.0M;
public Product()
{
ProductId = 0;
ParentFolderId = 0;
ProductLevel = 1;
ProductName = string.Empty;
ProductFaceCollection = new ProductFaceCollection();
ProductFaceCollection.ProductFaceCells.Add(new ProductFaceCell());
ProductInteriorCollection = new ProductInteriorCollection();
ProductInteriorCollection.ProductInteriorCells.Add(new ProductInteriorCell());
}
}
以及用于填充树状视图的 JsTreeView 类:
public class JsTreeModel
{
public string id { get; set; }
public string text { get; set; }
public int level { get; set; }
public List<JsTreeModel> children { get; set; }
public JsTreeModel()
{
id = "#";
text = string.Empty;
level = 0;
children = new List<JsTreeModel>();
}
public JsTreeModel(int id, string text, int level)
{
this.id = id.ToString();
this.text = text;
this.level = level;
children = new List<JsTreeModel>();
}
}
这是控制器:
using System.Data;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Mvc;
using MyIMS_EFCore.Models;
namespace MyIMS_EFCore.Controllers
{
public class ProductsController : Controller
{
private readonly AppDbContext _context;
private readonly ILogger<ProductsController> _logger;
public ProductsController(AppDbContext context, ILogger<ProductsController> logger)
{
_context = context;
_logger = logger;
}
// GET: Products
public IActionResult Index(int? id)
{
// Create jstree nodes from product categories.
JsTreeModel ProductTreeModel = new JsTreeModel();
GetProductFolders(ref ProductTreeModel);
// Save jstree nodes as Json in ViewBag.
ViewBag.Json = JsonConvert.SerializeObject(ProductTreeModel);
// Create product list by product category, represented by jstrees selected node id.
List<Product> productList = new List<Product>();
productList = _context.Products.Where(x => x.ParentFolderId == id).ToList();
// The model for this controllers View is IEnumerable<Product> (to be used to populate the products table), so return the product list.
return View(productList);
}
public void GetProductFolders(ref JsTreeModel treeModel)
{
List<ProductFolder> rootList = new List<ProductFolder>();
rootList = _context.ProductFolders.Where(x => x.ParentFolderId == 0).ToList();
PopulateTree(rootList[0].ProductFolderId, treeModel);
}
private void PopulateTree(int parentid, JsTreeModel treeModel)
{
List<ProductFolder> productFolders = new List<ProductFolder>();
productFolders = _context.ProductFolders.Where(x => x.ParentFolderId == parentid).ToList();
treeModel.children = new List<JsTreeModel>();
for (int i = 0; i < productFolders.Count; i++)
{
JsTreeModel childNode = new JsTreeModel(productFolders[i].ProductFolderId, productFolders[i].ProductFolderName, productFolders[i].ProductFolderLevel);
treeModel.children.Add(childNode);
PopulateTree(productFolders[i].ProductFolderId, childNode);
}
}
private int StringToInt(string value)
{
if (String.IsNullOrEmpty(value))
{
return -1;
}
int result = 0;
bool b = int.TryParse(value, out result);
if (b == false)
{
return -1;
}
return result;
}
[HttpGet]
[Route("Products/Node/{id}")]
public IActionResult Node(string id)
{
int nodeid = StringToInt(id);
List<Product> productList = new List<Product>();
productList = _context.Products.Where(x => x.ParentFolderId == nodeid).ToList();
return View(productList);
}
}
}
而视图看起来是这样的:
@model IEnumerable<Product>
@{
ViewData["Title"] = "Products";
}
<h2>Products</h2>
<div style="height: 363px">
<div class="panel panel-default" style="width: 300px;height: 320px;float:left">
<div class="panel-body">
<div id="divProductTree">
<div id="jsProductTree"></div>@* Product categories *@
</div>
</div>
</div>
<div style="width: 600px;height: 320px;float:left;margin-left: 10px">
<table class="table">@* Products *@
<thead>
<tr>
<th>ProductName</th>
<th>DX</th>
<th>DY</th>
<th>DZ</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@item.ProductName</td>
<td>@item.DX</td>
<td>@item.DY</td>
<td>@item.DZ</td>
</tr>
}
</tbody>
</table>
</div>
</div>
@section Scripts {
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" />
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#jsProductTree').jstree({
'core': {
'themes': {
'icons': false
},
'data': @Html.Raw(ViewBag.Json)/* Product categories data created in Controller*/
},
'plugins': ["defaults","changed","json_data","ui"],
})
$('#jsProductTree').on('changed.jstree', async function(e, data) {
alert("(" + data.node.id + ") " + data.node.text);
let url = `@Url.Action("Node", "Products")/${encodeURIComponent(data.node.id)}`;
let response = await fetch(url);
if (response.ok) {
// parses the JSON response from the server
let newTreeData = response.json();
// redraw the jstree
}
else {
console.error('Failed to load jstree');
}
});
});
</script>
}
我想做什么?
当左侧树状视图选中的节点改变时,我想更新右侧的 Products 表。
什么工作?
一切直到并包括以下内容都有效:
1.正确填充jstree。
2. onchanged.jstree 事件,它调用 Node(string id) 函数。脚本中的alert调用显示了正确的jstree节点ID和文本,且在 Node() 函数中的 int id 变量传递了正确的节点ID。但是,我完全不知道该函数需要返回什么、为什么要返回,以及jstree在返回时如何处理它。
什么不起作用?
我觉得更多的是我不理解的部分。我原本以为可以“只要”获取所选树状节点的ID,然后以某种方式把它传给控制器,使用该ID运行查询,创建我的产品列表,刷新页面,表格就会显示正确的产品。
显然没有那么简单,我也不知道从哪里着手才好。我想也许需要在jstree内部调用 GetProductFolders() 方法来填充jstree,以便把它与 Index() 方法断开连接,这样在刷新视图时就只更新表格。
我的主要问题是:节点改变时,如何刷新页面?
我知道我这要求有点多。我显然在困境中,也许十年前我会花时间学会游泳,但现在已经没有那段时间了,再说,就像荷马·辛普森一样,我担心再往脑子里塞东西,会有更重要的东西被挤出去。
顺便说一句,我看了很多jstree的示例,这里也有大量与jstree相关的问题,但它们都在讨论单独使用jstree;我还没找到关于将jstree与其他控件一起使用的问答或示例,这似乎有点奇怪。我猜你可以只用一个独立的树状视图作为目录编辑工具?
解决方案
请查看 Start here: FileBrowser demo 的处理方式,检查源码和开发者工具中的网络标签。
根据你的帖子,起初加载是正常的,现在你想刷新类别树视图并从该类别刷新产品。
伪代码
//刷新树视图自身
$('#jsProductTree').jstree({
'core': {
'themes': {
'icons': false
},
'data' : {
'url' : '?operation=get_node',
'data' : function (node) {
return { 'id' : node.id };
}
},
},
'plugins': ["defaults","changed","json_data","ui"],
});
data.instance.refresh(); 这个调用将执行Ajax并刷新树,请查看 文档
//根据选中的节点刷新右侧视图
.on('changed.jstree', function (e, data) {
//load products for selected category
});
理想情况下,你可以把 php代码 迁移到asp.net