0

First time using ajax. Have successfully progressed through a number of teething problems, so far with happy results. However now is a more confusing one specific to one particular input field nested within a table - there is a good reason for that.

First the html:

 <table id="speakersName" style="width: 100%; height: auto;">
  <tbody><tr class="activity_row">
   <td class="right" style="width: 190px;">Name of Speaker:</td>
   <td><input type="text" id="input_3_1" name="input_3_1" id="input_3_1" placeholder="Name of Speaker" value="<?=$input_3_1?>" required></td>
   <td><input type="button" name="button2" id="button2" value="&nbsp;+1&nbsp;" class="button" style="width: auto !important; margin: 5px;"></td>
  </tr>
  <tr>
   <td class="center" colspan="3"><input type="hidden" name="MAX_FILE_SIZE" value="5632000">
   <label for="file">Filename:</label> <input type="file" name="file" id="file">
   <input class="button" style="width: 70px; margin-top: 12px;" type="submit" name="submit" value="Upload"></td>
  </tr></tbody>
 </table>

We can fairly much ignore the section containing the file upload. I just wanted to be clear about the entire table structure.

The .js file that is included in the head contains this relevant code:

function doSend_3_1() {
    $.post('./post.4.ConSupAp.php?appID=' + (appID) + '&ident=input_3_1', $('#input_3_1').serialize());
}

$("document").ready(function() {
    $("#input_3_1").blur(doSend_3_1);
})

Which ajax's the data entered into the text input field over to this bit of php:

// include the funcky stuff
include './conf/Funcs.php';
include './conf/DBconfig.php';

// GET the constants
$appID = $_GET['appID'];
$ident = $_GET['ident'];

if(($ident) == "input_3_1") {
    $userInput = $_POST['input_3_1'];
    if(($userInput == "") || ($userInput == " ") || ($userInput == NULL)) { $userInput = NULL; }
    try {
        $stmt = $conn->prepare("UPDATE $database.app_ConSupAp SET `nameOfSpeakers` = :userinput, `lastModified` = :time WHERE `appID` = :appid");
        $stmt->bindParam(':userinput', $userInput, PDO::PARAM_STR, 128);
        $stmt->bindParam(':time', time(), PDO::PARAM_INT, 11);
        $stmt->bindParam(':appid', $appID, PDO::PARAM_INT, 11);
        $stmt->execute();
    } catch(PDOException $e) { catchMySQLerror($e->getMessage()); }
}

Which happily drops in the text that the user typed into the initial text input field, soon as they click out of it. This technique is being used across the form successfully.

True I don't yet have a success or error message coming back to the user facing page, but I'll get onto that after I've sorted this query out. One thing at a time, right? :)

Ok so now I'll show what makes the particular table input (the one above the file upload ) a little more complicated. In the head of the html facing page, I have also got the following code, within a tag:

$(window).load(function() {
    // trigger event when button is clicked
    $("#button2").click(function() {
        // add new row to table using addTableRow function
        addTableRow($(this),$("#speakersName"));
        // prevent button redirecting to new page
        return false;
    });

    // function to add a new row to a table by cloning the last row and incrementing the name and id values by 1 to make them unique
    function addTableRow(btn,table) {
        // clone the last row in the table
        var $tr = btn.closest($("tr")).clone(true);
        var num;    // Current unique field number
        // Clear the input fields (that are not the button)
        $tr.find(":not(:button)").val("");
        // get the name attribute for the input field
        $tr.find("input").attr("name", function() {
            // break the field name and its number into two parts
            var parts = this.id.match(/(\D+)(\d+)$/);
            num = parts[2]; //Get the number for later
            // create a unique name for the new field by incrementing the number for the previous field by 1
            return parts[1] + ++parts[2];
            // repeat for id attributes
        }).attr("id", function() {
            var parts = this.id.match(/(\D+)(\d+)$/);
            return parts[1] + ++parts[2];
        });
        btn.remove();
        num++;
        // append the new row to the table
        $(table).find(".activity_row:last").after($tr);
    };
});

And this function works wonderfully on it's own, it pops up new table rows for other input, in a nice unlimited manner. I've used a variation on this once before (for which it was originally written for) but that was not utilising ajax. This version works as expected for the initial input value, but I believe I need some sort of JS foreach function to arrange each of the additional new input text fields into one value, separated by a delimiter such as ^ so that I can break them up in the php and count them there with an explode and foreach.

