Reputation: 16781
Wasn't too sure exactly how to title this question, but here's the issue that I'm having:
I need to be able to submit an array or a linked list of data via an HTML form. Since this array could be of variable length I have two options:
I would like to pursue the second option, although I felt that since this particular project will have many such "array inputs", I should create a jQuery plugin so that I can more easily implement this feature into future forms.
As I work on this I've also been adding functionality for multiple input types, selects, select multiples (via a delimiter), and linked inputs (for instance - having two text inputs for "first name" and "last name" which must be submitted as a pair)
I've got a basic layout for how I think it should work:
(function($) { // Self-calling anonymous function to avoid namespace collisions or compatibility issues
$.fn.dynamicList = function( options ) { // jQuery plugin definition
/* Set up default options here */
defaults = {
option1: "value1",
option2: "value2"
};
var options = $.extend( defaults, options );
/* Step 1. Add a jQuery-UI plus icon to the DOM */
/* Note: I have an option to specify where this icon should go in the DOM. It defaults right after the last input */
plus = $("<span>").addClass( 'jquery-ui-classes' );
this.last().after( plus );
/* Step 2. Add a table to the DOM for displaying added values */
/* Note: I have an option to specify where this table should go in the DOM. It defaults right after the plus icon */
plus.after( table );
/* Step 3. Add a handler for our plus button */
this.delegate( plus, 'click', function() {
/* Read the inputs, add their values to the table, empty the inputs */
/* Also, the last column of the table contains a minus button */
/* This is where I THOUGHT my first issue was.... */
minus = $("<span>").addClass( 'jquery-ui-classes' );
table.append( row ).append( minus );
});
/* Step 4. Add a handler for our minus button */
/* Since the minus is created within the plus handler we can't use "minus" here */
/* Instead I use a class name to locate it */
this.delegate( '.jquery-ui-minus-class', 'click', function() {
/* Remove the relevant row from the table */
$(this).parents('tr').remove();
});
/* Step 5. Add our submit handler */
this.parents('form').first().submit(function() {
/* We want all of the data in the table to be submitted with the form */
/* Therefore we create an input per cell and name it accordingly */
/* The name is just the name of the original input plus "[]" to make it an array */
/* This is where I thought my second problem was */
});
return this; // Maintain chain-ability
};
)(jQuery);
This code is called on the inputs that you wish to replace with the dynamic list. If you have multiple inputs selected then they will become a list of linked data (such as the first and last name example). An example of how to call this would be the following:
<html>
<head>
<!-- Include jQuery and my dynamic list library here -->
<script>
$('#fn,#ln').dynamicList();
</script>
</head>
<body>
<form>
<input id="fn" name="first_name" />
<input id="ln" name="last_name" />
</form>
</body>
</html>
This will create a dynamic list in which you can type in a first and last name then click the plus button (now to the right of or under the last name input based on your CSS rules) and it will add a row to a table containing the first name in one column, last name in another, and a minus button in the last. You can continue to type names and hit this plus button and the table is extended downward infinitely. Upon submitting the form (via a submit button or any other method) the table cells should become inputs.
I thought that the first problem I would encounter was within the plus handler. Since I am inside a function called by clicking on the plus button, the "this" keyword now refers to the plus icon's DOM element. I can't grab the value of the inputs to add them to the table. One proposed solution I had was to create a global variable which is a copy of the object on which the dynamic list function was called like so:
.
.
.
/* Step 2. Add a table to the DOM for displaying added values */
/* Note: I have an option to specify where this table should go in the DOM. It defaults right after the plus icon */
plus.after( table );
var dynamicList = this;
/* Step 3. Add a handler for our plus button */
this.delegate( plus, 'click', function() {
/* Read the inputs, add their values to the table, empty the inputs */
/* Also, the last column of the table contains a minus button */
/* This is where I THOUGHT my first issue was.... */
for( i = 0; i < dynamicList.length; i++ ) {
// dynamicList.get(i) will give us each input in order
}
minus = $("<span>").addClass( 'jquery-ui-classes' );
table.append( row ).append( minus );
});
.
.
.
My concern with this is that if I instantiate multiple dynamic lists they will override one another. Is this true? If so, how do I overcome this?
My second issue that I thought I'd run into was similar. Upon submitting the form I need to find the table to get the values to submit AND find the inputs in order to get the names under which they should be submitted.
However upon playing around with the code to try and fix it myself, I ran into an issue much sooner than expected.
Uncaught TypeError: Object [object Object] has no method 'replace'
jquery-1.6.2.min.js:16
f.each.f.fn.(anonymous function) jquery-1.6.2.min.js:17
f.fn.extend.delegate jquery-1.6.2.min.js:17
$.fn.dynamicList jquery.dynamicList.js:76
(anonymous function) test.php:16
f.extend._Deferred.e.resolveWith jquery-1.6.2.min.js:16
e.extend.ready jquery-1.6.2.min.js:16
c.addEventListener.B jquery-1.6.2.min.js:16
Lookin at line 76 of my dynamic list code, it's the following line:
this.delegate( plus, 'click', function() {
Can I not call .delegate()
since "this" refers to more than one jQuery object? Or can I not call .delegate()
because "this" isn't a parent element of the plus icon? And once this issue is resolved, how do I solve my other two issues?
EDIT -
I modified the title and I found a solution to my problem on line 76. I'm not sure exactly how .delegate()
works, because at first I tried:
this.first().parent().delegate( plus, 'click', function() {
and it was still upset at me. Then I realized that I could just use the .click()
function instead (I kinda goofed)
plus.click( function() {
I used similar logic when creating the minus button per row. I added minus.click()
after creating the minus button and before inserting it into the DOM, saving me the trouble of using .delegate()
anywhere in the code.
Still not sure how to get the values from the input fields within the plus handler since the "this" keyword has been overwritten at this point to refer to the plus button, not my input fields.
Upvotes: 4
Views: 604
Reputation: 24617
I modified the title and I found a solution to my problem on line 76. I'm not sure exactly how .delegate() works, because at first I tried:
this.first().parent().delegate( plus, 'click', function() {
and it was still upset at me. Then I realized that I could just use the .click() function instead (I kinda goofed)
plus.click( function() {
I used similar logic when creating the minus button per row. I added minus.click() after creating the minus button and before inserting it into the DOM, saving me the trouble of using .delegate() anywhere in the code.
Upvotes: 1