I'm doing what should be a pretty simple test of D7 AJAX and I simply can't get it working.
I've created a content type called "Test" and in a hook_form_alter have done the following:

case 'test_node_form':
        
     $test = isset($form_state['input']['field_test_textfield']) ? $form_state['input']['field_test_textfield'] . "ASDFASDF" : '';
                
    $form['field_test_textfield'] = array(
                '#type' => 'textfield',
                '#default_value' => $test,
                '#prefix' => '<div id="test_ajax_field">',
            	'#suffix' => '</div>',
        );
        
        $form['field_test_submit'] = array(
                '#type' => 'button',
                '#value' => 'Go!',
                '#ajax' => array(
                        'callback' => 'test_ajax_test_callback',
                        'wrapper' => 'test_ajax_field',
                        ),   
                );

And the callback...

function test_ajax_test_callback($form, $form_state)
{
        return $form['field_test_textfield'];
}

break;

Like I said, a pretty simple test. It should simply take whatever is entered in the textfield, append some dummy text to it, and on form rebuild set that as the value of the textfield.
The problem is that it doesn't work. I know the callback happens and the form gets rebuilt, and even that the proper data is put into the $test variable. However, after the rebuild the textfield has not been populated with new data. My only clue is that in the code, an empty div gets inserted into the wrapper div, as seen below:

Before:

<div id="test_ajax_field">
<div class="form-item form-type-textfield form-item-field-test-textfield">
 <input type="text" id="edit-field-test-textfield" name="field_test_textfield" value="NOPE" size="60" maxlength="128" class="form-text" />
</div>
</div>

After:

<div id="test_ajax_field">
<div></div>
<div class="form-item form-type-textfield form-item-field-test-textfield">
 <input type="text" id="edit-field-test-textfield" name="field_test_textfield" value="NOPE" size="60" maxlength="128" class="form-text" />
</div>
</div>

Any idea why this is happening? Like I said, this should be a pretty simple test of AJAX and from the examples I've seen I think I'm doing everything right, so I'm really not sure what the problem is.
Any help would be much appreciated.

Comments

Web Assistant’s picture

Someone may have a better answer but I think with submit driven AJAX you need to put all your logic in the callback. So it would be:

<?php
..
$form['field_test_textfield'] = array(
  '#type' => 'textfield',
  '#default_value' => '',
  '#prefix' => '<div id="test_ajax_field">',
  '#suffix' => '</div>',
);
$form['field_test_submit'] = array(
  '#type' => 'button',
  '#value' => 'Go!',
  '#ajax' => array(
    'callback' => 'test_ajax_test_callback',
    'wrapper' => 'test_ajax_field',
  ),   
);
..
?>

and the callback would be:

<?php
function test_ajax_test_callback($form, $form_state) {
  $test = isset($form_state['values']['field_test_textfield']) ? $form_state['values']['field_test_textfield'] . "ASDFASDF" : '';
  $form['field_test_textfield']['#value'] = $test;

  return $form['field_test_textfield'];
}
?>
Jaypan’s picture

That is actually the wrong way to do it - the form has already been cached at that point and changing values will cause the form to fail upon submission, as the changes made in the callback function will not be part of the cache.

Looking at the original code, I'm wondering if this:

$test = isset($form_state['input']['field_test_textfield']) ? $form_state['input']['field_test_textfield'] . "ASDFASDF" : '';

Isn't supposed to be this:

$test = isset($form_state['values']['field_test_textfield']) ? $form_state['values']['field_test_textfield'] . "ASDFASDF" : '';
Web Assistant’s picture

Jay, I've just tested this and it works just fine:

<?php
function code_form_alter(&$form, &$form_state, $form_id) {
  
  if ($form_id == 'test_node_form') {
    $form['field_test_textfield'] = array(
      '#type' => 'textfield',
      '#default_value' => '',
      '#prefix' => '<div id="test_ajax_field">',
      '#suffix' => '</div>',
      '#weight' => -10,
    );
    $form['field_test_submit'] = array(
      '#type' => 'button',
      '#ajax' => array(
        'callback' => 'ajax_example_submit_driven_callbacktest',
        'wrapper' => 'test_ajax_field',
        'name' => 'submit1',
      ),
      '#value' => t('Update'),
      '#weight' => -10,
      '#limit_validation_errors' => array(),
    );
    
    $form['#submit'][] = 'test_submit_handler';
  }
}

function ajax_example_submit_driven_callbacktest($form, $form_state) {
  $test = isset($form_state['values']['field_test_textfield']) ? $form_state['values']['field_test_textfield'] . "ASDFASDF" : '';
  $form['field_test_textfield']['#value'] = $test;
  
  return $form['field_test_textfield'];
}

function test_submit_handler($form, &$form_state) {
  // value does show with appended "ASDFASDF" text
  dsm($form_state['values']['field_test_textfield']);
}
?>

It's just essentially appending text to a field, not actually changing form structure/elements, so I can't see there being a problem.

Jaypan’s picture

Yes, you can append text, and you're right, there won't be a problem. Until the user then tries to extrapolate that since they were able to add text, they can also add form elements, then they are left wondering why They are getting these error messages that aren't very clear and can't understand where they came from.

There are many things you can do in Drupal that will 'work'. But that doesn't mean they are a good way to do things. Anyone can hack core - but that's not a good thing. People can write database retrieval functions and drop them into templates to get data, and it will work, but it's still not a good thing.

