Copy Primary key value into another table as Foreign Key? - foreign-keys

CREATE TRIGGER copy_pk_into_fk_trigger
AFTER insert ON tableA
FOR EACH ROW
BEGIN
insert into tableB (tblA_ID) values (new.tblA_ID);
END$$
i'm using the above trigger to copy the Primary Key value from tableA and inset into the tableB as foreign key. But after that, whenever, i edit the record in tableB using detailsView in ASP.net , the foreign key value goes "NULL". And this cause no more connection b/w parent and child. plz help me.
[tableA having col: tblA_ID, tblA_val and
TableB contains tblB_ID, tblA_ID, tblB_str
etc etc]

I don't think this have something to do with your trigger. As the trigger will only run after an insert has been triggered. Can you show me the query for updating in the detailsView? Are you sure you didn't mentioned the update statement as well?

Here's complete source code of SqlDATASource and DetailsView:
<asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:HADIConnectionString %>"
DeleteCommand="DELETE FROM "INDENT_FILE_NO" WHERE "FILE_NO_ID" = :original_FILE_NO_ID AND (("INDENT_ID" = :original_INDENT_ID) OR ("INDENT_ID" IS NULL AND :original_INDENT_ID IS NULL)) AND (("FILE_NO" = :original_FILE_NO) OR ("FILE_NO" IS NULL AND :original_FILE_NO IS NULL)) AND (("FILE_DESCRIPTION" = :original_FILE_DESCRIPTION) OR ("FILE_DESCRIPTION" IS NULL AND :original_FILE_DESCRIPTION IS NULL)) AND (("INDENTER" = :original_INDENTER) OR ("INDENTER" IS NULL AND :original_INDENTER IS NULL))" InsertCommand="INSERT INTO "INDENT_FILE_NO" ("FILE_NO_ID", "INDENT_ID", "FILE_NO", "FILE_DESCRIPTION", "INDENTER") VALUES (:FILE_NO_ID, :INDENT_ID, :FILE_NO, :FILE_DESCRIPTION, :INDENTER)" OldValuesParameterFormatString="original_{0}" ProviderName="<%$ ConnectionStrings:HADIConnectionString.ProviderName %>"
SelectCommand="SELECT * FROM "INDENT_FILE_NO" WHERE ("INDENT_ID" = :INDENT_ID)"
UpdateCommand="UPDATE "INDENT_FILE_NO" SET "INDENT_ID" = :INDENT_ID, "FILE_NO" = :FILE_NO, "FILE_DESCRIPTION" = :FILE_DESCRIPTION, "INDENTER" = :INDENTER WHERE "FILE_NO_ID" = :original_FILE_NO_ID AND (("INDENT_ID" = :original_INDENT_ID) OR ("INDENT_ID" IS NULL AND :original_INDENT_ID IS NULL)) AND (("FILE_NO" = :original_FILE_NO) OR ("FILE_NO" IS NULL AND :original_FILE_NO IS NULL)) AND (("FILE_DESCRIPTION" = :original_FILE_DESCRIPTION) OR ("FILE_DESCRIPTION" IS NULL AND :original_FILE_DESCRIPTION IS NULL)) AND (("INDENTER" = :original_INDENTER) OR ("INDENTER" IS NULL AND :original_INDENTER IS NULL))" ConflictDetection="CompareAllValues">
<UpdateParameters>
<asp:Parameter Name="INDENT_ID" Type="Decimal" />
<asp:Parameter Name="FILE_NO" Type="String" />
<asp:Parameter Name="FILE_DESCRIPTION" Type="String" />
<asp:Parameter Name="INDENTER" Type="String" />
<asp:Parameter Name="original_FILE_NO_ID" Type="Decimal" />
<asp:Parameter Name="original_INDENT_ID" Type="Decimal" />
<asp:Parameter Name="original_FILE_NO" Type="String" />
<asp:Parameter Name="original_FILE_DESCRIPTION" Type="String" />
<asp:Parameter Name="original_INDENTER" Type="String" />
</UpdateParameters>
Details View :
<asp:DetailsView ID="DetailsView2" runat="server" AutoGenerateRows="False" DataKeyNames="FILE_NO_ID" DataSourceID="SqlDataSource2" Height="50px" Width="287px" >
<Fields>
<asp:BoundField DataField="FILE_NO_ID" HeaderText="FILE_NO_ID" ReadOnly="True" SortExpression="FILE_NO_ID" Visible="False" />
<asp:BoundField DataField="INDENT_ID" HeaderText="INDENT_ID" SortExpression="INDENT_ID" Visible="False" />
<asp:BoundField DataField="FILE_NO" HeaderText="FILE_NO" SortExpression="FILE_NO" />
<asp:BoundField DataField="FILE_DESCRIPTION" HeaderText="FILE_DESCRIPTION" SortExpression="FILE_DESCRIPTION" />
<asp:BoundField DataField="INDENTER" HeaderText="INDENTER" SortExpression="INDENTER" />
<asp:CommandField ShowEditButton="True" ButtonType="Button" />
</Fields>
</asp:DetailsView>
But now, when i press the edit button and edit the fields and then update them, the data of that fields remains same. Means no action takes.

