PDA

View Full Version : Fill one form field from another...



mhodgson
05-04-2007, 12:18 PM
On to the next problem.
On my form I have a text box to type a number into. I also have a hidden box that stores a value from a database.

What I want to do is take those two values, perform a calculation and fill out a new text box on screen for entry into a database.

<td>
<input name="userinput" type="text" id="userinput">
</td>
<td><input name="dbvalue" type="hidden" id="dbvalue"></td>
<td>
<input name="calculated" type="text" id="calculated">
</td>

Take the value from 'userinput' and the value from 'dbvalue', do the calculation, and show the result in 'calculated'

'calculated' will then be inputed into a database.

jscheuer1
05-04-2007, 04:57 PM
Without knowing more about this, but assuming that it is in a valid form, I would just do:


<td>
<input name="userinput" type="text" id="userinput" onchange="this.form.elements['calculated'].value=this.form.elements['dbvalue'].value*1+this.value*1;">
</td>
<td><input name="dbvalue" type="hidden" id="dbvalue"></td>
<td>
<input name="calculated" type="text" id="calculated">
</td>

The onchange event could (should probably) include something to ensure that the entry(s) is a number. There could be a button to click to calculate the result but, it would need no name, id or event, and wouldn't even need to be in the form:


<input type="button" value="Calculate">

Important: Since this does involve, as you say, writing the result to a database, there should be a server side fall back unless you can be guaranteed that the user could never view this content without javascript enabled.

mhodgson
05-05-2007, 09:06 AM
Thanks for the suggestion. I'll work on it and see what I get.


I have already got it working in PHP perfectly, it's just that I want to allow the user to see the result immediatly before it gets inputed into the DB.
It is for an Intranet so Javascript will be running, but as you say I will probably leave the PHP method in as well just to make sure.

Thanks again and I'll be back if there's a problem.

mhodgson
05-08-2007, 12:13 PM
Okay I admit defeat, the best I an get is NaN to appear.
The suggestion works perfectly but when I adapted it for my code it don't work.
This is what I have tried




<td>
<input name="userinput" type="text" id="userinput" onchange="this.form.elements['calculated'].value=Math.abs(parseInt((this.value = (this.value.split('.'))[0], 10) * 12 + parseInt(this.value[1], 10)) - this.form.elements['dbvalue']);
">
</td>
<td><input name="dbvalue" type="hidden" id="dbvalue" value="5"></td>
<td>
<input name="calculated" type="text" id="calculated">
</td>

User input is a decimal type number (eg. 8.1 age in year.month to be converted to age in months) which is why it needs splitting.
Ideally the calculation would be a function (I have several pages that will use it).

As I said the best I can get is NaN to appear in the 'caculated' box.
Hope someone can help.

mwinter
05-08-2007, 03:40 PM
I don't know what you're calculating so I can't verify correctness, but consider the following function:



function calculateValue(form) {
var controls = form.elements,
age = controls.userinput.value.split('.'),
months = (age[0] * 12) + (+age[1]);

controls.userinput.value = age[0];
controls.calculated.value = Math.abs(months - controls.dbvalue.value);
}

Mike

jscheuer1
05-08-2007, 03:49 PM
At this point it might be better to break it out into a separate function, but this works:


<form action="">

<table>
<tr>
<td>
<input name="userinput" type="text" id="userinput" onchange="this.form.elements['calculated'].value=this.value.split('.')[0]*12 + this.value.split('.')[1]*1 - this.form.elements['dbvalue'].value*1;">
</td>
<td><input name="dbvalue" type="hidden" id="dbvalue" value="5"></td>
<td>
<input name="calculated" type="text" id="calculated">
</td>
</tr>
</table>


</form>

Here's one way to break it out into a function:


<script type="text/javascript">
function calcFrm(el){
var frm=el.form.elements;
for (var i = 0, vals=el.value.split('.'); i < 2; i++)
vals[i]=vals[i]? vals[i]*(i? 1 : 12) : 0;
vals[2]=frm['dbvalue'].value*1;
frm['calculated'].value=isNaN(i=vals[0] + vals[1] - vals[2])? '' : i;
}
</script>

<form action="">

<table>
<tr>
<td>
<input name="userinput" type="text" id="userinput" onchange="calcFrm(this)">
</td>
<td><input name="dbvalue" type="hidden" id="dbvalue" value="5"></td>
<td>
<input name="calculated" type="text" id="calculated">
</td>
</tr>
</table>


</form>

Either way, it could still use some error/validation checking, which I threw in with the function to at least make sure that the result is a number.

mhodgson
05-08-2007, 07:30 PM
jscheuer1, many thanks, the function seems to be doing just what I need.
you have been most helpfull
However I would like to understand it a little more.
I get some of it but other bits are unclear