Anyone can change the form definition in the ajax callback - but that's not a good thing. Best practice is to put all changes to the form in the form definition.

Web Assistant’s picture

With respect, gwalliman hasn't asked to add form elements, if they had I would not have shown the solution I did, that is a completely different functionality. Also, this is not akin to core hacking, or the other things you mention.

If gwalliman shouldn't do it the way I offered, could you show an example of how it should be done? Perhaps your example will change my mind, and I'm really interested to see the way you'd do it. I think if you are saying something is wrong, you need to offer up the right way of doing it. :)

Jaypan’s picture

Sure, there is an example right here: http://api.drupal.org/api/drupal/includes!ajax.inc/group/ajax/7

As you can see, all the logic is done in the form definition, and the ajax callback function simply returns the form element required. There are many, many tutorials on this, I can link a few more if you'd like, or if you'd really like, I'd be happy to write one more, but it won't be much different than the one in that link.

Web Assistant’s picture

No Jay, an example of how you would solve the functionality gwalliman is looking for. I find it interesting that you haven't yet.

You say putting logic in the callback is akin to core hacking. You need to look through the AJAX example module for submit driven AJAX http://api.drupal.org/api/examples/ajax_example!ajax_example.module/function/ajax_example_submit_driven_callback/7, so how you can you say putting logic in the callback is like core hacking?

Please, show some code of your working for the functionality gwalliman is asking for, then that would definitively prove me wrong.

Jaypan’s picture

You'll have to point me to where I said it was akin to hacking core. And I'm not going to write the function for him for the sake of proving a point. But if I were to do it, I would alter the form definition to deal with the form state, both as I've stated and as the example I linked to showed, and then I would return the form element in the callback function. The example I linked to shows an almost identical case to the example you gave, adding some text to a form element on #ajax submission, and it did so in the form definition, just like I've been saying.

As I've mentioned, what you showed will work, and it will work fine. But it's not the way it's supposed to be done.

Look at it a different way - if I hire a new developer in my company, then give them some code I've written to fix, they are going to go looking in certain places to find where it's happening. If it's something being changed by #ajax, then they have a reasonable expectation that they should be able to look in the form definition to find out what his happening, how it is happening, and under what circumstances it happens. If the changes are made in the callback function, they are going to have to go looking in there to find the solution. Now, most programmers who know their way around Drupal will probably figure this out quick enough, but they shouldn't have to, as the changes are meant to be kept in the form definition.

Web Assistant’s picture

There are many things you can do in Drupal that will 'work'. But that doesn't mean they are a good way to do things. Anyone can hack core - but that's not a good thing. People can write database retrieval functions and drop them into templates to get data, and it will work, but it's still not a good thing.

I really think if your hiring people who can't work out one tiny thing is being changed in the callback rather than the form, they really shouldn't be working for you in the first place.

Thank you:

As I've mentioned, what you showed will work, and it will work fine.

I do actually agree with you, but not to show an example after you say something is wrong just makes things more confusing for people in my opinion. You say you won't do this to prove a point, well it's then a point not proven.... right?

If I say I'm wrong, and don't understand how this would work, will you then show some code of how you would do this? You don't need to write the function, merely alter the code that is already there, it will only take you a second.

Furthermore, should best working practices be adhered to all the time, or just when it's convenient to do so? http://drupal.org/node/1427318#comment-5554084

Jaypan’s picture

I really think if your hiring people who can't work out one tiny thing is being changed in the callback rather than the form, they really shouldn't be working for you in the first place.

While you're not incorrect about the abilities of the person, you have missed the point altogether. The whole point of having guidelines for code is for consistency's sake. The more people are consistent, the easier it is for different people to work with a project. As I mentioned earlier, I could put code that directly pulls data from a database directly into a template file, and it would work fine. I would expect my developers to be able to find that if they had to. But that said, if it was put into the template.php file with the output creating a variable, and the variable being used in the .tpl.php file, it would be quicker/easier for my developer to find, because it would be done within the guidelines of Drupal. On that note, if I bring in a brand new developer on the project, they will be expecting to find the code in template.php, because that's how it's done with Drupal. If you don't want to follow the same guidelines as everyone else, you don't have to - that's the beauty of open source. But, each time you make a decision to do something in a different manner than best practices, you make it more difficult for any other developer to step in and start working on a project.

but not to show an example after you say something is wrong just makes things more confusing for people in my opinion.

I gave an example in the link I pointed to. The link does exactly what I say, in a very simple manner so as to make it easy to understand.

If you really want me to post one, here you go:

function myform_form($form, &$form_state)
{
  $form['select'] = array
  (
    '#type' => 'select',
    '#title' => t('Please make a selection'),
    '#options' => array(1, 2),
    '#ajax' => array
    (
      'callback' => 'my_ajax_callback',
      'wrapper' => 'my_ajax_wrapper',
    ),
  );
  $form['display'] = array
  (
    '#prefix' => '<div id="my_ajax_wrapper">',
    '#suffix' => '</div>',
  );
  if(!isset($form_state['values']['select']))
  {
    $form['display']['#markup'] = '<p>' . t('You have not made a selection') . '</p>';
  }
  else
  {
    $form['display']['#markup'] = '<p>' . t('You selected !number', array('!number' => $form_state['values']['select'] + 1)) . '</p>';
  }
  return $form;
}

function my_ajax_callback(&$form, &$form_state)
{
  return $form['display'];
}