Related

ColdFusion function returns an error "The system has attempted to use an undefined value"?

I have function that should save some form fields. This function is able to process multiple insert rows. So far I was able to create logic to handle this but the only issue so far I have once user tries to save the record. The server response looks like this:
The system has attempted to use an undefined value, which usually indicates a programming error, either in your code or some system code. Null Pointers are another name for undefined values. coldfusion
At first I was looking and trying to find which value is not defined/missing in my function but so far I couldn't detect the issue. The next thing I did was commenting out the cfquery. After that I was getting message Form successfully saved.. This tells me that my code is breaking inside of the cfquery tag. Here is example of my code:
<cffunction name="saveRecords" access="remote" output="false" returnformat="JSON">
<cfargument name="formID" type="string" required="true" default="">
<cfargument name="status1" type="string" required="true" default="0">
<cfargument name="status2" type="string" required="true" default="0">
<cfargument name="status3" type="string" required="true" default="0">
<cfargument name="status4" type="string" required="true" default="0">
<cfargument name="status5" type="string" required="true" default="0">
<cfargument name="comment1" type="string" required="true" default="">
<cfargument name="comment2" type="string" required="true" default="">
<cfargument name="comment3" type="string" required="true" default="">
<cfargument name="comment4" type="string" required="true" default="">
<cfargument name="comment5" type="string" required="true" default="">
<cfset local.fnResults = structNew() />
<cfset local.runProcess = true />
<cfset local.arrStatus = arrayNew(1) />
<cfloop collection="#arguments#" item="i">
<cfif findNoCase(left(i,6), "status") and arguments[i] eq 1>
<cfset arrayAppend(local.arrStatus, right(i,1)) />
</cfif>
</cfloop>
<cfset local.frmValidation = {
formID : len(arguments.formID),
status1 : ArrayContains([0,1], trim(arguments.status1)),
status2 : ArrayContains([0,1], trim(arguments.status2)),
status3 : ArrayContains([0,1], trim(arguments.status3)),
status4 : ArrayContains([0,1], trim(arguments.status4)),
status5 : ArrayContains([0,1], trim(arguments.status5))
}/>
<cfloop collection="#frmValidation#" item="i">
<cfif !frmValidation[i] or !arrayLen(arrStatus)>
<cfset local.runProcess = false />
<cfset local.fnResults = {status: 400, message: "Form data is either missing or incorrect."}>
<cfbreak>
</cfif>
</cfloop>
<cfif runProcess>
<!---
<cfquery name="saveRec" datasource="testDB">
<cfloop array="#arrStatus#" index="i">
INSERT INTO formDetails (
formid,
refid,
status,
comment,
userid,
lastupdate
)
SELECT
<cfqueryparam cfsqltype="cf_sql_numeric" value="#trim(arguments.formID)#">,
#i#,
<cfqueryparam cfsqltype="cf_sql_bit" value="#trim(arguments["status"&i])#">,
<cfqueryparam cfsqltype="cf_sql_varchar" maxlength="8000" value="#trim(arguments["comment"&i])#" null="#!len(trim(arguments["comment"&i]))#">,
<cfqueryparam cfsqltype="cf_sql_varchar" maxlength="6" value="#trim(session.userid)#" null="#!len(trim(session.userid))#">,
#now()#
WHERE NOT EXISTS (
SELECT refid
FROM formDetails
WHERE formid = <cfqueryparam cfsqltype="cf_sql_numeric" value="#trim(arguments.formID)#">
AND refid = #i#
)
</cfloop>
</cfquery>
--->
<cfset local.fnResults = {status: 200, message: "Form successfully saved!"}>
</cfif>
<cfreturn fnResults>
</cffunction>
One thing that I forgot to mention is that before I commented out cfquery, I tried to save the records and error will occur that I mentioned above but at the same time record is saved in database. That is weird since I didn't expect query to execute successfully because of the error. I use Sybase database and here are the details about formDetails table:
column name data type length
recid numeric 18 PK
formid numeric 10 FK
refid numeric 18 FK
status bit 1
comment varchar 8000
userid varchar 6
lastupdate datetime 23
I'm not sure where my code is breaking and why I'm getting an error message about undefined value. If anyone can help or provide some useful resource on how to fix this issue please let me know.
UPDATE
I have been looking for solution on how to fix this problem and I tried using cfscript with UNION ALL. This was suggested from few people and here is example of my code:
remote function saveRecords(required string formID, string status1="0", string status2="0", string status3="0", string status4="0", string status5="0", string comment1="", string comment2="", string comment3="", string comment4="", string comment5="") output=false returnFormat="JSON" {
local.fnResults = structNew();
local.runProcess = true;
local.arrReference = arrayNew(1);
try {
local.frmValidation = {
formID : len(arguments.formID),
status1 : ArrayContains([0,1], arguments.status1),
status2 : ArrayContains([0,1], arguments.status2),
status3 : ArrayContains([0,1], arguments.status3),
status4 : ArrayContains([0,1], arguments.status4),
status5 : ArrayContains([0,1], arguments.status5)
};
for ( i in frmValidation ) {
if ( !frmValidation[i] ) {
local.runProcess = false;
local.fnResults = {status: 400, message: "Form data is either missing or incorrect."};
break;
}
}
for ( fld in arguments ) {
if ( findNoCase(left(fld,6), "status") && arguments[fld] == 1 ) {
arrayAppend(arrReference, right(fld,1));
}
}
if ( runProcess ) {
local.qrySql = "INSERT INTO formDetails(recid, formid, refid, status, comment, userid, lastupdate)";
local.qryParams = [];
for ( idx=1 ; idx<=arraylen(arrReference) ; idx++ ) {
local.referenceID = arrReference[idx];
local.recPK = trim(arguments.formID) & trim(referenceID);
local.statusVal = trim(arguments["status" & local.referenceID]);
local.commentVal = trim(arguments["comment" & local.referenceID]);
if ( idx != 1 ) {
local.qrySql &= " UNION ALL ";
}
local.qrySql &= " SELECT ?,?,?,?,?,?,?";
qryParams.append({cfsqltype="cf_sql_numeric", value=local.recPK});
qryParams.append({cfsqltype="cf_sql_numeric", value=trim(arguments.formID)});
qryParams.append({cfsqltype="cf_sql_numeric", value=local.referenceID});
qryParams.append({cfsqltype="cf_sql_tinyint", value=local.statusVal});
qryParams.append({cfsqltype="cf_sql_varchar", value=local.commentVal, maxlength=8000, null=!len(local.commentVal)});
qryParams.append({cfsqltype="cf_sql_varchar", value=trim(session.userid), maxlength=6, null=!len(trim(session.userid))});
qryParams.append({cfsqltype="cf_sql_timestamp", value=now()});
}
local.qryResult = queryExecute(qrySQL, qryParams, {datasource="#application.datasource#", result="insertRecords"});
local.fnResults = {result: insertRecords, sql: qrySql, params: qryParams, array: arrReference, args: arguments, status: 200, message: "Form successfully saved!"};
}
} catch (any e) {
local.fnResults = {sql: qrySql, params: qryParams, error: e, status: 400, message: "Error! Something went wrong..."};
}
return fnResults;
}
After I tried to save the record from I got the message Something went wrong.... I looked in response and all I can see is error that is pointing to the this line:
local.qryResult = queryExecute(qrySQL, qryParams, {datasource="#application.datasource#", result="insertRecords"});
I checked the database and records are in there. No details about this error nothing. At least I'm not getting an error that I described in the question above but still no clue what is causing my code to crash.
I've found two things
1) you insert query does not have any value. INSERT INTO tablename( columns ... ) VALUES ( column values.. ) But you have missed VALUE key word in your query.
2) Whenever you are going to do two query operations in single cfquery tag you should add some Connection String in your testDB data sources. Which is available in CFML admin part. Open you data source and set below options. In CFML admin :
If you are using Lucee means: Please check the below options in your data source which is available in lucee server / web admin,

