使用 JSGrid 的主从关系






4.50/5 (6投票s)
使用类似 Excel 的 JSGrid 控件(快速编辑模式下的 XsltListViewWebPart)创建主从表单。
引言
主从表单非常常见。经典的例子是一个订单及其中的商品列表……
因此,从 SharePoint 的角度来看,您需要做的是将可编辑的明细列表嵌入到主编辑表单中,如下所示
通常,如果明细数据表类似 Excel,那将非常方便。 Excel 对用户来说通常非常熟悉,并且可以快速有效地添加信息。
幸运的是,在 SharePoint 中,有一个名为 JSGrid 的客户端控件,它与 Excel 工作表表非常相似。
因此,在本文中,我将利用 JSGrid 来实现一个主从表单。
将 XLV 切换到快速编辑模式
在 SharePoint 2013 中,JSGrid 用于在快速编辑模式下显示列表视图。列表视图由 XsltListViewWebPart(也称为 XLV)表示。
所以想法是将明细列表 XLV 添加到您的主列表编辑表单页面,然后以某种方式将其切换到快速编辑模式。
表单页面可以是普通的列表表单(EditForm),也可以是自定义页面布局。后者通常是创建可打印表单的绝佳方法。
所以第一步是将 XLV 添加到您的表单页面。这是基础:只需在浏览器中将页面切换到编辑模式,然后在此处添加具有明细数据的列表。
现在,通常列表视图会有一个链接,允许将它们切换到快速编辑模式
所以我只是侦察了这个链接后面的脚本。这是它
_spBodyOnLoadFunctions.push(function()
{
EnsureScriptParams('inplview', 'InitGridFromView', '{YOUR-GUID-HERE}');
});
GUID 是 XLV 视图的。您可以通过页面源代码等方式进行侦察。搜索 g_ViewIdToViewCounterMap
如果您页面上有一个 XLV,那么只有一个这样的位置。右边的 GUID 正是您需要的。
或者,您可以在 SharePoint Designer 中侦察视图 ID,或者以任何其他您喜欢的方式获取它。
因此,如果您将 EnsureParams 脚本添加到页面,那么当页面加载时,列表将以快速编辑模式显示。
如果您正在自定义普通的列表表单,请将代码放入 EditForm 页面,例如通过 Script Editor WebPart(当然,不要忘记将 js 包装在 <script>
标签中)。
注意:如果您的网站启用了 MDS,请参阅下面的“最小下载策略”部分。
如果您正在创建自定义页面布局,请将代码放入页面源代码中,并且不要忘记将脚本包装在 <PublishingWebControls:EditModePanel runat="server">
标签中,以便它仅在页面处于编辑模式时出现。
所以我们页面上有明细 XLV,并且我们已经将其切换到编辑模式。但它显示所有项目,而不是仅与当前主列表项目相关的项目。让我们来修复它!
过滤 XLV 以仅显示相关项目
在 SharePoint Designer 中可以轻松进行过滤。
编辑表单
如果您正在自定义编辑列表表单页面:在 SPD 中找到您的列表并打开列表表单页面编辑器,然后执行以下操作
- 找到 XLV(搜索
XsltListViewWebPart
标签)。在 XLV 内部,找到 ParameterBindings 标签并在其中添加以下代码<ParameterBinding Name="itemId" Location="QueryString(ID)" />
- 修改
Query
标签(在 XLV 内的View
标签内)。将“YourLookupFieldInternalName”替换为您主查找字段的内部名称,该字段从明细指向主。<Where><Eq><FieldRef Name="YourLookupFieldInternalName" LookupId="TRUE" /><Value Type="Integer">{itemId}</Value></Eq></Where>
保存页面并查看。完成!XLV 现在已过滤。
结果
自定义页面布局
如果您正在创建自定义页面布局,请在 SPD 的 masterpage
文件夹中找到您的页面布局。打开页面布局编辑器并执行以下操作
- 找到 XLV(搜索
XsltListViewWebPart
标签)。在 XLV 之上,添加以下代码:<div style="display:none;"> <SharePointWebControls:NumberField ID="ItemID" ControlMode="Display" FieldName="1d22ea11-1e32-424e-89ab-9fedbadb6ce1" runat="server"/> </div>
- 在 XLV 内部,找到 ParameterBindings 标签并在其中添加以下代码
<ParameterBinding Name="itemId" Location="Control(ItemID,ItemFieldValue)" />
- 最后,修改
Query
标签(在 XLV 内的View
标签内)。将“YourLookupFieldInternalName”替换为您主查找字段的内部名称,该字段从明细指向主。<Where><Eq><FieldRef Name="YourLookupFieldInternalName" LookupId="TRUE" /><Value Type="Integer">{itemId}</Value></Eq></Where>
在我的例子中,结果看起来是这样的
新项目问题
好的,您已经过滤了可编辑的网格,听起来很棒!……但是如何添加新项目,我们是否真的必须每次都强制用户手动选择订单?当然不是。
为了解决这个问题,显然我需要做这样的事情
- 确定何时将新记录添加到 JSGrid
- 在“订单”列中填写正确的值
- 隐藏该列本身,以便用户无法编辑它
JSGrid 是一个很大程度上未被记录的控件,拥有巨大的客户端 API,并且处理起来很困难。当试图弄清楚如何使用 JSGrid 做某事时,我通常会深入到 SharePoint 的 JS 内部:)
关于 JSGrid 的好消息是它有大量的事件,您可以附加处理程序到这些事件。我发现 SP.JsGrid.EventType.OnEntryRecordPropertyChanged 事件在录入记录行(用于添加新记录的最后一行)发生更改时触发。
为了订阅事件,使用 jsGrid.AttachEvent 方法。
jsGrid 对象本身可以从 JSGrid 容器元素的属性中获取。此容器元素的 ID 可以通过 g_SPGridInitInfo
全局变量确定。
var viewId = "{GUID-OF-YOUR-VIEW}";
var jsGridContainer = $get("spgridcontainer_" + g_SPGridInitInfo[viewId].jsInitObj.qualifier)
var jsGrid = jsGridContainer.jsgrid;
jsGrid.AttachEvent(SP.JsGrid.EventType.OnEntryRecordPropertyChanged, function() {
debugger;
});
我在浏览器的 JS 控制台中运行了此代码,然后尝试更改录入记录行。当我点击“BMW W5”值时,调试器弹出
很好,现在我们可以探索堆栈跟踪、参数,并思考我们还能做什么。例如,这是传递给此事件处理程序的参数的屏幕截图
如您所见,参数描述了更改:字段名称、旧值和新值、字段类型以及一些其他信息。JSGrid 并非完全文档化,但其 API 非常好!
在堆栈跟踪的上面,我找到了 UpdateProperties 方法。事实证明,此方法可用于更新给定行中的任何属性。所以过了一段时间,我用以下代码更新了主查找列(lock
用于防止递归)
var viewId = '{YOUR-VIEW-GUID-HERE}';
var orderFieldValue = 7; // ID of the master item
var orderFieldInternalName = "Order0"; // Internal name of the lookup field
var jsGridContainer = $get("spgridcontainer_" + g_SPGridInitInfo[viewId].jsInitObj.qualifier)
var jsGrid = jsGridContainer.jsgrid;
var lock = 0;
jsGrid.AttachEvent(SP.JsGrid.EventType.OnEntryRecordPropertyChanged, function(args) {
if (lock == 0) {
lock = 1;
var update = SP.JsGrid.CreateUnvalidatedPropertyUpdate(args.recordKey,orderFieldInternalName,orderFieldValue,false);
jsGrid.UpdateProperties([update], SP.JsGrid.UserAction.UserEdit);
lock = 0;
}
});
我直接从控制台测试了代码,它奏效了!
最后一步是隐藏查找列。这非常简单
jsGrid.HideColumn(orderFieldInternalName);
完善
好了,现在它工作了,但肯定需要一些清理
- 不需要“添加列”按钮。
- JSGrid 列可以被过滤和排序,这个功能不知何故覆盖了我的订单过滤器。
- 应隐藏“停止编辑此列表”链接。
为了隐藏“添加列”,我不得不修改我用来将表单切换到编辑视图的脚本。
_spBodyOnLoadFunctions.push(function()
{
var viewId = "{YOUR-VIEW-GUID-HERE}";
g_SPGridInitInfo[viewId].jsInitObj.canUserAddColumn = false;
g_SPGridInitInfo[viewId].jsInitObj.showAddColumn = false;
EnsureScriptParams('inplview', 'InitGridFromView', viewId);
});
为了防止列排序和过滤,我用以下代码
var columns = jsGrid.GetColumns();
for (var i in columns)
{
columns[i].isSortable = false;
columns[i].isAutoFilterable = false;
}
jsGrid.UpdateColumns(new SP.JsGrid.ColumnInfoCollection(columns));
为了隐藏“停止编辑此列表”行,我不得不将其包装在一个 div 中,并为该 div 设置 style.display='none'
var hero = $get('Hero-' + ctx.wpq);
var wrapper = document.createElement("div");
wrapper.style.display = 'none';
hero.parentNode.insertBefore(wrapper, hero);
wrapper.appendChild(hero);
最后,需要进行一些包装以确保自定义的应用时机正确。我最终使用了 CSR OnPostRender 事件。
最终代码
所以,这里是 Script Editor Web Part 的最终完整 JS 代码,用于进行我在文章中描述的所有更改(XLV 过滤不包含,因为它是在声明性中完成的,请参阅“过滤 XLV 以仅显示相关项目”一章)
<script type="text/javascript">
_spBodyOnLoadFunctions.push(function()
{
var viewId = '{YOUR-VIEW-GUID-HERE}';
var orderFieldValue = GetUrlKeyValue("ID"); // ID of the master item
var orderFieldInternalName = "Order0"; // Internal name of the lookup field
// Switch to edit view and hide "Add column" button
SP.SOD.executeFunc('inplview', 'InitGridFromView', function() {
g_SPGridInitInfo[viewId].jsInitObj.canUserAddColumn = false;
g_SPGridInitInfo[viewId].jsInitObj.showAddColumn = false;
InitGridFromView(viewId);
});
SP.SOD.executeFunc('clienttemplates.js', 'SPClientTemplates.TemplateManager.RegisterTemplateOverrides', function() {
var done = false;
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function(ctx) {
if (ctx.view != viewId || ctx.enteringGridMode || !ctx.inGridMode || done)
return;
// Hide quick links (aka "hero") row
var hero = $get('Hero-' + ctx.wpq);
var wrapper = document.createElement("div");
wrapper.style.display = 'none';
hero.parentNode.insertBefore(wrapper, hero);
wrapper.appendChild(hero);
// Fetch JSGrid object
var jsGridContainer = $get("spgridcontainer_" + g_SPGridInitInfo[viewId].jsInitObj.qualifier)
var jsGrid = jsGridContainer.jsgrid;
// Automatically update master lookup column
var lock = 0;
jsGrid.AttachEvent(SP.JsGrid.EventType.OnEntryRecordPropertyChanged, function(args) {
if (lock == 0) {
lock = 1;
var update = SP.JsGrid.CreateUnvalidatedPropertyUpdate(args.recordKey,orderFieldInternalName,orderFieldValue,false);
jsGrid.UpdateProperties([update], SP.JsGrid.UserAction.UserEdit);
lock = 0;
}
});
// Make columns non-sortable and non-filterable
var columns = jsGrid.GetColumns();
for (var i in columns)
{
columns[i].isSortable = false;
columns[i].isAutoFilterable = false;
}
jsGrid.UpdateColumns(new SP.JsGrid.ColumnInfoCollection(columns));
// Hide master lookup column
jsGrid.HideColumn(orderFieldInternalName);
done = true;
}
});
});
});
</script>
注意:如果您正在创建自定义页面布局,请将以下行替换为
var orderFieldValue = GetUrlKeyValue("ID"); // ID of the master item
请使用此行
var orderFieldValue = <SharePointWebControls:NumberField ControlMode="Display" FieldName="1d22ea11-1e32-424e-89ab-9fedbadb6ce1" runat="server"/>; // ID of the current page
最小下载策略
启用最小下载策略后,Script Editor Web Part 的效果不太好。因此,对于普通的列表表单,我们需要稍微更改代码,并将其放入一个单独的文件中,然后以某种方式将此文件附加到页面,例如通过 JSLink。
所以我创建了一个 js 文件 /Style Library/JSGrid-masterDetails.js 并进行了修改
SP.SOD.executeFunc('clienttemplates.js', 'SPClientTemplates.Utility.ReplaceUrlTokens', function() {
function init()
{
// all the code here, starting from _spBodyOnLoadFunctions.push(... and so on.
}
RegisterModuleInit(SPClientTemplates.Utility.ReplaceUrlTokens("~site/Style Library/JSGrid-masterDetails.js"), init);
init();
});
然后,要将其附加到您的列表表单,请编辑列表表单页面,然后编辑 XLV WebPart
在 Miscellaneous 部分,找到 JSLink 属性,并将您的 js 文件链接放在此处
链接应如下所示:~site/Style Library/JSGrid-masterDetails.js
。
源代码
存档包含以下文件
- JSGrid-masterDetails.js - 通过 JSLink 附加到列表编辑表单的代码
- XLV_ListEditForm.aspx - 列表表单上 XLV 的示例标记
- CustomPageLayout.aspx - 自定义页面布局的示例标记。除了文章中解释的内容之外,它还通过 CSR 使用自定义显示模板,以便列表在显示模式下对打印机友好
请不要忘记 aspx 文件中的标记取决于实际列表,因此这些文件在您的系统上将无法正常工作,主要提供参考。
结论
JSGrid 具有强大而灵活的 API,可以为用户提供非常方便的类似 Excel 的界面。您可能已经注意到,我与 JSGrid 本身并没有遇到太多问题。包装很困难,但 JSGrid 本身很棒!
在主从表单的情况下,JSGrid 提供了绝佳的机会,可以使用类似 Excel 的界面以及自定义页面布局和普通的列表表单。