Resin Mix Application Documentation

Database
The resin mix application uses a MySQL database on isasrvr called resin. resin consists of the tables “component”, “product”, and “batch”. component Every resin mixed at OECO consists of one or more components. Many components are used in more than one resin. The component table contains data pertaining to each resin component. component create script: DROP TABLE IF EXISTS `resin`.`component`; CREATE TABLE `resin`.`component` ( `pn` varchar(12) NOT NULL, `pic_pn` varchar(45) default NULL, `name` varchar(45) NOT NULL, `full_name` varchar(100) NOT NULL, `sl` varchar(100) NOT NULL, PRIMARY KEY (`pn`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; product “product” contains each resin and information that is useful to the user when he or she mixes the resin. product create script: DROP TABLE IF EXISTS `resin`.`product`; CREATE TABLE `resin`.`product` ( `id` int(10) unsigned NOT NULL auto_increment, `part_num` varchar(100) default NULL, `oeco_pn` varchar(100) default NULL, `name` varchar(100) default NULL, `hardness` varchar(100) default NULL, `sc` varchar(100) default NULL, `op_temp_c` varchar(100) default NULL, `spec_rng_min` varchar(100) default NULL, `spec_rng_max` varchar(100) default NULL, `note` tinytext, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

recipe “recipe” is basically a junction table that tells which components are in which resins in what amounts. recipe create script: DROP TABLE IF EXISTS `resin`.`recipe`; CREATE TABLE `resin`.`recipe` ( `id` int(10) unsigned NOT NULL auto_increment, `prod_id` varchar(45) default NULL, `component` varchar(45) default NULL, `amt` varchar(45) default NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Web Components
Overview The resin mix application, like the MSDS database app, is built on MySQL and ColdFusion. In addition the resin mix application makes heavy use of JavaScript and of an AJAX framework for ColdFusion called ajaxCFC. The primary purpose of the resin mix application is to aid OECO associates in the mixing of resins. It does this by taking a resin number and desired weight of resin and outputting a list of components that should be mixed to make the resin along with the amount of each component to mix to make the desired weight of the resin. As an added benefit, the application provides of means of tracking batches of resin made using it. In order to support these two core functions, we also created a number of pages for updating the database and querying it for batch history. Home The application’s start page, index.cfm, contains only one form, The Resin Mix Formula Sheet. The Resin Mix Formula Sheet takes three pieces of information from the user; the employee number of the user, the resin number of the resin to be mixed, and the weight of resin to be mixed. The user types his or her employee number into the employee number input box. If the employee number is valid (if it is found in the database), the employee name box populates with the corresponding employee name. The resin number goes into the box labeled “Resin Number (OECO)”. An example of a resin number is “R-283” or “R290-3”. When a valid resin number is entered, information corresponding to that resin appears in the box labeled “Mfgrs Description”. In addition, a list of components required to make this resin appears in the table in the middle of the form with the headers “Component”, “PN”, and “Amount (grams)”. If a weight for the resin batch has already been input, then the weight of each component will appear in the “Amount (grams)” column. If not, these weights will appear once the user types a number into the box labeled “Weight Required (grams)”. Once the Resin Mix Formula Sheet has been filled out, the user can save it to the batch history by clicking “Save” to submit the form to a page called batch.cfm which updates the database with the batch information. The formula sheet will only submit the form if all three inputs (employee number, resin

number, and weight) are valid. An input is valid if the form elements or elements with which it is associated are updated when it is entered. For example a valid employee number is one that causes an employee name to appear in the employee name box when the user types it in the employee number box. Once a batch has been saved, it’s record can be accessed using the “history” portion of the application. Resins The “Resin Management” page, resin.cfm, is designed to allow an administrative user to add, edit, and remove resin mixes from the database. It’s main form, “Resin Mix Details” is used for all three of these functions. By default, this form is blank. To add a resin mix, the user simply types in the pertinent information in the boxes. A component is added to the resin mix’s “recipe” by clicking the button labeled “Add a Component” and filling in the form elements that appear. Changing a component’s PN in its drop down list causes the corresponding component name to appear in the box below it. When resin.cfm loads, it checks for an optional resin mix number argument in the URL. When this argument is present, it queries the resin table in the database for a resin mix record with a matching resin mix number and displays the data from that record in the appropriate input boxes. resin.cfm also queries the recipe table and loops through the records of the corresponding recipe result set, creating and populating a set of input boxes for each component inside its own table. In addition, it creates a number of tables with their display property set to “none”. These are the form elements that appear when the “Add a Component” button is clicked. When the user is ready save the resin mix, he or she clicks the “Save” button. This triggers a validation function that checks to make sure that (1) a resin mix number has been entered, (2) at least one component has been entered, (3) every component has a value in its “amount” box, and (4) the value for every component’s amount is a number. The last two validation steps are only performed on components whose parent table elements (e.g. “myTable_1”) have their display property set to “block” and thus have been displayed to the user for editing. When all the validation criteria have been satisfied, a function is called that checks to see whether or not the value in the resin mix number box belongs to a record already in the database. If it does, the user is prompted as to whether or not they want to overwrite the existing record, if not, a new record is created for the resin mix. To clear the resin mix form, the user clicks “Reset”. This directs them to the default “resin.cfm” page with all of the input boxes empty and all of the components empty and hidden. If the user clicks “Cancel”, he or she is directed to index.cfm. In addition to the Resin Mix Details form, resin.cfm also contains two other parts; a resin search and a list of recent batches. The resin search is modeled after and operates in much the same fashion as the MSDS search. When search criteria are entered and submitted, the resulting query results are presented in rows in a new page. Each row is linked back to the resin.cfm with the corresponding record’s resin mix number as an argument in the link URL. When a row is clicked, its record’s information is loaded in the resin.cfm input boxes. The list of recent batches contains rows that contain less information but are of the same nature.

Components In case a resin component in needed that is not already in the database, or the information for a particular component needs updated, we have included component.cfm to allow users to make changes. The dropdown box next to “PN”, PNSelect, contains a list of every resin in the system that it gets dynamically when the page loads. At the end of the list is an option labeled, “other, please specify:”. When this opti on is selected, a box (PNBox) appears beneath it to accept a new PN for a component and the other three boxes are cleared. Otherwise, an ajaxCFC query loads record information on whichever PN is selected in PNSelect. The button labeled “Add a new component” is a jury rig. All it does is set PNSelect to “other, please specify:”, thus triggering the function to clear the other boxes. Like the components page, the resins page can take an argument in the URL on which to query and fill the boxes with the query results: http://127.0.0.1:8500/resin/component.cfm?PN=451%2D00825%2D03 This is how links are constructed for component search results and recent changes. These two components (search and recent) work the same way as in the resins section. History The history section contains a copy of the form seen in the home section with the “readonly” attribute set for every input box. Thus, it is only for recalling information from old batches, and not for editing them. A record is recalled by supplying the page with a record ID (aka batch ID) as in argument in the URL: http://127.0.0.1:8500/resin/history.cfm?ID=12 In addition to simply typing the record number after “ID=” in the URL, there are three other ways of loading a record. For one, the user can type the number into the box next to the button labeled “Load Record” and click “Load Record”. For another, he or she can use the batch search and click one of the links that appears in the search results, and lastly, he or she can click one of the links in the list of rece nt batches.

Technical Notes
Queries with ajaxCFC and JavaScript You may have noticed that most input tags in the various pages of the resin mix application have no “value” attribute. This is because they get their values from JavaScript functions that in turn use ajaxCFC to query the database and return values. As such, the user is given immediate feedback based on his or her input. This is accomplished by making CFCs that extend a special CFC called ajax.cfc and invoking them with a Direct Web Remoting (DWR) engine. Here’s how it works: 1. The user types in a field. ex: Charles Thompson types his employee number into the input “emp_num”.

2. The field’s “onpropertychange” event fires, calling the JavaScript function indicated in its onpropertychange element. ex: emp_num’s onpropertychange fires, calling the function “employeeQuery”. 3. The function calls the “_execute” method of the DWR engine with the user’s input as an argument. ex: employeeQuery calls DWREngine._execute with Charles’ employee number as an argument. 4. The DWR engine invokes a CFC (also in its arguments) that runs the query on the input. ex: DWREngine._execute invokes query.cfc which runs this query:
select * from employee where id = '[Charles’ employee number]'

5. The DWR engine calls a callback function that takes “r” as an argument. The object, “r” contains results of the query. ex: The DWR engine calls “employeeQueryResults” 6. The callback function populates related fields with the query results using the Document Object Model (DOM) or one or more built-in DWR utilities. ex: employeeQueryResults sets the value of “emp_name” to a string containing the first and last name found in the record matching the number input by Charles and supplied to the query. Using the DWR Engine DWREngine._execute has pre-defined arguments that are outlined in the ajaxCFC documentation as follows: DWREngine._execute(_ajaxConfig._cfscriptLocation, null, 'method', args1, args2, args3, callBackFunction); ‘_execute’ takes three or more arguments: 1st: path to listener CFC that contains the method you need to call 2nd: scriptName to execute; this argument will be null 99% of the times. 3rd: methodName; this is the function inside the CFC you want to execute. 4th: arguments; you may pass one or more arguments to the ColdFusion functi on. You can add additional arguments as needed. These arguments are optional. 5th: Call-back function; ajaxCFC will check if the very last argument is a function will asynchronously (by default) call that function after resolving the ColdFusion function and getting back a result. Return Types In addition, ajaxCFC supports a number of return types. The two return types used in the resin mix app are structure and query returns. An example of a structure is seen in the case of emp_num/emp_name. In this example, the function “employeeQuery” in query.cfc queries the employee database and creates a ColdFusion structure, “mystruct” in which it stores the query results:
<cfset mystruct = StructNew()> <cfset mystruct.first_name = #qReturn.first_name#> <cfset mystruct.last_name = #qReturn.last_name#>

When this structure is returned to employeeQueryResults in index.cfm, its members are easily referenced with the names assigned them in employeeQuery:
function employeeQueryResults (r) { var full_name = r.last_name + ', ' + r.first_name if(full_name != ', '){ document.frmResin.emp_name.value = full_name; } else { document.frmResin.emp_name.value = ""; } }

While practical and useful for queries that return only one record, structure assignment is inconvenient when dealing with several rows. When querying the “recipe” table for a list of resin ingredients, rather than make a structure member for every part of every row returned, we chose instead to simply return the query itself.
<cfquery name="qReturn" datasource="resin"> Select recipe.amt as amt, component.name as component, component.pn as pn from recipe, component where recipe.component = component.pn and recipe.prod_id = '#resin_num#' </cfquery> <cfreturn qReturn>

When returned to “getRecipeResults”, the query result is represented in JavaScript as an object containing an array for each column. Thus, a query representing the recipe for the resin R-283, having two rows and three columns: amt component pn 9 DEVCON F BASE 451-00003-07 1 DEVCON F HARDENER 451-00003-08 Takes the following form in JavaScript:
am t [array] array 0 [string] 9 1 [string] 1 component [array] array 0 [string] DEVCO N F BASE 1 [string] DEVCO N F HAR DENER pn [array] array 0 [string] 451-00003-07 1 [string] 451-00003-08

Cell references are then constructed with the general form r.column[n] where column is the name of the column in the result set that you wish to reference and n is the number of the row. For example r.component*1+ has the value “DEVCON F HARDENER”. Once a query has been retrieved from the database and returned to the callback function, there are a number of ways in which it can be put in the page for presentation to the user. One method of doing this, which we’ve already described involves using the DOM directly to assign result set field values to page elements such as “emp_name”. Another way to present the user with query results is to use one of the DWR utilities. A DWR utility that works particularly well for processing query results of a varying number of rows (such as the results of a recipe query) is addRows. addRows The addRows utility is called inside the callback function of an ajaxCFC query. It follows this format: addRows(id, array, cellfuncs, [options]) Its argument are: id: The id of the table element (preferably a tbody element) array: Array (or object from DWR 1.1) containing one entry for each row in the updated table cellfuncs: An array of functions (one per column) for extracting cell data from the passed row data options: An object containing various options addRows is used in index.cfm like so:
DWRUtil.addRows("tableBody", r, [getComponent, getPN, getAmount], null);

addRows loops through the query results and displays cells in the columns specified in the “cellfuncs” arguments for each row. The “cellfuncs” variables are the ones enclosed in brackets above. They refer to functions that retrieve the value in a certain column for the current iteration of the loop. A typical bare bones one of these looks like this:
var getComponent = function (thisRow) { return thisRow.component; }

If necessary, you can perform arithmetic within the function. For example in getAmount, we translate the amount from a rational amount to the actual amount to be used in the mix:
var getAmount = function (thisRow) { var Parts = thisRow.amt/componentSum; var Weight = parseInt(document.frmResin.weight.value); var Amount = Parts*Weight; if(isNaN(Amount.toFixed(2))){ return ""; } else{ return (Amount.toFixed(2)); } }

The argument “tableBody” refers to the page element—in this case a tbody element—that takes the output of addRows:
<table> <thead> <th><br><br>Component</th> <th><br><br>PN</th> <th><br><br>Amount (grams)</th> </thead> <tbody id="tableBody"></tbody> </table>

A good reference for addRows can be found here: http://getahead.org/dwr/browser/addRows. Also, for further reading on DWR in general, visit http://getahead.org/dwr. Development\Troubleshooting Tips In JavaScript, a useful way to see variable’s value or to test whether a code block is executing is to use the built-in function “alert”. Alert simply takes an argument and displays its value in a dialog box with an “OK” button to dismiss it. You can read more about it here: http://en.wikipedia.org/wiki/Alert_dialog_box. Another tool that was indispensible while developing this site was a little JavaScript module called “dump” that gives you the options of dumping from JavaScript in much the same fashion as cfdump. “cfdump” is a ColdFusion tag that prints to the screen the value of a variable specified in its attribute “var”. To use it, simply download it here: http://www.netgrow.com.au/files/javascript_dump.cfm, include it in the head portion of your cfm or html file:
<script type='text/javascript' src="../js/dump.js"></script>

And then call it anywhere in your script with the line:
dump(variableToBeDumped);

This was particularly helpful when checking query results in returns to ajaxCFC callback functions by typing “dump(r);” before any other line inside the function. Finally, a good way to debug JavaScript is with the Error Console in Firefox. To access this, click Tools > Error Console in Firefox’s menu bar. If you click “Clear” and then load the page you’re debugging or interact with form elements, the Error Console displays a list of any errors that result. Keep in mind while using this that Firefox and Internet Explorer behave differently and will not always have the same errors. For example, Firefox does not recognize the “onpropertychange” event, and as such it will not display errors associated with this event.

Sign up to vote on this title
UsefulNot useful