CFGRID,CFGRIDUPDATE和额外值

时间:2011-12-06 23:02:45

标签: coldfusion coldfusion-9 cfgrid

我正在使用CFGRIDCFGRIDUPDATE将值插入数据库。问题是,每条记录都需要获得一个不在网格中的附加字段。有没有办法将该附加字段保存到记录中,或者我是否需要添加CFGRID的替代字段?

基本上,我有很多用户正在进入网格。该页面正在获取类别ID。我希望使用该类别ID保存所有用户。

另一件可行的方法是,如果我可以获得所有主键的列表,包括刚创建的记录的主键,并使用类别ID更新所有主键。但看起来CFGRIDUPDATE不会返回有关已创建行的任何信息。

2 个答案:

答案 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>