function calcFrm(el){ This obviously sets the function (does (el) mean this element-userinput ?)

var frm=el.form.elements; This sets a string variable frm to save code typing

for (var i = 0, vals=el.value.split('.'); i < 2; i++) This sets splits the variable vals and puts it into an array with two values

vals[i]=vals[i]? vals[i]*(i? 1 : 12) : 0; I Know what this does but don't understand the process

vals[2]=frm['dbvalue'].value*1; This gets the value from the db field (but why *1??)

frm['calculated'].value=isNaN(i=vals[0] + vals[1] - vals[2])? '' : i; This calculates the 'calculated' value but I'm not too sure of the process - why isNan?
}

Hope I am on the right track as the function is good. The only value that needs validating is the user input, the one from the db is already validated.

Twey
05-08-2007, 08:48 PM
function calcFrm(el){ This obviously sets the function (does (el) mean this element-userinput ?)Yes, in this case.
var frm=el.form.elements; This sets a string variable frm to save code typingNot only that, it also saves lookups -- people often forget that . is an operator. Long strings of repeated lookups slow down code no end.
vals[i]=vals[i]? vals[i]*(i? 1 : 12) : 0; I Know what this does but don't understand the processIt's equivalent to:
if(vals[i]) {
if(i) {
vals[i] *= 1;
} else {
vals[i] *= 12;
}
} else {
vals[i] = 0;
}
vals[2]=frm['dbvalue'].value*1; This gets the value from the db field (but why *1??) Quick way of converting to a number.
frm['calculated'].value=isNaN(i=vals[0] + vals[1] - vals[2])? '' : i; This calculates the 'calculated' value but I'm not too sure of the process - why isNan?Sets it to blank if there's a non-numerical value involved, rather than displaying "NaN."

mwinter
05-08-2007, 09:11 PM
function calculateValue(form) {
var controls = form.elements,
match = /^(\d+)\.(\d+)$/.exec(controls.userinput.value),
months;

if (!match) {
/* Validation failed */
return;
}
months = (match[1] * 12) + Number(match[2]);
controls.calculated.value = months - controls.dbvalue.value;
}

A validating form of my earlier function.



function calcFrm(el){ This obviously sets the function (does (el) mean this element-userinput ?)

It will be whatever is passed when the function is called, but in John's previous post, yes, it's the userinput form control.



var frm=el.form.elements; This sets a string variable frm to save code typing

No, it assigns an object reference to the variable, frm. The elements property is a collection (think array) of controls within a form. You got the reason partially right, though: not only does it save typing, it's quicker as the script interpreter doesn't have to perform so many property accessor operations.



for (var i = 0, vals=el.value.split('.'); i < 2; i++) This sets splits the variable vals and puts it into an array with two values

vals[i]=vals[i]? vals[i]*(i? 1 : 12) : 0; I Know what this does but don't understand the process

This is a rather convoluted way of writing:



var vals = el.value.split('.');

if (vals[0]) vals[0] = vals[0] * 12;
else vals[0] = 0;
if (vals[1]) vals[1] = vals[1] * 1;
else vals[1] = 0;

The conditional operator (? :&#41; is somewhat like an expression form of an if statement.

&#160;&#160;cond ? expr1 : expr2

The first expression, cond, is evaluated as a boolean. If true, the entire expression evaluates to expr1. If false, the expression evaluates to expr2.



vals[2]=frm['dbvalue'].value*1; This gets the value from the db field (but why *1??)

The value property of form controls is always a string. There are many ways to convert a string to number, including multiplying by one (1). However, it's not the best way. Either use unary plus:



vals[2] = +frm.dbvalue.value;

or the Number constructor function:



vals[2] = Number(frm.dbvalue.value);




frm['calculated'].value=isNaN(i=vals[0] + vals[1] - vals[2])? '' : i; This calculates the 'calculated' value but I'm not too sure of the process - why isNan?
}

Simplified, this is equivalent to:



var value = vals[0] + vals[1] - vals[2];

if (isNaN(value)) frm.calculated.value = '';
else frm.calculated.value = value;

/* or:
* frm.calculated.value = isNaN(value) ? '' : value;
*/

If one of the string-to-number conversions fails, it will yield the special number value, NaN. Therefore, using the isNaN function is a simple form of error detection. However, using regular expressions to validate input before beginning work on it (as in my code above) is generally a better approach.

Hope that helps,
Mike

jscheuer1
05-09-2007, 03:41 AM
Thanks, Twey and mwinter. I don't think I could have done better myself in explaining the code. I especially enjoyed Twey's explanation as, it seemed to follow my thought process very closely. About Mike's (mwinter's) idea of validating earlier in the process - it is a good idea and one I considered, but figured it wasn't crucial. If I follow Mike's suggested code utilizing the exec method correctly however, it would reject 9 months or 8 years (values representing only years or only months). I could be mistaken though as, I am not intimately familiar with the exec method.

Just to be clear though, my code would accept:

8.1

or:

.9

or:

8

of those three, I believe Mike's would only accept 8.1.

If I've got that wrong, I would be happy to learn in what way.

Thanks again guys.

mhodgson
05-09-2007, 07:36 AM
Thanks guys for the clear explanations.

It always gets a little confusing (for me anyway) when there are several ways to do the same thing and when things can get contracted into shorter forms of coding.

Now, I not only have a function that works but I understand it clearly, I'll make sure I add some comments for future reference.

Can't thank you enough.

mhodgson
05-09-2007, 12:02 PM
I knew I spoke too soon.
Works great until I put it in a repeat region where the function is called on each line.


<tr>
<td><input name="userinput" type="text" id="userinput" onchange="calcFrm(this)" /></td>
<td><input name="dbvalue" type="hidden" id="dbvalue" value="5"></td>
<td><input name="calculated" type="text" id="calculated"></td>
<td>&nbsp;</td>
</tr>
<tr>
<td><input name="userinput" type="text" id="userinput" onchange="calcFrm(this)" /></td>
<td><input name="dbvalue" type="hidden" id="dbvalue" value="5"></td>
<td><input name="calculated" type="text" id="calculated"></td>
<td>&nbsp;</td>
</tr>
I realise that this is because the function is 'confused' by all the fields with the same name.
Now,
I know that when the repeat region is generated by PHP I can add a [number] after each input name and id eg. <input name="dbvalue[1]" but how to transfer this into the function so each line works?

mwinter
05-09-2007, 12:26 PM
If I follow Mike's suggested code utilizing the exec method correctly however, it would reject 9 months or 8 years (values representing only years or only months).

That's correct: the OP stated that the input would come in the form of "m.n". It's trivial to change though.

Sorry, mhodgson, but I don't have time to address your problem right now. I have to dash...

Mike

jscheuer1
05-09-2007, 02:36 PM
Thanks Mike, I knew it would be easy to change, just wanted to be sure I was reading the code correctly.

On to mhodgson's question - If all will be as you describe with the same unique identifier being added to all of the elements names, this modification to the script will work:


<script type="text/javascript">
function calcFrm(el){
var frm=el.form.elements, nId=el.name.replace(/userinput/,'');
for (var i = 0, vals=el.value.split('.'); i < 2; i++)
vals[i]=vals[i]? vals[i]*(i? 1 : 12) : 0;
vals[2]=frm['dbvalue'+nId].value*1;
frm['calculated'+nId].value=isNaN(i=vals[0] + vals[1] - vals[2])? '' : i;
}
</script>

This assumes, as I understood you to say that subsequent generated markup will also be in a valid form (it can be the same form or a different form for each) and look like so:


<td><input name="userinput[1]" type="text" id="userinput[1]" onchange="calcFrm(this)" /></td>
<td><input name="dbvalue[1]" type="hidden" id="dbvalue[1]" value="5"></td>
<td><input name="calculated[1]" type="text" id="calculated[1]"></td>

or something similar, the key points being that:


The base names (userinput, dbvalue and calculated) shall be employed.
Whatever is added to these names will be added identically to all names in a given grouping of three of these inputs.
Whatever is added will be unique to a given grouping.
It will be added at the end and only at the end of each name.


Notes: The id's are not required for the script, but should be unique anyway, in order to follow the specification on id's which requires that each one be unique on a page. If your PHP code doesn't require them and you have no other script on the page that might need them, all of the id's may simply be omitted.

mhodgson
05-09-2007, 03:16 PM
It works!
I get the '+nId' bit, it just adds the Id number to the end of the value, simple when you see it.

nId=el.name.replace(/userinput/,'') This was the bit that really stumped me

Though I understand what this does- gets the value of nId but am not sure why 'replace' is used. Looked it up but found the explanation as clear as mud.
The function then applies the same nId to the other two form fields because they have the same ID being on the same row of the repeat region.

Just out of curiosity, if each field had a different ID would I use this code?

nId_db=el.name.replace(/dbvalue/,'')
nId_calc=el.name.replace(/calculated/,'')

to get their ID values.

jscheuer1
05-09-2007, 03:42 PM
Replace uses its regular expression, in this case:

/userinput/

to determine what to replace in the original string. The slashes are the delimiters. Then comes the replacement string, which in this case is empty. So, we are left with whatever the name was minus 'userinput', in this case, the unique identifier or nothing, if no identifier was used.

The other part of your question is unclear to me - it seems like it might not be exactly applicable, but I don't quite get what you would want to have happen. However, now that you (hopefully) understand how replace is being used here, that will answer it for you.

mhodgson
05-09-2007, 06:57 PM
I get it, just couldn't quite understand what you were replacing, with what and where the identifier came from.

The second part was just a what if, nothing to do with what I need, I'm not after doing anything else with this function (at least I don't think so so far), I was just thinking about how the method could be extended if each field had a seperate identifier.

Many thanks again to all for the help and explanations and of course your time.
Martin Hodgson