As you can see, I've added some #ajax to the 'select' element. Depending on whether or not a selection has been made, the 'display' element shows that no selection has been made, or displays the selection that has been made. As you can see, all the changes are made within the form definition.

Web Assistant’s picture

Thank you, I'm going to look at your example and learn something.

Though, I do find your words a bit rich regarding Drupal best practices. http://drupal.org/node/1427318#comment-5554084.

Also, if you adhere to Drupal standards to the letter, can I ask why you put your openening bracket on a new line? They don't say to do this in the Drupal Coding Standards page. Why is it OK for you not to follow the same guidelines as everyone else?

http://en.wikipedia.org/wiki/Hypocrisy

Web Assistant’s picture

Oh dear, your example has nothing to do with what gwalliman was asking for. I think the reason you are avoiding showing how you would code the function gwalliman is asking for is that you don't know how to. Am I wrong?

Jaypan’s picture

Oh dear, your example has nothing to do with what gwalliman was asking for. I think the reason you are avoiding showing how you would code the function gwalliman is asking for is that you don't know how to. Am I wrong?

It would depend - by 'don't know how to'. Do you mean 'don't have the ability', or do you mean 'haven't sat down and figured it out yet'? If you mean the former, you are incorrect. Most D7 forms I build have #ajax functionality in them, and most D6 forms I built have #AHAH functionality built into them, and #AHAH was a lot harder than #AJAX. It's an API of Drupal than I am very comfortable in, and have extensive experience with. Look at the example I gave above - I typed that off my head. If you put that into a module, it may have some syntax errors, but if after fixing the syntax errors it didn't work, I'd be extremely surprised. Now if by 'don't know how to' you meant that I haven't sat down and solved gwalliman's problem, yes, you are entirely correct, I haven't sat down and figured it out.

Now, it seems to me that you have been unimpressed that I made a comment that what you had done is not a best practice, and have been trying to pick at me ever since to cover that fact. Am I wrong?

Web Assistant’s picture

Exactly, you go on about adhering to best Drupal practices. Yet you go around showing new Drupal coders to wrongly use brackets on a new line, and also telling them that they should just overwrite Drupal core files rather than deleting them first because it's quicker to do so. So why then, should I listen to you lecture me about best Drupal practices, for something that isn't even near as bad as what you do and say.

It's not even bad practice to put logic in the callback, it's only *recommended* that you do so in most cases.

Also, the reason why you can't show a working example for the functionality that was asked for is because you don't know how to, otherwise you would have done this earlier and this discussion would have ended a long time ago.

Jaypan’s picture

why then, should I listen to you lecture me about best Drupal practices

Why, there is absolutely no reason at all you should listen to me. I'm some random guy on the Drupal forums. I am not one of the core developers, I have never completed any courses in Drupal work, and my opinion on Drupal holds no more weight than any other person here. I am not paid for helping here, and I have no endorsement whatsoever from the people behind Drupal.

People can choose to listen to what I say, and follow what I do if they want, and can completely ignore me and call me a hack if that is what they prefer. There is absolutely nothing stopping them.

You obviously seem to have taken the stance that I don't know what I'm talking about, so call me a hack! Ignore my posts! Do whatever you want, I cannot stop you. So if a user decides that I only post crap and that I'm not worth listening to because I don't know what I'm talking about, then they completely have that right, and I fully respect that.

But, generally I have a pretty solid track record with my responses, and I have managed to help people with issues they have been having on more than a few occasions. So I'll continue to give them assistance, and if they decide they want to take it, then I'm happy to help, and if they don't, then I wish them luck.

Jaypan’s picture

I haven't claimed I follow Drupal standards to the letter, and in fact if you searched around you could find a thread in which I staunchly defended my choice to not follow Drupal coding standards.

What I actually said was the following:

If you don't want to follow the same guidelines as everyone else, you don't have to - that's the beauty of open source. But, each time you make a decision to do something in a different manner than best practices, you make it more difficult for any other developer to step in and start working on a project.

The point is that I carefully decide when I do and don't want to follow Drupal standards. For me, code format is an entirely arbitrary decision, and rather than following the Drupal standards, I follow my own. But as I am completely aware that in doing so, it makes it more difficult for other developers to step in. So I am consistent with the standards I do follow, so that when I do bring other developers into my projects, I can tell them exactly what to expect from my code. While there is a slight adjustment the first time we work together, it's only that first time.

Web Assistant’s picture

So you basically give me some long lecture about following Drupal standards, when you don't fully adhere to them yourself. Now your saying you *carefully* decide when to follow Drupal standards or not. With respect, and you do know much more about Drupal than I do, but I find your words VERY hypocritical.

You still haven't shown me or gwalliman how you would code the functionality gwalliman is asking for, not just some random AJAX example for a select field that you copied from the example module.

Jaypan’s picture

So you basically give me some long lecture about following Drupal standards, when you don't fully adhere to them yourself.

It wasn't a lecture. You posted some code that was not using best practices. I pointed it out. You have interrogated me since, so I've been answering your questions. I even made the point that 'if you don't want to follow standards, you don't have to', and you showed a place where I have decided that I didn't want to follow the standards - which is great, since I don't have to! You are looking for a contradiction that doesn't exist. I didn't make the point that you weren't allowed to do what you did (altering the form data in the ajax callback), I made the point that by doing so, you could lead people to situations where they have errors coming up and it confuses them. If you really want to make the comparison between this and my decision to not follow the Drupal code formatting standards, then please show how my usage of my own code formatting could lead to a situation where people have errors coming up and are not sure why. If you can do so, I will definitely have to re-consider whether or not I want to make the decision to format my code that way.

