Blog demo - Ajax submit

Hello everybody

First couple hours playing around with this nice new framework and I starting by looking at the blog demo included in the zip file (by the way a great way to learn the framework - just a little more comments would be good too).

In order to extend this demo a bit further I am trying to change the comments submission into ajax submit.

For that I started by adding a ajaxButon in the comments view. I am now struggling with the ajax response because I do not quite understand the logic behind it. Should I use renderPartial? Where do I catch the ajax answer and render differntly?

I am a little bit lost and quite new to this framework - thank you for any help you can provide.

Cheers,

You need ajaxSubmitButton to submit the form data. You need to create a new action in the controller to handle this ajax request. In the action, you add the comment and return a partial view which should replace the div content of the comments section.

Since you are new to the framework, I recommend you focus more on the whole architecture (e.g. controller, action, view, model, validation) rather than ajax handling.

Hello,

Thank you for your reply.

My problem came from the fact that “ajaxSubmitButton” creates an input of the type “button” whereas the “submitButton” creates an input of the type “submit”.

Changing the ajaxSubmitButton to the "submit" type made it work.

Is that a normal behavior? I noted that submitButton sets : $htmlOptions[‘type’]=‘submit’; before creating the button.

Thanks for your help,

Do you mean ajaxSubmitButton doesn't submit the form as expected? I don't think the button type matters here since both require js.

It did submit the form but the comment wasn't saved as expected in the newComment method.

Did you get the $_POST data as expected? The problem lies in how you handle the $_POST data.

Hmm seems that submitComment in the _POST is not filled as expected (like a normal submit)

this is my ajax submit :

<?php echo CHtml::ajaxSubmitButton('AJAX '.$buttonLabel,array(),array(),array('name'=>'submitComment', 'type'=>'submit'));?>

Try the simple setting first, such as <?php echo CHtml::ajaxSubmitButton('submit'); ?>

Make sure the button is inside a form together with other fields needed to be submitted.

Tried that, but the $url is mandatory (error on yii\framework\web\helpers\CHtml.php(715)).

my button is in the rowaction of the comment box of the blog demo.