How to set entity as unique Key in ColdBox

I'm creating a model entity in ColdBox.
component persistent="true" {
property name="id" type="int" fieldType="id" generator="increment";
property name="stamptime" type="timestamp";
property name="type" type="string" length="1" sqltype="varchar(1)";
property name="serial" type="string" length="100" sqltype="varchar(100)";}
the id field is set as identity and as primary key. the problem is i want to set serial field as unique key.. is there any way to set this field as unique key?
Have you tried defining in the property as follow:
component persistent="true" {
property name="serial" type="string" length="100" sqltype="varchar(100)" unique="true";
// and / or as a validation via constraints?
this.constraints = {
serial = { unique=true };
} //constraints
} //component

Can't get process start variables

I'm trying to get process start variables via rest, but I can't receive any start variable.
When i try:
http://192.168.30.1:8080/engine-rest/process-definition/key/invoice/form-variables
I got only this:
{}
How to get process start variables?
Thats because the resource Get Start Form Variables only supports Generated Start Forms.
The invoice process uses Embedded Start Form. The start event of the invoice process references an external HTML file, which is used to render the Start form.
See here:
<startEvent id="StartEvent_1" name="Invoice
received" camunda:formKey="embedded:app:forms/start-form.html">
<outgoing>SequenceFlow_1</outgoing>
</startEvent>
If you want to receive the start form variables, the start event of the process must be look like the following example:
<startEvent id="start">
<extensionElements>
<camunda:formData>
<camunda:formField id="stringField" label="String Field" type="string" defaultValue="someString">
<camunda:validation>
<camunda:constraint name="maxlength" config="10" />
<camunda:constraint name="minlength" config="5" />
</camunda:validation>
</camunda:formField>
<camunda:formField id="longField" label="Long Field" type="long" defaultValue="5">
<camunda:validation>
<camunda:constraint name="max" config="10" />
<camunda:constraint name="min" config="3" />
</camunda:validation>
</camunda:formField>
<camunda:formField id="customField" label="Custom Field" type="string">
<camunda:validation>
<camunda:constraint name="validator" config="org.camunda.bpm.engine.test.api.form.CustomValidator" />
</camunda:validation>
</camunda:formField>
<camunda:formField id="dateField" label="Date Field" type="date" defaultValue="10/01/2013" />
</camunda:formData>
</extensionElements>
</startEvent>
See here for the complete example process.
In that case the FormService can resolve the form variables and the request will return the defined variables.