not just some random AJAX example for a select field that you copied from the example module.

The code I wrote was untested and written off the top of my head. If you'd like, I can copy and paste a real example from one of the projects I have on the go. But it's going to show the exact same thing - all changes are processed in the form definition, and the callback function only returns the form element in question.

Web Assistant’s picture

The code you wrote, is near enough exactly the same function as the ajax example module line 213, and has nothing to do with the functionality that was originally asked for.

You go around showing new Drupal coders to wrongly use brackets on a new line, and also telling them that they should just overwrite Drupal core files rather than deleting them first because it's quicker to do so. So why then, should I listen to you lecture me about best Drupal practices, for something that isn't even near as bad as what you do and say.

It's not even bad practice to put logic in the callback, it's only *recommended* that you do so in most cases.

Also, the reason why you can't show a working example for the functionality that was asked for is because you don't know how to, otherwise you would have done this earlier and this discussion would have ended a long time ago.

I should also add, EVERY code you post isn't using best Drupal practice and just because this won't throw up an error, that doesn't make this OK. Also, having stay files all over the place can be confusing for people, and that tells me a little about the quality of your work if you're defending this practice.

Jaypan’s picture

The code you wrote, is near enough exactly the same function as the ajax example module line 213

And if you'll recall, I said this:

There are many, many tutorials on this, I can link a few more if you'd like, or if you'd really like, I'd be happy to write one more, but it won't be much different than the one in that link.

So even after saying that it would be much the same, then coding an example that was much the same, you've felt the need to drive home some point that it was... much the same?

and has nothing to do with the functionality that was originally asked for.

If you'll remember, our whole thread of conversation sprouted from my observation that you had not followed best practices. It never had anything to do with the original functionality.

Web Assistant’s picture

I find it unbelievable that you can't show the poster how you would do this using best working practices, after giving a big lecture about it. Again, I ask why won't you do this?

Jaypan’s picture

I remember one time, having a discussion with my friend. I was annoyed about how someone else we knew had been fighting so hard to prove a point even when they were totally in the wrong. I said that I had a hard time understanding how someone could argue a point even when they knew they were wrong, and my friend said something to me that stuck with me from then until now. "When I know I'm wrong about something, I'll argue even harder, because I hate to be pointed out how I'm wrong."

You seem hellbent on turning this personal. About how I don't have the skills to code something. Challenging me. Trying to call me out and expecting me to get caught up in it and sit down and prove that I know what I'm talking about.

I'm not going to rise to that. I don't know you, and I have nothing to prove to you. I have a link to my portfolio in my signature, with my real name, an explanation of who I am, and examples of code I have written. That is all the proof/backing I'm going to give. If it's not enough for you, and you want to continue thinking that I don't have the ability to solve gwilliman's code, then you go right on ahead and keep thinking that. I have no issue with it whatsoever.

But it sure seems to me that you've been arguing a point you know is wrong, same as my friend described to me all those years ago. And if you disagree, then show me where you have actually shown that I was wrong with my initial comments about you not using bad practices. You are avoiding the issue and trying to make it personal, which says to me you are only trying to draw attention away from the issue at hand - that I was right in the first place. Or don't show me - it really doesn't matter. I'm not the one with the bone to grind here, you are.

Web Assistant’s picture

I also had a friend, he told me... it takes two to argue!

I'm liking how you are changing this discussion to make me sound like I have a personal vendetta against you. Please point out how I have made this personal?

Your argument is that one should *always* put logic in a callback, yet this isn't the case in some rare cases like submit driven ajax. I only pointed out the fact that I find it ironic you are telling me and others to always stick to best Drupal practices, when you don't actually do so yourself. Furthermore, in other posts you actually tell people to go against the best Drupal practices.

There is a very simple fix here, show this functionality using best practices, then everyone walks away happy. I said previously, I'm open to the fact I'm wrong, but show the right code!! If not for me then for the original poster.

Jaypan’s picture

It most certainly does come off like you have a personal vendetta against me. After all, you have publicly made the claim that I do not know how to do this, which would imply that I'm lying, seeing as I have stated that I do in fact have the skills to be able to solve this problem. If calling someone a liar is not getting personal, then we have a very big cultural gap in our ways of thinking. Where I'm from calling someone a liar without proof, is a direct personal attack. Maybe things are different in your country.

Now as to solving the problem, thats always been your thing. I have never made the offer, and I don't intend to do it to prove some point to you. If you think it should be solved, then by all means feel free, I am not stopping you.

Web Assistant’s picture

I have suggested that you might not know how to do what the poster is asking, using best practices, because you seem so adamant not to show any code. Yet your happy to take a large amount of time to lecture me and break into stories about your friend and hacking core code. You also have the time to post code that has nothing to do with the functionality the poster is asking for.

If you felt offended by that, I apologise. But if someone tells me I'm wrong, I'd like to see the code of the right way of doing it. And by that I mean the right code showing the functionality that was asked for by the original poster. This seems to me a very simple request.

I agree, logic should not be placed in the callback, but for a textfield and button, how else would you update the text. I know how to do this using custom AJAX, but not through AJAX in the form API, other than the original example I showed. So why not help further?

