C5-8.2.1: multiple Ajax forms on page - how to prevent conflict?

Permalink
Hi,

I have a block with an ajax form. What's the way to prevent conflicts between multiple forms on the same page? Because obviously all input fields have the same IDs.

I could add a unique bUID number from the controller to each field name, but how in this case do I pass this bUID number to jQuery from php?

Thank you.

linuxoid
 
mnakalay replied on at Permalink Reply
mnakalay
since the forms are submitted through Ajax so JavaScript, the first thing that happens, as you suggested, is those fields are grabbed by the script.

To make sure the script grabs the right fields you don't need to give each a name distinct from those in other forms. All you need is to give the form itself or a form wrapper a distinct id. For instance:

<form ID="form1">
    <input type="text" name="name">
</form>
<form ID="form2">
    <input type="text" name="name">
</form>


One thing you have to be really careful with is to NOT give your inputs the same ID.

If your inputs are generated by Concrete5 form helper than it's probably happening. Typically, using the helper to generate a text input name "name" I'd do
$formhelper->text('name');

And that would generate
<input type="text" id="name" name="name">

So using the same exact code would generate several inputs with the same ID attribute which is wrong.

So my advice if you are sure the same fields are repeated on one page in different forms:
1- write the inputs yourself, don't use the form helper
2- give each form a distinct ID (you can use $bID)
3- make sure Ajax grabs the inputs by using the form's ID
linuxoid replied on at Permalink Reply
linuxoid
I generate inputs like this:
<input type="hidden" name="bUID" data-bUID="<?php echo $bUID; ?>">
<?php 
echo $form->text('name'.$bUID, $name, array ('maxlength'=>"50", 'required'=>"required", 'data-bUID'=>$bUID));
?>

But I can't seem to get jQuery to recognize which of the multiple forms on the page fires and property of which one needs to be changed.

I can get the form's bUID
var bUID = $('input[name=bUID]').attr("data-bUID");

