HTML5, CSS3, jQuery, JSON, Responsive Design...

Dynamic fields with jQuery

Michael Brown   June 23 2010 03:06:31 AM



The more that use the jQuery add-on library, along with its associated add-on libraries, the more I wonder how I've coped with JavasScript development up to now.

I'm working on an application form where I need to associate one or more addresses with a company group.  (The form's in Domino, but much of the code is applicable whatever your back end is.)  I want the user to enter each address on a single TextArea field, rather than on multiple "Address1", "Address2" lines.

One way to do this in Domino would be to create as many Address fields as I think the form might ever need, and then hide the ones that I don't need.  This is a clunky method though; if you create five such fields, you know it's only a matter of time before the users will want a sixth one!  I'm after something more dynamic and without limits.


Setting Up the First Field

It's easy enough to set up the first address field on the form.  Here's the HTML (angled brackets replaced with square ones to stop the code from parsing):

[div id="divAddressesEditMode"]
[textarea name="taAddress1" id="taAddress1" rows="5" cols="75"][/textarea]
[/div]


It's a simple TextArea field that's inside a div.  The div is important.

(Note how I've done this as raw HTML rather than use a Domino field. That's because to get a TextArea from a Domino field I'd have to define the field as a Rich Text, which is a pain that I can do without!  There's other reasons not to use a Domino field, and these should become clear as you read on.)


The Add Address Button

That's fine if the user has only one address to associate with this form.  What I want now is an Add Address button, in case the user wants to associate other addresses with that form.  Here's the JavaScript code for my Add Address button:

$("textarea#taAddress1").clone(true).val('').appendTo("div#divAddressesEditMode");


Using jQuery, it makes a clone of the my "taAddress1" TextArea tag, sets its value to an empty string and appends it to the "divAddressesEditMode" div.  That's one line of jQuery to do that, although it's actually a number of jQuery instructions chained on that one line.  You might find it easy to read like this:

$("textarea#taAddress1")
.clone(true)
.val('')
.appendTo("div#divAddressesEditMode");


This does exactly the same as the single-lined version listed above.

There's a problem with this initial code though: the clone() parameter, if left to its own devices, will clone everything from the source field, including its ID and NAME parameters if present.  (And you do need the ID parameter to be present to do anything with jQuery).  Here's the HTML that we'll end up with after clicking the Address button the first time:

[div id="divAddressesEditMode"]
[textarea name="taAddress1" id="taAddress1" rows="5" cols="75"][/textarea]
[textarea name="taAddress1" id="taAddress1" rows="5" cols="75"][/textarea][/div]


(Note: I'm using the the View->Generated Source Code function of FireFox's Web Developer Toolbar to see this generated HTML.  An ordinary View->Source only shows the HTML that was there when the page was first loaded.)

Yep, we've now got two "taAddress1" fields, and a heap of problems!  What we need is to manually set the ID and NAME parameters after cloning the first field and before we append it to the div.  Here's a modified version of the Add Address button that does just that:

$("textarea#taAddress1")
.clone(true)
.attr({'id': 'taAddress' + (taCount + 1), 'name': 'taAddress' + (taCount + 1)})
.val('').
appendTo("div#divAddressesEditMode");

The attr() function here incements the cloned field's ID and NAME values - "taAddress2", "taAddress3" etc - as determined by the taCount counter variable.  Now our generated source code looks a lot more useful:

[div id="divAddressesEditMode"]
[textarea name="taAddress1" id="taAddress1" rows="5" cols="75"][/textarea]
[textarea name="taAddress2" id="taAddress2" rows="5" cols="75"][/textarea][/div]


But how to set the value of taCount?  Well, we could keep that as a global variable, and keep track of how many times the user's clicked the button during that session, but that kind of dead reckoning code is severely error prone, IMHO.  It's also unnecessary, because jQuery gives us another single line way of tracking this:

var taCount = $("div#divAddressesEditMode > textarea").size();


This code gets a count of how many textarea elements are directly inside the "divAddressesEditMode" div. Problem solved.  Put the code together and we now have a working Add Address button:

var taCount = $("div#divAddressesEditMode > textarea").size();
$("textarea#taAddress1")
.clone(true)
.attr({'id': 'taAddress' + (taCount + 1), 'name': 'taAddress' + (taCount + 1)})
.val('').
appendTo("div#divAddressesEditMode");