Jaypan’s picture

Are you honestly not able to extrapolate from my example how changes are made in the form definition, and then returned by the Ajax callback? I may have been making unjustified assumptions - I assumed you could see from my example how you would use the same method as I showed.

If you really are unable to see the connection, I will sit down and try to solve the problem, showing how it relates to my example. I am happy to help people with conceptual difficulties - its one of the reasons i come here. But if you can see the connection, and are just asking me to do it out of some expectation I should prove myself, then I'm not going to bother, as I have nothing to prove.

So are you honestly not able to see the connection between my example and the problem at hand?

Web Assistant’s picture

I'll say no, I can't make the connection. But remember Jay, this is not for me, this is for poor old gwalliman who by now doesn't know what the heck to do. I'd be very grateful if you could show the code, that is all I want and I'll go away after that. :)

Jaypan’s picture

Gwalliman has already stated clearly that he found an answer. So it's not for him - his problem is settled. If you would like me to write it for you, then just ask, and I'll do so.

Web Assistant’s picture

Yes please. Let the world know, I don't know how to do this, my original example was wrong. Please help me!!

OK, all joking aside, can you show me how to do this? Yes, the example I did does work, and yes I agree it may not be the best way to it. Can you show me the best way?

I could use a $_SESSION variable in the callback and then update in the form definition, but even that feels wrong. Can you manipulate the $form_state variable in the callback?

Note: he said he found the answer using the code I suggested, so we don't want him using that if it's wrong!

@gwalliman please do not use my code in your module. Jay is coming up with the right way to do this.

Jaypan’s picture

Ok, here is how it is set up. I had to make the assumption that the original function that the gwilliman showed was part of hook_form_alter. The case statement that he made suggested this, as it appeared that he was trying to target the node form for a note type called 'test'. So I created a 'test' content type, and used the following in a node named 'ajaxe':

function ajaxe_form_alter(&$form, &$form_state, $form_id)
{
	// First test to ensure that the code is only executed on
	// the form with the id 'test_node_form'
	switch($form_id)
	{
		case 'test_node_form':
			// next, unset the $form_state['input'] value, as if this
			// is set, it prevents #default_value from being overwritten
			unset($form_state['input']['field_test_textfield']);
			
			// Now generate the value that will be shown as the #default_value.
			// This will change depending on whether or not the form has been
			// submitted as part of the #ajax framewor
			$test = isset($form_state['values']['field_test_textfield']) ? $form_state['values']['field_test_textfield'] . "ASDFASDF" : '';
	              
			// Next, create the element
			$form['field_test_textfield'] = array
			(
				'#type' => 'textfield',
				// The default value is set according value as it was set above
				'#default_value' => $test,
				'#prefix' => '<div id="test_ajax_field">',
				'#suffix' => '</div>',
			);
	      
			// The submit button has #ajax set on it
			$form['field_test_submit'] = array
			(
				'#type' => 'button',
				'#value' => 'Go!',
				'#ajax' => array
				(
					'callback' => 'test_ajax_test_callback',
					'wrapper' => 'test_ajax_field',
				),  
			);
	}
}	

function test_ajax_test_callback(&$form, &$form_state)
{
	// This function only returns the changed values.
	// The values themselves are changed in the form definition.
	return $form['field_test_textfield'];
}

Now in comparing this to my earlier example, both the earlier example, as well as this example here have all alterations in the logic done within the form definition. Changes are based on the $form_state['values'], and only the changed element is returned in the ajax callback function.

Web Assistant’s picture

This does not work.

When I click the button, it does not update the value. After the callback, $form_state['values']['field_test_textfield'] is not set???? Am I missing something?

Can you confirm you have tested this and it is working your end?

It's interesting, this is how I went about doing it when I read the post initially, but ended up doing it the other way as this way didn't work for me!

If I use FirePHP to output the value of $test to console, it returns nothing, even after pressing the button multiple times?? I'm really puzzled!! I'm testing this on a vanilla Drupal install.

Jaypan’s picture

Yes, I can confirm it is working on my end, on a vanilla Drupal installation (no other non-core modules enabled).

Would you like me to put together a screencast for you as well?

Web Assistant’s picture

Yes please!

Web Assistant’s picture

Also, would be offended it I posted in the forums asking why it isn't working for me? Maybe someone can give me a reason why. This is not personal, I just want to understand what the problem is, and it appears I'm doing something wrong.

@gwalliman, it would be great if you can confirm this works.

Jaypan’s picture

Post your code.

Jaypan’s picture

Web Assistant’s picture

Thank you, you've been very kind helping me out with this. Would it be possible to get a copy of your test module?

Jaypan’s picture

I posted the entire code above. I made no changes from what you can see there to what was on the screencast, with the exception of the code I changed within the screencast to show that the code I was showing was in fact the code affecting my form.

Web Assistant’s picture

I cut and pasted exactly what you posted, and it doesn't work my end. It didn't work before when I used that method. It must be something my end. I will reinstall Drupal again and try to find out why it isn't working for me. Just know that if I can't figure it out I'm going to post in the forums asking for help why, it's nothing personal, I'm not saying I don't believe you.

Jaypan’s picture

Did you clear your cache? It's an implementation of hook_form_alter() which is cached to some degree. Also, are you sure you are working with a content type named 'test', and therefore on a form with the id of 'test_node_form'?

Web Assistant’s picture