I still did not figure out how to specify what the ajax answer should update (div with id #comments)

In my Post controller :

<?php


	public function actionShow()


	{


		$post=$this->loadPost();


		$comment=$this->newComment($post);


				


		if (Yii::app()->request->isAjaxRequest)


		{


			$this->renderPartial('show_comments',array(


				'post'=>$post,


				'comments'=>$post->comments,


				'newComment'=>$comment,


				)


			);


		}


		else


		{		


			$this->render('show',array(


				'post'=>$post,


				'comments'=>$post->comments,


				'newComment'=>$comment,


			));


		}


	}


?>

I created a show_comments view without the display of the posts (based on the show.php view)

Sorry, you need to specify the url parameter. For example, you may use the following:



<?php echo CHtml::ajaxSubmitButton('submit', array('show'), array('update'=>'#test'); ?>


<div id="test"></div>


In your 'show' action, you may display something (e.g. print_r($_POST)).

I really do not understand why doesn't the "SubmitComment" POST is not set when using a submitAjaxButton. The other post variables are correctly set tho. Any ideas from where this can come from ? (didn't change anything else from the blog demo)

If ajaxSubmitButton works now (except the SubmitComment), you may try setting the button type to be 'submit' so that SubmitComment can be submitted.

submitComment is still not set, I changed the newComment method :

<?php


else if((isset($_POST['submitComment']) || Yii::app()->request->isAjaxRequest) && $comment->save())


this makes the save work now.

once I submit an ajax request, the delete button does not work anymore, the update does tho.

I also got the Problem. The submit button value isn't submitted as post value on ajax forms.

This is because the buttons doesn't submit the form, cause the onclick js code is 'return false;' to avoid that the form is actually 'really posted' on the normal way.

And the jquery serialize function only serialize the contents of the input fields of the form.

'data':jQuery(this).parents("form").serialize()

The value of a button or submitbutton also doesn't change on click.

If the 'value' of buttons would also be serialized and submitted you also wouldn't know which button you have pressed when multiple buttons are on a form.

The only way to solve this is to get the value of the clicked button and put the value into a hidden field inside the form.

Thanks for your feedback.

If i get this correctly, in Ajax, a submitted form is not really submitted (i.e. not like a regular form post)? Or is this specific to jQuery?

For the blog demo issue, i can indeed add a hidden field which will be submitted or completely change the comment save handling (not chacking for the POST['submitComment'] value.

Cheers,

I find one "easy" possibility to solve your problem.



<?php echo CHtml::ajaxSubmitButton('submit', $this->createUrl('newComment',array('command'=>'submitComment')), array('update'=>'#test',array('name'=>'submitComment'))); ?>


Use this code, it will create a ajaxSubmitButton which calls your newComment method and post the form values in $_POST but also 'command' => 'submitComment' via $_GET.

So you can now knew which button on your Form has been pressed.

But it would actually better in terms of reusing code for ajax and normal forms if there is a function to set an hidden action command for ajax form posts.

I have made a patch to solve this issue. It adds a parameter to ajax options called "extraData" that contains an array of names and values for hidden inputs to be added dynamically. It behaves somewhat like jQuery.yii.submitForm():




//file: system.web.js.source.jquery.yii.js

...


	addFormParams : function (element, params) {

		var f = $(element).parents('form')[0];

		jQuery.each(params, function(name, value) {

			input = jQuery('input[name="'+name+'"][type="hidden"]')[0];

			append = false;

			if(!input) {

			    var input = document.createElement("input");

			    input.setAttribute("type", "hidden");

			    input.setAttribute("name", name);

			    append = true;

			}

			input.setAttribute("value", value);

			if(append) {

			    f.appendChild(input);

			}

		});

	},


	serializeFormWithExtraParams : function (element, params) {

		jQuery.yii.addFormParams(element, params);

		return jQuery(element).parents("form").serialize();

	}


...







//file: system.web.helpers.CHtml

...


	/**

	 * Generates the JavaScript that initiates an AJAX request.

	 * @param array AJAX options. The valid options are specified in the jQuery ajax documentation.

	 * The following special options are added for convenience:

	 * <ul>

	 * <li>update: string, specifies the selector whose HTML content should be replaced

	 *   by the AJAX request result.</li>

	 * <li>replace: string, specifies the selector whose target should be replaced

	 *   by the AJAX request result.</li>

	 * <li>extraData: array, names and values to be added to a form as hidden inputs.</li>

	 * </ul>

	 * Note, if you specify the 'success' option, the above options will be ignored.

	 * @return string the generated JavaScript

	 * @see http://docs.jquery.com/Ajax/jQuery.ajax#options

	 */

	public static function ajax($options)

	{

		Yii::app()->getClientScript()->registerCoreScript('jquery');

		if(!isset($options['url']))

			$options['url']='js:location.href';

		else

			$options['url']=self::normalizeUrl($options['url']);

		if(!isset($options['cache']))

			$options['cache']=false;

		if(!isset($options['data']) && isset($options['type'])) {

			Yii::app()->getClientScript()->registerCoreScript('yii');

			$extraData = '{}';

			if(isset($options['extraData'])) {

				$extraData = CJavaScript::encode($options['extraData']);

				unset($options['extraData']);

			}

			$options['data']='js:jQuery.yii.serializeFormWithExtraParams(this,'.$extraData.')';

		}

		foreach(array('beforeSend','complete','error','success') as $name)

		{

			if(isset($options[$name]) && strpos($options[$name],'js:')!==0)

				$options[$name]='js:'.$options[$name];

		}

		if(isset($options['update']))

		{

			if(!isset($options['success']))

				$options['success']='js:function(html){jQuery("'.$options['update'].'").html(html)}';

			unset($options['update']);

		}

		if(isset($options['replace']))

		{

			if(!isset($options['success']))

				$options['success']='js:function(html){jQuery("'.$options['replace'].'").replaceWith(html)}';

			unset($options['replace']);

		}

		return 'jQuery.ajax('.CJavaScript::encode($options).');';

	}


...



And in the view:




...


	<?php echo CHtml::beginForm(); ?>

...

... some inputs ...

...

	<div class="action">

		<?php

		$htmlOptions['name'] = 'abc[def]';

		$htmlOptions['id'] = 'abc_def';

		$htmlOptions['type'] = 'submit';

		$htmlOptions['value'] = 'delete';

		$ajaxOptions['extraData'] = array($htmlOptions['name']=>$htmlOptions['value']);

		echo CHtml::ajaxSubmitButton(Yii::t('app', 'delete marked'), '', $ajaxOptions, $htmlOptions);

		?>

	</div>


	<?php echo CHtml::endForm(); ?>


...



If this form is submitted without javascript, the post value for button will be present, idem when with javascript.

Is it generic enough to be committed to framework source? If not, how could I extend it to get the same behavior?

Thanks