我正在使用CFGRID
和CFGRIDUPDATE
将值插入数据库。问题是,每条记录都需要获得一个不在网格中的附加字段。有没有办法将该附加字段保存到记录中,或者我是否需要添加CFGRID的替代字段?
基本上,我有很多用户正在进入网格。该页面正在获取类别ID。我希望使用该类别ID保存所有用户。
另一件可行的方法是,如果我可以获得所有主键的列表,包括刚创建的记录的主键,并使用类别ID更新所有主键。但看起来CFGRIDUPDATE
不会返回有关已创建行的任何信息。
答案 0 :(得分:3)
- 原始答案已删除 -
根据您对原始答案的评论,现在有一个隐含的假设,即Category_ID是一个外键,可能在连接表上,不能(无论出于什么原因)包含在初始显示查询中 - 或者,你只是希望在网格中包含一个动态变量,它将在内联插入过程中包含在内,而无需用户干预(即物理上阻止他们自己选择Category_ID,但它仍然是动态的,如果...通过从URL var)提供。
如果正确的话,我相信真正的问题更贴近:
CFGRID可以通过CFGRIDUPDATE更新多个表/动态列吗?
简短回答:否
答案很长:是的,但不是通过CFGRIDUPDATE - 而是通过CFQUERY和CFGRID上的更多工作,通过CFC绑定和CFAJAXPROXY。
<强>解决方案:强>
1)此解决方案需要两个文件。文件#1是包装查询功能的组件;我们称之为cfgrid.cfc
<cfcomponent>
<cfset this.dsn = "gridexample" />
<cffunction name="getUsers" returntype="any" access="remote" output="false">
<cfargument name="page" />
<cfargument name="pageSize" />
<cfargument name="gridsortcolumn" />
<cfargument name="gridsortdirection" />
<cfset var getUsers = 0 />
<cfquery name="getUsers" datasource="#this.dsn#">
SELECT Users.UserID, Users.FirstName, UserCategories.Category_ID
FROM Users
INNER JOIN UserCategories ON (Users.User_ID = UserCategories.UserID)
<cfif arguments.gridsortcolumn neq "" or arguments.gridsortdirection neq "">
order by #arguments.gridsortcolumn# #arguments.gridsortdirection#
</cfif>
</cfquery>
<cfreturn QueryConvertForGrid(getUsers, page, pageSize) />
</cffunction>
<cffunction name="addNewUser" returntype="string" access="remote" output="false">
<cfargument name="fullname" type="string" required="true" />
<cfargument name="category_id" type="numeric" required="true" />
<cfquery datasource="#this.dsn#">
INSERT INTO Users
(
fullname
)
VALUES
(
<cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.fullname#">
)
</cfquery>
<cfquery name="getPkey" datasource="#this.dsn#">
SELECT Max(User_ID) as PKey
FROM Users
</cfquery>
<cfquery datasource="#this.dsn#">
INSERT INTO UserCategories
(
User_ID,
Category_ID
)
VALUES
(
<cfqueryparam cfsqltype="cf_sql_integer" value="#getPKey.PKey#" />
<cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.category_id#" />
)
</cfquery>
<cfreturn "User Added" />
</cffunction>
<cffunction name="editUser" access="remote">
<cfargument name="gridaction">
<cfargument name="gridrow">
<cfargument name="gridchanged">
</cffunction>
</cfcomponent>
注意CFC的这些关键部分:
a)getUsers()返回当前用户数据及其CategoryID。您必须重新编写此查询以匹配您的架构,但关键的一点是这是一个数据填充查询,因此创建用户所需的所有数据也应该存在以便更新用户也是。这也假设您每个用户只有1个CategoryID(许多开发人员通过将CategoryID保留在Users表中而取消规范化 - 我将自行决定是否这样做。)
b)addNewUser()接受表单/网格提交传递新名称 - 以及category_id - 但我们提前知道我们不会要求Category_ID是由输入表格/网格数据的人填写 - 我们将以编程方式执行此操作。然而,最终结果仍然是相同的 - 查询将需要知道两个值。为简洁起见,我已经停止了<CFTRANSACTION>
次来电,但请记住这一点 - 您将要连续执行三个查询,其中一个(第三个)依赖于来自的动态数据其他(第1和第2) - 所以当你继续使用这种类型的设计时,你需要记住并发性。
c)现在忽略editUser() - 你需要在某个时候填充它 - 它只需要存在就可以使这个演示工作。
2)你需要的第二个文件是前端 - 网格本身,我们称之为cfgrid.cfm。
我们将通过这个,从上到下,因为它非常大,每个代码块都需要解释:
<cfparam name="URL.Category_ID" default=4 />
模板的第一行参数化一个URL变量,我们希望用它以编程方式为新用户提供幕后分配。使用您自己的机制根据需要提供动态Category_ID。
<cfajaxproxy cfc="cfgrid" jsclassname="dataproxy">
这一行导致ColdFusion创建一个名为&#39; dataproxy&#39;的javascript对象。并将其包装在一个容器中,以便访问您指向的CFC中存在的核心功能......在这种情况下,您将其指向“cfgrid”,这是我们提到的第一个文件上面(cfgrid.cfc)。因此,您现在可以轻松地期望您有一个带有getUsers()和addNewUser()方法的javascript对象。
<html>
<head>
<script type="text/javascript" src="/CFIDE/scripts/ajax/ext/package/toolbar/toolbar.js"></script>
在这里,您开始HTML文档标记,并包含对ColdFusion中包含的一个Ajax库的引用,toolbar.js文件。
<script type="text/javascript">
var dataproxy = new dataproxy();
dataproxy.setCallbackHandler(handleResult);
function handleResult(response)
{
alert(response);
}
在这里,您创建了一个dataproxy对象的本地实例(请记住上面的<CFAJAXPROXY>
调用)并分配一个回调处理程序,它指向另一个javascript函数&#39; handleResult&#39;。这是您在处理asynchronous communication时使用的过程 - 这是在Ajax中工作的基本部分。
function init()
{
grid = ColdFusion.Grid.getGridObject("UserGrid");
var gridHead = grid.getView().getHeaderPanel(true);
var tbar = new Ext.Toolbar(gridHead);
tbar.addButton({text:"Add User", handler:onAdd });
}
此init()函数创建驱动与cfgrid通信所必需的javascript对象。您可以通过ColdFusion.Grid.getGridObject获取对cfgrid的引用,并从那里访问网格标题,这样您就可以将目标指向工具栏,并添加一个按钮&#34; Add User&#34;,然后以编程方式决定在单击时调用新函数...该函数名为&#34; onAdd&#34; ...
function onAdd(button,event)
{
ColdFusion.Window.show('addUserWin');
}
不出所料,这是onAdd()函数,它显示一个新窗口来添加用户(它的窗口包含用户的全名的输入字段)。
最后,上面的新AddUserWin窗口将需要自己的功能来添加用户,因为我们需要动态提供Category_ID - 而不是让用户提供它。所以,addUser()会做到这一点:
function addUser()
{
var f = document.frmUser;
dataproxy.addNewUser(
f.txtFullname.value,
f.txtCategory_ID.value
);
ColdFusion.Window.hide('addUserWin');
grid.refresh();
}
</script>
</head>
在addUser()中,我们通过名称(frmUser)引用下面的<FORM>
,并调用我们的javascript代理对象的addNewUser()方法 - 它映射到CFC。正如所料,它需要知道新用户的值,因此我们将txtFullname和txtCategory_ID的值传递给它。我们通过隐藏窗口并刷新网格来完成。
请记住,我们现在异步,因此我们不需要读取结果并显示结果 - 该结果将通过handleResult()方法中上面指定的回调处理程序触发。
现在,构建将为CFGRID
:
<cfset args = StructNew() />
<cfset args.name = "UserGrid" />
<cfset args.format = "html" />
<cfset args.bindOnLoad = "true" />
<cfset args.bind = "cfc:cfgrid.getUsers({cfgridpage},{cfgridpagesize},{cfgridsortcolumn},{cfgridsortdirection})" />
<cfset args.selectmode = "edit" />
<cfset args.onchange = "cfc:cfgrid.editUser({cfgridaction},{cfgridrow},{cfgridchanged})" />
我们在这里: 1.命名网格&#34; UserGrid&#34; (正如我们在上面的javascript中通过该名称引用它), 2.使用html进行渲染, 3.告诉它在页面加载时绑定其数据, 4.通过cfgrid.cfc绑定该数据,调用getUsers()方法(并通过其页面,pagesize,sortcolumn和sortdirection值传入cfgrid的当前参数), 5.使其可编辑,并且 6.分配onChange处理程序,以防我们也想允许编辑用户。最后一部分(不幸的是)是必要的,所以我无法将其剥离出来。
现在,构建<CFFORM>
和<CFGRID>
:
<cfform>
<cfgrid attributeCollection="#args#">
<cfgridcolumn name="User_ID" display="false">
<cfgridcolumn name="Category_ID" display="false">
<cfgridcolumn name="FullName" header="Full Name">
</cfgrid>
</cfform>
在这里,我们使用上面指定的参数填充网格,并且您注意我们只显示&#34; FullName&#34;网格上的字段,但仍然存在User_ID和Category_ID,以及数据集的一部分;它们根本没有显示在前端。
最后但并非最不重要的是,当用户点击&#34;添加新用户&#34;时会弹出的窗口。按钮,提供允许用户输入所需的界面,同时控制(幕后)动态提供的Category_ID:
<cfwindow name="addUserWin" modal="true" resizable="false" title="Add New User">
<form name="frmUser">
<input type="hidden" name="txtCategory_ID" value="<cfoutput>#URL.Category_ID#</cfoutput>" />
<table width="100%">
<tr>
<td>Fullname</td>
<td><input type="text" name="txtFullname" value=""></td>
</tr>
<tr>
<td colspan="2"><input type="button" value="Add User" onclick="javascript:addUser();"></td>
</tr>
</form>
</cfwindow>
这次对CFWINDOW的调用提供了必要的&#34;弹出&#34;封装表单。请注意,表单名称是frmUser,正如我们在上面的代码中引用它。另请注意,字段的名称与javascript中引用的内容相匹配(包括其大小写)。此表单显示要填写的用户的Fullname字段,而Category_ID保持隐藏状态,但仍由您编程驱动 - 通过此代码示例顶部的URL参数。最后,单击该按钮时,将触发addUser()方法,您将回忆起该方法是与javascript对象进行对话的方法 - 后者又与CFC进行对话 - 并将您的数据提交给数据库中。
最后,在您完成此模板之前,请不要忘记启动您的init()javascript函数!
<cfset ajaxOnLoad("init")>
</html>
希望这有帮助。
改编自CRUD with cfgrid html format(资料来源:Anuj Gakhar)
答案 1 :(得分:2)
还有另一种方式。它涉及直接处理由CFGRID发布的表单变量。这是一些示例代码:
form.cfm:
<cfform method="post" action="post.cfm">
<cfoutput><input type="hidden" name="ParentID" value="#ParentID#"></cfoutput>
<cfgrid format="html" name="GridData" query="Records" insert="yes" delete="yes" selectmode="edit">
<cfgridcolumn name="RecordID" display="no">
<cfgridcolumn name="RecordName" width="150" header="Name" headeralign="left" dataalign="left" select="Yes" display="Yes">
<cfgridcolumn name="RecordColor" width="150" header="Color" headeralign="left" dataalign="left" select="Yes" display="Yes">
</cfgrid>
<br />
<input type="submit" value="Save Records" />
</cfoutput>
</cfform>
然后在post.cfm
<cfif isDefined("GridData.RowStatus.Action") and isArray(GridData.RowStatus.Action)>
<cfloop from="1" to="#ArrayLen(GridData.RowStatus.Action)#" index="i">
<cfswitch expression="#GridData.RowStatus.Action[i]#">
<cfcase value="I">
<cfquery name="Records_INSERT" datasource="#request.maindatasource#" blockfactor="100">
INSERT INTO Records (RecordName, RecordColor, RecordParent)
VALUES (
<cfqueryparam cfsqltype="cf_sql_varchar" value="#Trim(GridData.RecordName[i])#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#Trim(GridData.RecordColor[i])#">,
<cfqueryparam cfsqltype="cf_sql_integer" value="#Val(ParentID)#">
)
</cfquery>
</cfcase>
<cfcase value="U">
<cfquery name="Records_UPDATE" datasource="#request.maindatasource#" blockfactor="100">
UPDATE Records
SET
RecordName = <cfqueryparam cfsqltype="cf_sql_varchar" value="#Trim(GridData.RecordName[i])#">,
RecordColor = <cfqueryparam cfsqltype="cf_sql_varchar" value="#Trim(GridData.RecordColor[i])#">
WHERE
RecordID=<cfqueryparam cfsqltype="cf_sql_integer" value="#GridData.original.RecordID[i]#">
</cfquery>
</cfcase>
<cfcase value="D">
<cfquery name="Records_DELETE" datasource="#request.maindatasource#" blockfactor="100">
DELETE
FROM Records
WHERE
RecordID=<cfqueryparam cfsqltype="cf_sql_integer" value="#GridData.original.RecordID[i]#">
</cfquery>
</cfcase>
</cfswitch>
</cfloop>
</cfif>