jQuery is being used.

This is where I'm lost as I do not know how to achieve this. Help warmly received. :)

10
  • 2
    most of the description doesnt seem relevant to the final question. I suggest you put a minimal sample that shows the error you are having. Commented Feb 9, 2015 at 2:15
  • Can you re-create the question in jsbin.com? Commented Feb 9, 2015 at 2:57
  • @Zig Mandel thanks but then I get criticised for not putting in all relevant information. Just can't win with some people. The problem is pretty clear, helpful comments are welcome. Point of view on how I ask my questions - not really. It does actually have to work with everything else that is going on so if you're not aware of it, chances are answers will break it. Commented Feb 9, 2015 at 3:05
  • @Ruben Kazumov Hi Ruben, I've cobbled it together in jsbin (first time I've used that) here: jsbin.com/tezogimupa/1 Or with full panels here: jsbin.com/tezogimupa/1/edit?html,js,console,output Please note - I have had to move the JS that would have been in the html head to a separate panel in this demo. I've also removed the ajax element from the demo, however if you find this unhelpful and can suggest the coalition of data being handled somewhere else, I'll add it back in. Just let me know. Commented Feb 9, 2015 at 3:37
  • @Cassandra: Thank you! I believe jsbin will help you in future as sandbox. Do I understood right, you want to connect your "name of speaker" cloning code to the some magic php page which produces records in database? Commented Feb 9, 2015 at 4:35

2 Answers 2

1

I carefully study your job at http://jsfiddle.net/k3dj214k/2/ Now, I will try explain all the steps to fix errors:

The form page html:

<form id="ConSupAp_section_3" name="ConSupAp" action="./post.4.ConSupAp.php" method="post" enctype="multipart/form-data"><!-- edited by [email protected] -->
    <input type="hidden" name="token" value="3e57334833283e22579f77e3a1ade083edf637bd3f4ab8009bbf1f4d7f517fde">
    <input type="hidden" name="uID" value="1">
    <input type="hidden" name="uaID" value="1">
    <input type="hidden" name="appID" value="1">
    <input type="hidden" name="ident" value="input_3_1"><!-- edited by [email protected] --> 

    <h2 style="margin: 0 auto 20px;">Conference Support Application - Section 3</h2>

    <table id="speakersName" style="width: 100%; height: auto;">
        <tbody>
            <tr>
                <td colspan="3" style="padding: 30px;"><span class="h3">3.1</span>Please list names of guest speaker(s). Use the <strong>+1</strong> button to add addtional speakers.</td>
            </tr>
            <tr class="activity_row">
                <td class="right" style="width: 190px;vertical-align:top">Name of Speaker:</td>
                <td id="speakers_list"><!-- edited by [email protected] -->
                        <!--<input type="text" name="s" placeholder="Name of Speaker" value="" required>--><!-- edited by [email protected] -->
                </td>
                <td>
                    <input type="button" id="btnAddSpeaker" value="&nbsp;+1&nbsp;" class="button" style="width: auto !important; margin: 5px; vertical-align:bottom"><!-- edited by [email protected] -->
                </td>
            </tr>
        </tbody>
    </table>
</form>

I added one hidden input and delete text input. The form tag id should be renamed to ConSupAp_section_3.

The app_ConSupAp.js editions:

Kill doSend_3_1() function

// edited by [email protected]
//function doSend_3_1() {
//    $.post('./post.4.ConSupAp.php?appID=' + (appID) + '&ident=input_3_1', $('#input_3_1').serialize(), function(data) {
//        $("#errorText_3_1").html(data.errorText_3_1);
//        $("#resultImg_3_1").html(data.resultImg_3_1);
//    }, 'json');
//}

Kill whole module for names manipulation:

// edited by [email protected]    
//    // trigger event when button is clicked
//    $("#button2").click(function() {
//        // add new row to table using addTableRow function
//        addTableRow($(this), $("#speakersName"));
//        // prevent button redirecting to new page
//        return false;
//    });
//
//    // function to add a new row to a table by cloning the last row and incrementing the name and id values by 1 to make them unique
//    function addTableRow(btn, table) {
//        // clone the last row in the table
//        var $tr = btn.closest($("tr")).clone(true);
//        var num;  // Current unique field number
//        // Clear the input fields (that are not the button)
//        $tr.find(":not(:button)").val("");
//        // get the name attribute for the input field
//        $tr.find("input").attr("name", function() {
//            // break the field name and its number into two parts
//            var parts = this.id.match(/(\D+)(\d+)$/);
//            num = parts[2]; //Get the number for later
//            // create a unique name for the new field by incrementing the number for the previous field by 1
//            return parts[1] + ++parts[2];
//            // repeat for id attributes
//        }).attr("id", function() {
//            var parts = this.id.match(/(\D+)(\d+)$/);
//            return parts[1] + ++parts[2];
//        });
//        btn.remove();
//        num++;
//        // append the new row to the table
//        $(table).find(".activity_row:last").after($tr);
//    };

append the script page with:

// ---------------------------------------------------
// code addition for phase (3) "Speakers" of "Guests"
// edited by [email protected]
// ---------------------------------------------------
$(document).ready(function() {

    function addSpeakerNameField() {
        var $txtInput = $("<input type=\"text\" name=\"speakers[]\" placeholder=\"Name of Speaker\" value=\"\" required />");// extended notation to create input element, 'id' is not nesessary
        $("#speakers_list").append($txtInput);
        $txtInput.blur(function(){// change value event    
            $.post(
                    "post.4.ConSupAp.php", // your address of page is different, i made temporary php page to debug
                    $("#ConSupAp_section_3").serialize(),// get all form values
                    function(data) {
                        // actually, your html have no tags with id "errorText_3_1" and "resultImg_3_1"
                        $("#errorText_3_1").html(data.errorText_3_1);// not working
                        $("#resultImg_3_1").html(data.resultImg_3_1);// not working
                    }, 
                    'json');
        });// end of blur() 
    }

    addSpeakerNameField();// the first field

    $("#btnAddSpeaker").click(function() { // add one more field
        addSpeakerNameField();
    });


});
// end of edition by [email protected]

As you can see, the important editions are:

a) you should generate all the input text fields from code, because it will create the whole sending routine for all the fields in one place;

b) you should naming the text fields in html like name="speaker[]", because it will create array after serialization;

c) you should adding hidden inputs inside the form, if you want to send static values;

d) i recommend you delete all over-navigation:

enter image description here

enter image description here

and rename the tabs:

enter image description here

Finally, in post.4.ConSupAp.php you will reach the names:

$speakers = $_POST["speakers"];// returns array

And you should to add the header to the post.4.ConSupAp.php

header("Content-type: application/json");

if you expecting the data.errorText_3_1 and data.resultImg_3_1 output to the form.

Sign up to request clarification or add additional context in comments.

10 Comments

Thanks Ruben, however it does not quite do what the original does - the "+1" button for example should not be on every line, just the last line on the bottom. While your code looks way less complicated, I'm hesitant to re-engineer all the styling and layout my current version has. It's in a table with other elements. I'm not really happy about having to re-order my entire page for one form field.
Actually it would be quite cool if it could number out the speaker names, like line one: Name of Speaker 1 line two: Name of Speaker 2 etc
This is long story. You should use IDs from database to render existing values and send new values to the database as-is. Probably, before insertion new values to database you have to check unique values. By the way, jQuery UI have cool autofill element. It is possible you can use it there. If you do not insert values, but edit them, I can not see the point to have IDs of inputs. If you need index of the input field -- completely different thing.
A solution is still being awaited however. It can't be THAT hard to get JS to make an array out of each new form input field and post it with spacial delimiters for whatever picks it up at the other end to understand where the new fields were.
Sorry meant to comment on this before. Regarding the note above saying: For example, you should change: $userInput = $_POST['input_3_1']; to $userInput = $ident; // because it is GET call and $_POST is empty It's often the same but not always. The ident used to pick which php process the post goes to is often different. Just wasn't in the above examples.
|
0

This looks like a situation where you have a jquery event you would like to bind to a number of elements, but not all of those elements have been created when the event - blur() - is bound.

You can bind events to higher DOM element and use the following syntax to bind events to new elements as they are created:

$("body").on("blur", "input.some_class_name", do_send);

When do_send() is called, "this" will be defined as the element where the event was generated, so you can identify which element needs to be posted:

function do_send(e) {
    // "this" is the dom element
    var the_id = $(this).attr('id');
    var value = $(this).val();
    // post away!
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.