我使用CFGRID
和CFGRIDUPDATE
插入值到数据库中。问题是,每个记录需要获得一个不在网格中的附加字段。是否有任何方法可以将该附加字段保存到记录中,或者我必须创建CFGRID的替代方案?
基本上,我有一群用户,我要输入一个网格。页面正在获取类别id。我想用这个类别id保存所有用户。
另一个可以工作的事情是,如果我可以得到所有主键的列表,包括那些刚刚创建的记录,并用类别id更新所有它们。但是看起来CFGRIDUPDATE
没有返回任何关于创建的行的信息。
——删除原始答案——
根据您对我原始答案的评论,现在有一个隐含的假设,即Category_ID是一个外键,可能在连接表上,不能(无论出于何种原因)包含在初始显示查询中—或者,您只是希望在网格中包含一个动态变量,该变量将在内联插入期间包含,而无需用户干预(即)。物理上阻止他们自己选择Category_ID,但在某种程度上它仍然是动态的,比如…
如果正确,我认为真正的问题更接近于:
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>
调用,但请记住——您将连续执行三个查询,其中一个(第三个)依赖于来自另一个(第一个和第二个)的动态数据——因此,当您继续进行这种类型的设计时,您需要牢记并发性。
c)现在忽略editUser()—您将需要在某个时候填充它—它只需要存在以使此演示工作。
你需要的第二个文件是前端——网格本身,我们称之为cfgrid.cfm。我们将从头到尾看一遍,因为它相当大,每个代码块都需要解释:
<cfparam name="URL.Category_ID" default=4 />
模板的第一行参数化了一个URL变量,我们希望使用该变量以编程方式为新用户提供后台分配。根据需要,使用您自己的机制来提供动态的Category_ID。
<cfajaxproxy cfc="cfgrid" jsclassname="dataproxy">
这一行导致ColdFusion创建一个名为'dataproxy'的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函数'handleResult'。这是您在处理异步通信时使用的过程——这是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获得对cfgrid的引用。getGridObject,然后从那里,访问网格的标题,它允许您瞄准工具栏,并添加一个按钮"添加用户",然后您以编程方式决定在单击时调用新函数…该函数名为"onAdd"…
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. 将网格命名为"UserGrid"(就像我们在上面的javascript中提到的那样),2. 使用html进行渲染,3.告诉它在页面加载时绑定它的数据,4. 通过cfgrid绑定该数据。cfc,调用getUsers()方法(并通过其page、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>
在这里,我们用上面指定的参数填充网格,你会注意到我们在网格上只显示"FullName"字段,但User_ID和Category_ID仍然存在,并且是数据集的一部分;它们只是不显示在前端。
最后但并非最不重要的是,当用户单击"添加新用户"按钮时弹出的窗口,它提供了我们需要允许用户进入的界面,同时,控制(在幕后)动态提供的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的调用提供了必要的"弹出"来封装表单。注意,表单名称是frmUser,正如我们在上面的代码中提到的那样。另外,请注意,字段的名称与javascript中引用的名称匹配(包括大小写)。这个表单显示Fullname字段供用户填写,而Category_ID保持隐藏,但仍然由您以编程方式驱动—通过此代码示例顶部的URL参数。最后,当单击该按钮时,将触发addUser()方法,您应该记得,该方法是与javascript对象通信的方法,而javascript对象又与CFC通信,并将数据提交到数据库。
最后,在你完成这个模板之前,不要忘记启动你的init() javascript函数!
<cfset ajaxOnLoad("init")>
</html>
希望对你有帮助。
改编自CRUD与cfgrid html格式(来源:Anuj Gakhar)
还有另一种方法。它涉及到直接处理由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>
then in post. cfg
<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>