but it doesn't work later for some reason
$("#name"+bUID).blur(function() {
    alert("#name"+bUID);
}

this doesn't pop up the alert.

what's wrong here?
mnakalay replied on at Permalink Reply
mnakalay
from your code i think it should work unless you have more than one form on the page with the same $bUID; How sure are you of their uniqueness?

Another thing, why do you give the first hidden field a data attribute, why not simply use its value to store $buID?

And finally, since that hidden field will give you the value of $buID, you probably don't need to add it as a data attribute to every other field? It seems redundant.

If you are sure that $buID is unique and that's not the issue, maybe you could post the whole code, it might be easier to troubleshoot it.
linuxoid replied on at Permalink Reply
linuxoid
Yes, the bUID is unique.

I'm not using the hidden input anymore. I just gave each of form inputs the bUID:
<div class="form-group">
            <?php 
                echo $form->label('name', $entry_name);
                echo $form->text('name'.$bUID, $name, array ('maxlength'=>"50", 'required'=>"required", 'data-buid'=>$bUID));
                echo '<div id="tip-name'.$bUID.'" class="tip-name tip">' . $entry_name_tip . '</div>';
                echo '<div id="error-name'.$bUID.'" class="error-name tip hidden">' . $error_name . '</div>';
            ?>
        </div>


Is there any way jQuery can read php variables? if it was inside the view.php, it would work like <?php echo $bUID; ?>. What about view.js? Is it possible?

I can add unique block IDs to each form and field from the controller. The ID changes every time a new block is placed. So, nothing can be accessed straight as for example $('#name123').

The problems are:
1. How can jQuery know which of the forms needs to be processed? how can it know which of the submit buttons was pressed if the ID number is unknown prior to that?
2. Even if I find out which form was tried to be submitted, how can a unique block ID generated by the controller be processed by jQuery? That is, if I, say, have 3 forms on a page, how can jQuery know which input element fired a signal (e.g. blur)?
mnakalay replied on at Permalink Reply
mnakalay
There is no "direct" way for PHP to share a variable with JS and the way you are doing it is OK. To answer your questions:
1. How can jQuery know which of the forms needs to be processed? how can it know which of the submit buttons was pressed if the ID number is unknown prior to that?
If you gave all your forms the same class name, for instance 'myform' and then caught the submit event
$('.myform').on('submit', function(evt) {
    evt.preventDefault();
    var buid = $(this).find('input[name=bUID]').attr('data-bUID');
});

Anytime you would submit a form, jquery would know which one it is and then using .find() would look for that hidden field inside the submitted form. Jquery is smart enough t do that.

2. Even if I find out which form was tried to be submitted, how can a unique block ID generated by the controller be processed by jQuery? That is, if I, say, have 3 forms on a page, how can jQuery know which input element fired a signal (e.g. blur)?
Again, if you do your processing on the form submit, jquery just knows which form it is, even if you do it using a class name that all forms share because jquery also keeps track of the index of the form so he knows if it's form 1 or form 2.

Then, when processing inside the submit event function, make sure you reference all your fields in relation to the form submitted. Using the .find() function like I did allows you to select stuff in that form.
linuxoid replied on at Permalink Reply
linuxoid
ok, I've generally got it working, except for on thing.

Here'e the js:
$('form').submit(function(e){
    bUID = $(this).find('input[name=bUID]').data("buid");
    e.preventDefault();
    submitForm(bUID);
});
function submitForm(bUID) {
    var post_data = {
    'name': $('#name'+bUID).val(),
    'email': $('#email'+bUID).val(),
    'message': $('#message'+bUID).val(),
    'code': $('#code'+bUID).val(),
    'buid': bUID,
    };
    $.post($('#contact_form'+bUID).attr('action'), post_data, function(response){
        alert(response);

That alert shows all data entered in the form fields. But the 'v' are all NULL, the array returned is empty. And it's empty, because the $_POST[] is actually empty! I'm totally puzzled here. How come the alert returns all data but php receives an empty post array?

Here's the controller:
public function action_submit($token = false, $bID = false) 
{
    $this->form_errors = array();
    array_push($this->form_errors, "error");
    array_push($this->form_errors, $_POST['name']);
    array_push($this->form_errors, $_POST['email']);
    array_push($this->form_errors, $_POST['message']);
    array_push($this->form_errors, $_POST['code']);
    array_push($this->form_errors, $_POST['buid']);
    echo Core::make('helper/json')->encode($this->form_errors, JSON_UNESCAPED_UNICODE);
    exit;
}
linuxoid replied on at Permalink Reply
linuxoid
The alert shows "error,gggg,gggg@gmail.commm,gggggggggggggggggggggg,gggg,171‌", but php retruns null objects: "["error",null,null,null,null,null]" - $_POST is empty!

What's wrong here? Why doesn't the form get posted?"
mnakalay replied on at Permalink Reply
mnakalay
try this
response = $.parseJSON(response);
$.each(response, function(i, v){
    $('#error_list'+bUID).append('<li>' + v + '</li>');
});
linuxoid replied on at Permalink Reply
linuxoid
I tried that. Doesn't make any difference.
linuxoid replied on at Permalink Reply
linuxoid
[SOLVED]

That was THE most difficult bug ever - all due to copy/paste stuff up.

This:
$('#errors'+bUID).append('<ul id="error_list"'+bUID+'></ul>');

should have been that:
$('#errors'+bUID).append('<ul id="error_list'+bUID+'"></ul>');

The damn '+bUID+' was pasted AFTER the " , not BEFORE!

Of course it couldn't append anything to it... 2 weeks...2 WEEKS wasted!!! )))
mnakalay replied on at Permalink Reply
mnakalay
when you said the alert was showing the correct data, it really surprised me that your loop was not getting it, it just didn't make sense. I have to confess I didn't see that typo at all.

Good job :)
linuxoid replied on at Permalink Reply
linuxoid
I literally had to go through every single line with alerts and see what happens because there were no errors. Until I got to a line before - it showed data, the next line - nothing. I looked at the code for hours and couldn't figure out what was wrong... until I checked every letter in the line. What made it worse was that the div the code was appending text to was initially hidden. Adding text to a misspelt hidden div did the bad trick.

I notice a pattern - if it seems to be insane that something should work but it doesn't, stop looking for logic errors and start looking for typos. Although to be honest, to notice a ' was pretty tough )))