Yes, I can confirm the cache has been cleared (and a few more times for luck). Also, the content type is 'test'. I'm currently installing another minimal install of Drupal 7, see if I've made a mistake somewhere.

Web Assistant’s picture

I can't get this to work, my steps:

  1. Download Drupal 7.
  2. Install using the minimal install option.
  3. Create a new content type called 'test'. (Confirm the machine name is also 'test').
  4. Install the module with the code you gave. Here's a copy of the module.
  5. Go to add a new node at /node/add/test, type some text in the textfield, click the button.

What happens is the throbber shows, but the text remains the same. I'm going to post this on the forum again, see if someone can tell me what the problem is.

Web Assistant’s picture

Hang on, I think I know why it's not working, you need to disable validation. I'll just double check.

Jaypan’s picture

With the code as-is, if required values have not filled in, an error appears and the #ajax magic does not happen.

Web Assistant’s picture

OK, so it appears your way does work BUT only if all the required fields are filled in. So lets assume this field is weighted before all the other form items, it won't work unless you go through the form and fill the other required fields in first. Would you just leave it as that or is there a fix for this?

I think it would be a legitimate request for someone to want to bypass validation on this. I tried '#limit_validation_errors' => array(), but that doesn't seem to work.

Jaypan’s picture

'#limit_validation_errors' => array(array('field_test_textfield'))

If you don't pass the text field value through #limit_validation_errors, the value is not a part of $form_state['values'], and this #ajax depends on that value being set.

Web Assistant’s picture

THANK YOU!! It seems to work as expected now. I must say, trying to get this solution from you is like getting blood from a stone. I just wished you'd posted this right from the start. I can now stop making that voodoo doll of you (that's a joke by the way!).

I am 100% clear this is the best way to do it and my initial example was the wrong way to do it.

Jaypan’s picture

Getting a solution from me is as simple as posting a question I have an idea to the answer of. Had you asked me nicely at the start, I would have shown you how to do it. But by the time you started asking me how it was done, you didn't 'ask', you made claims inferring I was lying and didn't know how to do it. That's a sure way to ensure I won't tell you how to do it.. It was when you finally admitted you don't know how to do it and needed to see how I do it that I was willing to show how it's done.

It's all about the approach. Asking someone nicely goes a lot further than calling them a liar in hopes that it will get their hackles up enough to show it.

Web Assistant’s picture

Oh Jay, admit it, you didn't know the answer, you spent the whole day trying to work it out.. that's the real reason why it took so long after I asked so nicely so many times. I think in future you shouldn't be so sensitive. If you consistently refuse to post the solution after claiming you know it, what else am I to think but you don't actually know it. Anyway, I'm happy now so thanks again.

Jaypan’s picture

I covered this earlier:

It would depend - by 'don't know how to'. Do you mean 'don't have the ability', or do you mean 'haven't sat down and figured it out yet'? If you mean the former, you are incorrect. Most D7 forms I build have #ajax functionality in them, and most D6 forms I built have #AHAH functionality built into them, and #AHAH was a lot harder than #AJAX. It's an API of Drupal than I am very comfortable in, and have extensive experience with. Look at the example I gave above - I typed that off my head. If you put that into a module, it may have some syntax errors, but if after fixing the syntax errors it didn't work, I'd be extremely surprised. Now if by 'don't know how to' you meant that I haven't sat down and solved gwalliman's problem, yes, you are entirely correct, I haven't sat down and figured it out.

I hadn't sat down and figured it out, so technically I didn't know how to solve his/her problem until I sat down and figured it out. But I know the #ajax framework and how to work with it, and I've built a lot of #ajax and #ahah enabled forms. But the real reason I didn't sit down to figure it out for as long as I did, was because I was never trying to prove the point that I knew how to do it. I simply pointed out how you were doing it wrong (which didn't involved figuring out the problem), then I linked to an example that showed how it is done (which still didn't involve figuring out the problem), and I even wrote a new example to show the theory behind it (which again, didn't involve figuring out the problem). It was only when you asked for help on how to actually do it, that I needed to sit down and figure out the problem.

You got tweaked on my pointing out bad practices, and you spent 20-30 posts going on about how I didn't know how it was done, when I was explaining how it was done, linking to posts showing how it's done, and giving examples that show how it's done. I already knew how it needed to be done, I'm not the one that got hung up on this.

Web Assistant’s picture

wow, so sensitive. If this is your attitude when someone says you 'may' be wrong, I think your in the wrong place. I believe you're being like this because I pulled you for being wrong on a previous post. It's a shame you think so little of us newcomers that you feel you have to act so self-righteous, just because you have vast experience does not make you right all the time, and it doesn't mean people shouldn't challenge your ideas without there being all this 'drama'.... this place is for discussion is it not!!

Jaypan’s picture

I have no problem with being called wrong, or even being wrong. It's not something I'm a stranger to in life. What I have a problem with is being called a liar. And if you are having troubles understanding why I don't like being called a liar, then you are beyond all hope.

As for my self-righteousness, I go out of my way to NOT be that way on here. You are the one who got all bent out of shape when I pointed out you were incorrect, you were the one furthering the interrogation, and you were the one who called me out for being a liar.

If my attitude is wrong for being called a liar, and that makes this the wrong place for me to be, then I'll have to stop coming here.

Web Assistant’s picture

OK Jay, whatever you say Jay. I think you're the one with the problem. Liar is your word, I didn't call you this, or even suggest this. I think you need to calm down and move on... nothing more to see here.