Lines of code run more than once causes concurrent transactions

Using CFWheels I sometimes get an error email stating that there was a DB error [Amazon](500310) Invalid operation: table 250818 dropped by concurrent transaction. After reading about this, it seems this can happen when a second transaction starts before the first is finished. (I'm not editing data here, just SELECT statements).
The odd part is in my trace in the error email I get something like the following:
Line 85 in models\ModelA.cfc
Line 73 in controllers\ControllerA.cfc
Line 85 in models\ModelA.cfc
Line 73 in controllers\ControllerA.cfc
Like it tried to run the same code twice. FYI, you can't follow my code and go from 73 to 85 and then back to 73. You just can't. It doesn't work that way. You do go from 73 to 85 and that makes sense, but I don't see why it's doing it twice.
What would cause my code to at least appear like it's running twice in the same request as I feel that is the underlying reason this DB error is happening.
EDIT: Example where this has taken place
Controller:
<cfparam name="params.key" default="0" />
<cfset resource = model("courseResource").findByKey(params.key) />
<cfif isObject(resource)>
<cfset course = model('Course').courseInfo(resource.id) />
<cfquery dbtype="query" name="course">
SELECT * FROM course ORDER BY id
</cfquery>
<!--- HERE IS WHERE ERROR OCCURRED --->
<cfset var enrollment = model("course").getCourseEnrollment(resource.id) />
<cfset resource.handoutdate = dateFormat(resource.handoutDate,'mm/dd/yyyy') />
<cfelse>
<cfset logEvent(SESSION.user.getProperty('id'),'Editing course resource - Missing',params.key) />
<cfset redirectTo(back="true",error="Course resource could not be found") />
</cfif>
Model Function:
<cfargument name="id" type="string" required="true" />
<cfargument name="students" type="string" required="false" default="" />
<cfquery datasource="DSN" name="local.qEnrollment">
SELECT
DISTINCT *fieldnames*
FROM course_dim c
INNER JOIN course_section_dim cs ON cs.course_id = c.id
INNER JOIN enrollment_dim e ON e.course_section_id = cs.id AND e.type = 'TypeName' AND e.workflow_state = 'active'
INNER JOIN user_dim u ON u.id = e.user_id
INNER JOIN pseudonym_dim p ON p.user_id = u.id
WHERE c.id IN (<cfqueryparam value="#ARGUMENTS.id#" list="yes" />)
AND c.sis_source_id IS NOT NULL
<cfif ARGUMENTS.students NEQ ''>
AND p.unique_name IN (<cfqueryparam value="#ARGUMENTS.students#" list="yes" />)
</cfif>
AND e.workflow_state = 'active'
ORDER BY u.name
</cfquery>
<cfreturn qEnrollment />
I'm not sure what you could possibly get from that, but that is essentially it. I mean there is nothing crazy. I just don't understand how the line that calls the model function can appear twice in the trace. It doesn't make any sense.

CFGRID, CFGRIDUPDATE and extra values

I'm using CFGRID and CFGRIDUPDATE to insert values into a database. The problem is, each record needs to get an additional field that isn't in the grid. Is there any way to save that additional field to the record, or do I have to whip up an alternative to CFGRID?
Basically, I have a bunch of users I'm entering into a grid. The page is getting a category id. I want all of the users to be saved with that category id.
Another thing that would work is if I could get a list of all primary keys, including those for the just-created records, and update all of them with the category id. But it looks like CFGRIDUPDATE doesn't return any information about the rows that were created.
-- original answer removed --
Based on your comment to my original answer, there is now an implied assumption that Category_ID is a foreign key, possibly on a join table, that cannot (for whatever reason) be included in the initial display query--or, that you simply wish to include a dynamic variable into the grid which will be included during inline inserts, without the user intervening (ie. physically preventing them from selecting the Category_ID themselves, but that it will still be dynamic in some capacity, say...by being fed from a URL var).
If correct, I believe the true question leans closer to:
Can a CFGRID update multiple tables/dynamic columns via CFGRIDUPDATE?
Short answer: No
Long answer: Yes, but not via CFGRIDUPDATE--rather, via CFQUERY and a little more work on the CFGRID, via CFC binds and the CFAJAXPROXY.
Solution:
1) You'll need two files for this solution. File #1 is your Component which wraps the query functionality; we'll call it 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>
Pay attention to these key pieces of the CFC:
a) getUsers() returns the current user data along with their CategoryID. You'll have to re-write this query to match your schema, but the key takeaway is that this is a data population query, so all the data that's necessary for creating a user should also be present for updating the user as well. This also assumes you only have 1 CategoryID per User (which many developers de-normalize by leaving the CategoryID on the Users table--I'll leave that to your discretion).
b) addNewUser() takes expects the form/grid submission to pass along the new name--as well as the category_id--but we know ahead of time we're not going to be asking that the Category_ID be filled out by the person entering the form/grid data--we'll do that programmatically. The end result is still the same, however--the query is going to need to know both values. For brevity, I've left off <CFTRANSACTION> calls, but bear it in mind--you're about to execute three queries in succession, one of which (the 3rd) depends on dynamic data from the other (the 1st and 2nd)--so you'll need to keep concurrency in mind when you move forward with this type of design.
c) Disregard editUser() for now--you will need to populate it at some point--it simply needs to exist for this demonstration to work.
2) The second file you'll need is the front end--the grid itself, we'll call it cfgrid.cfm.
We'll go through this one, top to bottom, as its quite large, and each chunk of code will need explanation:
<cfparam name="URL.Category_ID" default=4 />
The first line of the template parameterizes a URL variable, which we want to use to programmatically provide behind-the-scenes assignment to new users. Use your own mechanism to supply a dynamic Category_ID as needed.
<cfajaxproxy cfc="cfgrid" jsclassname="dataproxy">
This line causes ColdFusion to create a javascript object named 'dataproxy' and wrap it in a container necessary to provide access the core functions that exist in the CFC you point it to...and in this case, you are pointing it to 'cfgrid', which is our first file mentioned above (cfgrid.cfc). Therefore, you can now comfortably expect that you have a javascript object with getUsers() and addNewUser() methods.
<html>
<head>
<script type="text/javascript" src="/CFIDE/scripts/ajax/ext/package/toolbar/toolbar.js"></script>
Here, you begin your HTML document tags, and include a reference to one of the Ajax libraries included in ColdFusion, the toolbar.js file.
<script type="text/javascript">
var dataproxy = new dataproxy();
dataproxy.setCallbackHandler(handleResult);
function handleResult(response)
{
alert(response);
}
Here, you create a local instance of the dataproxy object (remember above, from the <CFAJAXPROXY> call) and assign a callback handler, which points to another javascript function 'handleResult'. This is the process you employ when dealing with asynchronous communication--a fundamental part of working in 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 });
}
This init() function creates the javascript objects necessary to drive communication to the cfgrid. You obtain a reference to the cfgrid via ColdFusion.Grid.getGridObject, and from there, access the grid's header, which allows you to target to toolbar, and add a button "Add User", which you then programmatically decide to call a new function when it is clicked...that function is named "onAdd"...
function onAdd(button,event)
{
ColdFusion.Window.show('addUserWin');
}
Unsurprisingly, here is that onAdd() function, which displays a new window to add a user (its the window that contains the input field for the user's fullname).
Finally, the new AddUserWin Window above is going to need its own function to add a user, since we will need to provide the Category_ID dynamically--as opposed to letting the user supply it. So, addUser() will do just that:
function addUser()
{
var f = document.frmUser;
dataproxy.addNewUser(
f.txtFullname.value,
f.txtCategory_ID.value
);
ColdFusion.Window.hide('addUserWin');
grid.refresh();
}
</script>
</head>
In addUser(), we refer to the <FORM> below by its name (frmUser), and call our javascript proxy object's addNewUser() method--which maps to the CFC. As expected, it'll need to know the values of the new user, so we'll pass it the value of txtFullname and txtCategory_ID. We finish by hiding the window and refreshing the grid.
Remember, we're asynchronous now, so we don't need to read a result and display it--that result will fire via the callback handler assigned above in the handleResult() method.
Now, build the arguments that will feed the population of the 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})" />
Here we:
1. Name the grid "UserGrid" (as we have referred to it by that name in javascript above),
2. Make it render using html,
3. Tell it to bind its data when the page loads,
4. Bind that data via the cfgrid.cfc, calling the getUsers() method (and passing in the current parameters of the cfgrid via its page, pagesize, sortcolumn, and sortdirection values),
5. Make it editable, and
6. Assign an onChange handler, in case we also want to allow the users to be edited. This last part (unfortunately) is necessary, so I was unable to strip it out for this example.
Now, build the <CFFORM> and <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>
Here, we populate the grid with our arguments specified above, and you'll note we display only the "FullName" field on the grid, yet User_ID and Category_ID are still present, and a part of the dataset; they simply aren't displayed to the front-end.
Last but not least, the window that will pop-up when a user clicks the "Add New User" button, which provides the interface we need to allow user entry, while at the same time, control (behind the scenes) what Category_ID is dynamically supplied:
<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>
This call to CFWINDOW provides the necessary "pop-up" to encapsulate the form. Note that the form name is frmUser, as we have referred to it above in code. Also, note that the name of the fields match (including their case) what are referred to in javascript. This form displays the Fullname field to the user to fill out, while the Category_ID stays hidden, but is still programmatically driven by you--via your URL parameter at the top of this code example. Finally, the button, when clicked, fires the addUser() method, which you'll recall is the one that talks to the javascript object--which in turn--talks to the CFC--and commits your data to the database.
Finally, just before you complete this template, don't forget to fire your init() javascript function!
<cfset ajaxOnLoad("init")>
</html>
Hope this helps.
Adapted from CRUD with cfgrid html format (Source: Anuj Gakhar)
There is another way. It involves handling directly the form variables which are posted by CFGRID. Here's some example code:
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.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>