One last, optional refinement.  As written, the code will append the TextArea fields directly after each other.  This means that they will go side by side, depending on how wide the div and/or the browser page is.  I want the TextArea fields to go underneath each other though, so I need to a append a BR tag before each TextArea that I'm appending cloning.  Here's the final version Add Address button code with this a new line to do this:

var taCount = $("div#divAddressesEditMode > textarea").size();
$('
').appendTo("div#divAddressesEditMode");
$("textarea#taAddress1")
.clone(true)
.attr({'id': 'taAddress' + (taCount + 1), 'name': 'taAddress' + (taCount + 1)})
.val('').
appendTo("div#divAddressesEditMode");


And here's the HTML that it generates:

[div id="divAddressesEditMode"]
[textarea name="taAddress1" id="taAddress1" rows="5" cols="75"][/textarea]
[br id="addressBR1"]
[textarea name="taAddress2" id="taAddress2" rows="5" cols="75"][/textarea]
[br id="addressBR2"]
[textarea name="taAddress3" id="taAddress3" rows="5" cols="75"][/textarea][/div]


The new jQuery line creates a new BR tag and appends it to the div before the TextArea is appended.  You'll note how the BR tag is given an ID paramater of its own - yes, even a humble BR tag can have an ID!!  We'll need to use this ID field when we want to get rid of the generated tags with our Delete Address button, speaking of which....



The Delete Address Button

We'll assume that our Delete Address button will remove the last TextArea that's in our div.  Here's the initial code to do that:

var taCount = $("div#divAddressesEditMode > textarea").size();
var $currentTextArea = $("textarea#taAddress" + taCount);
$currentTextArea.remove();
$('br#addressBR' + (taCount - 1)).remove();


We count the number of TextAreas on the div again; use that number to work out the ID of the highest placed one; and then delete it. Finally, we also delete the preceding BR tag, using its ID parameter.

We need to put some error checking into the above code though.  For one thing, there is nothing to stop the user clicking the Delete Address button to eventually remove the original TextArea too.  And when that happens, the Add Address button will no longer work because there will be nothing left for it to clone.  Also, we don't really want to be removing any TextAreas that have data in them, at least not without consulting the user first.  So, let's cut to the chase.  Here's the final version of the Delete Address button:

var taCount = $("div#divAddressesEditMode > textarea").size();

// Remove the last TextArea as long it's not also the *first* one.
if (taCount > 1) {
var $currentTextArea = $("textarea#taAddress" + taCount);
if($currentTextArea.val() !== '') {                // Check if there's a value in there, and warn the user if there is
    if(confirm('Address ' + taCount + ' currently contains an entry, which will be wiped if you proceed.  Is this okay?')) {
            $currentTextArea.remove();
            $('br#addressBR' + (taCount - 1)).remove();  //  need to get rid of the preceding
tag
    }
}
else {
    $currentTextArea.remove();
    $('br#addressBR' + (taCount - 1)).remove();    //  need to get rid of preceding
tag
}
}
else {
alert('Cannot remove first address field');
}



This version first checks if the count of TextAreas currently on the Div is equal to 1, and immediately exits if that's the case.

If we've more than one TextArea present, it checks to see if there's any data in the one with the highest ID number.  If there's data present in it, then the user gets a confirm() dialog that asks if they really want to delete the field.  If there's no data present, the field is deleted without any prompting.  In both cases, the preceding BR tag is also deleted.



Working Example









Problems - Saving the Data

There's a tiny, tiny problem with not using Notes fields: the data doesn't get saved!  No, Domino does not automatically create equivalent Notes fields for all the HTML fields that we've dynamically generated with our buttons.  If you save the web form, there will no "taAddress1" or "taAddress2" fields to be seen at the Notes back end; you've got nothing!  And you want to make some kind of issue out of that?

Okay, we're going to have to do some extra work to get the data saved, and also to get it the fields repopulated from that data when our web form is re-opened.  This is a topic in its own right, so it's going on a post of its own.  (Watch this space).









Comments

1cghgjh  04/07/2017 6:37:54 AM  Dynamic fields with jQuery

CBSE Results - CBSE 10th Result 2017 | CBSE 12th Result 2017 @ Cbseresults.nic.in - CBSE 10th Results 2017 CBSE 12th Results 2017 CBSE Board Results 2017 News , Updates

{ Link }

About