Thanks for your help, in future, I'll know not to ask.

Jaypan’s picture

Liar is your word, I didn't call you this, or even suggest this.

Really? You seem to have a short memory. Let's start with your post. First you make the claim that I cannot do it:

Oh dear, your example has nothing to do with what gwalliman was asking for. I think the reason you are avoiding showing how you would code the function gwalliman is asking for is that you don't know how to.

To which I replied in the next post that you were incorrect:

It would depend - by 'don't know how to'. Do you mean 'don't have the ability', or do you mean 'haven't sat down and figured it out yet'? If you mean the former, you are incorrect.

And in the following post you said:

Also, the reason why you can't show a working example for the functionality that was asked for is because you don't know how to, otherwise you would have done this earlier and this discussion would have ended a long time ago.

So after you made a claim, I denied it, and you went on to claim in the following post that I don't know how to do it. If you cannot see that you very clearly inferred I was lying about my ability to do it, you need to re-read the above quotes a few times.

If I read someone else doing this on the site, I would go out of my way to not help them out. And you did it directly to me - and I still helped you out. And then you go on to say I'm the one with the problem.

Yes, I definitely have a problem. It's that you will call people liars - then lie having done so after the fact, even though it's right there in text for everyone to see.

Web Assistant’s picture

Unbelievable!!! Still trying to pick a fight with me. How is this constructive, the solution has been found... move on. I have never seen anything like this, it's a joke.

Jaypan’s picture

You were the one who started the fight by getting all tweaked when I pointed out where you were wrong, and then calling me a liar. Then you went on to lie about it.

It's ironic that you can say 'move on', while not doing it yourself.

Web Assistant’s picture

Your a joke. I refused to get intellectually bullied by you any further. I'm going to take a break from this forum, this isn't right at all. You'll be hearing no more replies from me.

Jaypan’s picture

You will have to point out this 'intellectual bullying', as I have to disagree that I have bullied you whatsoever. You called me a liar and interrogated me. You act as if you are an innocent here, when you were the one who started, it, and kept it going. You only have yourself to blame for this one.

Web Assistant’s picture

I may have suggested you didn't in fact know the code to solve the problem, I admit, but only because you were sooooo reluctant to show it. Why else wouldn't you just show the solution, if you knew it? Rather than playing what appears to be some weird power game. Your attitude of "I know the solution to this problem, but I won't tell you because I don't have to prove anything to you" really surprised me! I just assumed you were a bit off because I mentioned you were wrong in previous post.

I was actually going to stay away from this forum, but why should I, I pay my membership. I'll just know better not to ask you for help, or correct you when you are wrong... then everyone's happy. At the end of the day, all I wanted to do was help the guy out.

Jaypan’s picture

I may have suggested you didn't in fact know the code to solve the problem

Suggested? You call this a suggestion:

the reason why you can't show a working example for the functionality that was asked for is because you don't know how to.

That mate is a direct statement, not a suggestion.

Why else wouldn't you just show the solution, if you knew it?

Mate, you have something to learn about 'approach'. Your approach in asking me was 'you can't do it, if you can prove it'. I don't know you, and I have nothing to prove to you. I'm not going to do it because you demand it. If you want someone to do something, generally the most effective way to get them to do it is to politely ask them to do it. Trying to manipulate them by attacking them personally so that they prove it in order to defend themselves is at the best going to leave the person with hurt feelings. But I'm not a person who can be manipulated to do something I want, which is why I flat out refused you. I don't care if people think I cannot do it - I know I can, and more importantly my clients know I can.

You still keep playing the victim here, but you brought this upon yourself mate. I've never had to get to this point with ANYONE on this forum before. Ever. The forum is still the same, I'm still the same, and Drupal is still the same - the only thing different between this thread and others is you. Go back and read the thread - I made a statement, then spent the rest of the time answering your interrogation. I never attacked you as a person and I never made any demands for proof of skill from you. So if the only thing different between this thread and others is you, and your approach, then it's a fairly straightforward process to deduct that the problem here is you.

Web Assistant’s picture

Remember when I emailed you advising you that I could see error messages on your website, I was trying to be helpful, which was the case in this post. All I wanted to do was help the guy out. So with that in mind, can we agree to disagree and end this discussion.

Jaypan’s picture

Your were most definitely trying to help, and that's a good thing. All I did was point out that the help you gave was incorrect. That's when you got all tweaked and brought it to this. You still keep trying to play as if you are the victim. If you really want to drop it, either walk away, or admit your culpability. Or keep on arguing if that's what you prefer. But don't expect me to keep quiet while you profess to be the victim here, as it's very clear you are not.

Web Assistant’s picture

Oh Jay. No point stirring this up even more, lets just move on.

Jaypan’s picture

You keep saying it, but you keep not doing it.

Web Assistant’s picture

We as a collective need to move on. Let us be friends again Jay, and move on.

Jaypan’s picture

Again, you keep saying it, and you keep not doing it.

Web Assistant’s picture

Oh, you need to have the final word, go ahead...

Jaypan’s picture

you need to have the final word

I've actually been playing with your need for the final word for like the last five posts.

Web Assistant’s picture

Thanks for all your help Jay, all the best.

Jaypan’s picture

You too! Haha.

dotpex’s picture

Web Assistant’s picture

-1 = 0 :)

Web Assistant’s picture

Also, just to add that the reason I used:

<?php
$test = isset($form_state['values']['field_test_textfield']) ? $form_state['values']['field_test_textfield'] . "ASDFASDF" : '';
  $form['field_test_textfield']['#value'] = $test;
?>

Was because gwalliman said:

It should simply take whatever is entered in the textfield, append some dummy text to it, and on form rebuild set that as the value of the textfield

So I can't see it being anything else, maybe gwalliman would like to clarify?

gwalliman’s picture

You are correct in your interpretation of the functionality.
I'll test moving the logic into the callback on Monday; hopefully it will work for me. I'll update with the results.
In the meantime, though, let me ask about your use of $form_state['values'] over $form_state['input']. I have seen this used as the example in countless AJAX tutorials but never actually saw the 'values' in my own $form_state. I checked it out from inside the handler and it just wasn't there. That's why I was using ['input']. I did see a few other people who never saw the ['values'], but never saw a conclusive reason as to why.

Might the fact that I'm not seeing ['values'] be related to the problem I'm having?

Web Assistant’s picture

Have you got FirePHPCore installed? If so you can do the below test, if not follow instructions here to install first:

<?php
function yourmodule_form_alter(&$form, &$form_state, $form_id) {
    
  // show $form_state['values] in FirePHPCore
  $path = libraries_get_path('FirePHPCore');
  if (file_exists($path . '/fb.php')) {
    include_once $path . '/fb.php';
    include_once $path . '/FirePHP.class.php';
  }
  isset($form_state['values']) ? dfb($form_state['values'], 'ERROR', FirePHP::ERROR) : '';

  if ($form_id == 'test_node_form') {
    $form['field_test_textfield'] = array(
      '#type' => 'textfield',
      '#default_value' => '',
      '#weight' => -10,
      '#ajax' => array(
        'callback' => 'ajax_example_submit_driven_callbacktest',
        'wrapper' => 'test_ajax_field',
        'name' => 'submit1',
      ),
      '#prefix' => '<div id="test_ajax_field">',
    );
    $form['field_test_submit'] = array(
      '#type' => 'button',
      '#ajax' => array(
        'callback' => 'ajax_example_submit_driven_callbacktest',
        'wrapper' => 'test_ajax_field',
        'name' => 'submit1',
      ),
      '#value' => t('Update'),
      '#weight' => -10,
      '#limit_validation_errors' => array(),
      '#suffix' => '</div>',
    );
  }
}

function ajax_example_submit_driven_callbacktest($form, $form_state) {
  return $form;
}
?>

The above code is showing, using AJAX, button vs textfield and how they differ in $form_state['values'] output. This is the reason why, for buttons, I'd say put the logic in the callback as the values aren't available like they would be for the textfield.

Jaypan’s picture

You will not see $form_state['values'] filled in unless you are looking at the form generation when it's being created through #ajax. The reason for this is that in the initial form definition, the form hasn't yet been submitted, so there are no values to be filled in. However when the form is rebuilt during an ajax call, $form_state['values'] will exist. I believe the values in $form_state['input'] are unsanitized, so using them is a security risk.

#AJAX testing when creating a form definition can be quite difficult without a debugger installed - but I don't use a debugger myself. To do AJAX testing, I do the following:

1) I use firefox with the firebug plugin, set to the console tab.
2) I load the form normally through an HTTP request
3) I edit the code, and add my debugging code into the form definition. For example, at the top of the form definition I often do this:

function my_form($form, &$form_state)
{
  die(print_r($form_state['values']));
}

4) I then trigger my #ajax call (note, this will cause an error, but that's ok, it's expected)
5) The AJAX call is shown in the console tab of firebug. This can be expanded to see the response. Since the form generation has been killed with a dump of the $form_state['values'] element, this is listed as the response of the call

In this way, I can see what is in $form_state['values'] in an #AJAX call, and change my form definition accordingly.

gwalliman’s picture

I got it working - using a combination of all of your suggestions. The empty

is still appearing, but since it's all working now I guess it's not really a problem.
Thanks much for all of your help.
Web Assistant’s picture

@gwalliman, just to add that I find the problem with debugging using the method Jay uses is that you have to keep adding and removing the code from the form. With using an AJAX debugger like FirePHP, you can just leave it in there until your done developing the form.

@jay, http://drupal.org/node/1454692#comment-5658052

cfemocha’s picture

Thank you for the nice examples. It's working for me but if I changed the ajax button type to '#type' => 'submit', then no values will be appended. Will this not work if I need to submit my form?
Any help is appreciated.

waqarit’s picture

That is really so interesting and helpful discussion between @Jaypan and @Web Assistant .
I appreciate both of you. Good and keep it up while helping all the people who facing the problems.

Jaypan’s picture

I'd forgotten about this. That was an entertaining re-read!

waqarit’s picture

Yes, that is really entertaining but also helpful specially for new comers.

Jaypan’s picture

I'm glad it helped!

WorldFallz’s picture

definitely both amusing and informative. I also can't help but notice jay is still here in the forums helping people out day after day, post after post, user upon user-- while "Web Assistant" (the irony of that name in the context of this thread is not lost, lol) has long since left years ago.... also a lesson.

Jaypan’s picture

I also can't help but notice jay is still here in the forums helping people out day after day

You have to wonder why people would take any advice I give, considering that according to Web Assistant I don't know what I'm doing :)

waqarit’s picture

You are doing fabulous at all @Jay