struggleing through the ajax world

We recently decided to port our website over to the Kohana Framework. A great decision! However, we are not only switching over to Kohana and the MVC approach, we also want to make more use of ajax. Not at least because we want to give our users a better experience. Whatever, I will go more deeper into the whole migration process in some weeks. Today Im asking the jQuery heros for their help.

What I want is

1. to save a form

2. to show a modal box, telling the user the form can’t be saved, incase of any validation rule violation

3. to ask the user for confirmation incase of any special conditions. For example, if he changes his zipcode he will be informed that this will lead to loosing his credit points and he must confirm with yes in order to save the form.

What I have

I won’t post every detail but hopefully enough to understand my problem😉 I should also mention that I use the simplemodal box[1] to show the dialog. And it might be also worth knowing that Im a total noob on javascript and the whole ajax thing.

The View

I have a simple form like that:

<?=form::open('editroot/save', array('id' => 'edit_root'), NULL)?>
<table border="0" cellpadding="5" cellspacing="5">
<tr>
<td width="100">Vorname:</td>
<td><?=form::input('firstname',$firstname, ' class="inputbox_mp" maxlength="50"');?></td>
</tr>
<tr>
<td>Nachname:</td>
<td><?=form::input('lastname',$lastname, ' class="inputbox_mp" maxlength="50"');?></td>
</tr>
</table>
<?=form::close()?>

And my javascript looks like that…


$(document).ready(function()
{
     //attach onSubmit to the form
     $('#edit_root').submit(function()
     {
         //When submitted do an ajaxSubmit
         $(this).ajaxSubmit(
         {
             dataType: 'json',
             success: function(data, responseCode)
             {
                 if (data.category == 'conditionApplied')
                 {
                     confirm("Are you sure you want to ignore condition XYZ", function ()
                     {
                         jQuery.post('<?=url::base(FALSE)?>editroot/save', {ignoreConditions : "ignoreConditions"});
                     });
                 }
                 else if (data.category == 'invalid')
                 {
                     confirm("Your data is wrong (invalid email etc.)", function ()
                     {
                         // do nothing because the user has to change his inputs
                     });
                 }
             }
         });
         //return false to prevent normal submit
         return false;
     })
 }); 

function confirm(message, callback)
{
    $('#confirm').modal(
    {
        close:false,
        position: ["20%",],
        overlayId:'confirmModalOverlay',
        containerId:'confirmModalContainer',
        onShow: function (dialog)
        {
            dialog.data.find('.message').append(message);
            // if the user clicks "yes"
            dialog.data.find('.yes').click(function ()
            {
                 // call the callback
                 if ($.isFunction(callback))
                 {
                     callback.apply();
                 }
                 // close the dialog
                 $.modal.close();
            });
        }
    });
}

The Controller

	public function save()
	{
		if(request::is_ajax())
		{
			$post = new Validation($_POST);
			$post->add_rules('firstname', 'required');
			if(!$post->validate())
			{
				echo json_encode(array('category'=>'invalid','text'=>'Error during saving'));
			}
			else
			{
				if (isset($_POST['ignoreConditions']))
					$this->store();
				else
				{
					echo json_encode(array('category'=>'conditionApplied','text'=>'Error during saving'));
				}
			}
			$this->auto_render=false; 
			return;
		}
	}
	
	private function store()
	{
		//its valid, the user had been asked to accept special conditions etc.
                //we are ready to update the user account in the database
	}

Let me explain what the code does:

1. When the user clicks on the submit button, the $_POST array will be send through an ajax request to the controller.

2. The controller validates the data:

Now A.) If the user made an invalid input (missing firstname in this example) the controller will return ‘category’=>’invalid’. The javascript will show a message telling the user that the data was wrong.

Or B.) If the user made an input that will lead to something special (loosing credit points etc.) the controller will return ‘category’=>’conditionApplied’. The javascript will show a confirm message asking the user if he was sure to save and accept the special conditions coming along with his decision.

The first one is not really interesting because it means the user can not proceed until he changes his input. However the second one is more interesting. Incase the user clicks ‘Yes’ I want to send the $_POST data again and would like to append a value as an indicator for the controller.

It’s easy to send this single $_POST indicator but I need it to be send TOGETHER with the actual data. I find that hard to manage. Does it mean that I need to manipulate the form by appending a hidden field or anything?

And somehow, I think the whole js code smells and it feels like a dirty hack to me. There must be a more straight forward approach to it, isnt there? I also dislike the way I return the json answers…however I just took an example from the web and changed it a bit to make it run for me😉

Happy for anybody trying to help me on this…

UPDATE

I finally got it working on my own. So here is what I did:


$(document).ready(function()
{
     //attach onSubmit to the form
     $(document).ready(function() 
{ 
	//attach onSubmit to the form
	$('#edit_root').submit(function()
	{
		//When submitted do an ajaxSubmit
		$(this).ajaxSubmit(
		{
			dataType: 'json',
			success: function(data, responseCode) 
			{
				if (data.category == 'conditionApplied')
				{
					confirm(data.message, 'irgendein title', 'yesAndNo', function () 
					{
						var queryString = $('#edit_root').formSerialize(); 
						queryString = queryString + "&acceptConditions"
						jQuery.post('<?=url::base(FALSE)?>editroot/save', queryString);
					});
				}
				else if (data.category == 'invalid')
				{
					confirm(data.message,'irgendein anderer title', 'ok', function () 
					{
						// do nothing because the user has to change his inputs
					});
				}
			}
		});
	//return false to prevent normal submit
	return false;
	})
}
);  

function confirm(message, title, buttons, callback) {
	$('#confirm').modal({
		close:false,
		position: ["20%",],
		overlayId:'confirmModalOverlay',
		containerId:'confirmModalContainer', 
		onShow: function (dialog) {
		
		if (buttons == 'yesAndNo')
			dialog.data.find('.ok').remove();
		else if (buttons == 'ok')
		{
			dialog.data.find('.yes').remove();
			dialog.data.find('.no').remove();
		}
			
			dialog.data.find('.message').append(message);
			dialog.data.find('.header').append('<span>' + title + '</span>');			
			dialog.data.find('.yes').click(function () {
				if ($.isFunction(callback)) {
					callback.apply();
				}
				$.modal.close();
			});
			
			dialog.data.find('.ok').click(function () {
				if ($.isFunction(callback)) {
					callback.apply();
				}
				$.modal.close();
			});
		}
	});
}

The Controller

		public function save()
	{
		if(request::is_ajax())
		{
			$post = new Validation($_POST);
			$post->add_rules('firstname', 'required');
			if(!$post->validate())
			{
				$errorMessage = '';
				$errors = $post->errors();
				foreach ($errors as $key => $val)
				{
				   $errorMessage .= $key.' failed rule '.$val.'<br />';
				}				   
				
				echo json_encode(array('category'=>'invalid','message'=>$errorMessage));
			}
			else
			{
				if (isset($_POST['acceptConditions']))
					$this->store();
				else if (1 == 1) //special condition is true
					echo json_encode(array('category'=>'conditionApplied','message'=>'Wollen Sie Kondition XYZ in Kauf nehmen?'));
				else
					$this->store();	
			}
			$this->auto_render=false; 
			return;
		}
		else
		$this->template->content = 'Ohne Javascript nix los😦';
	}
	
	private function store()
	{
		//its valid, the user had been asked to accept special conditions etc.
	}

Well, basically I wasn’t really aware that Im using jQuerys form plugin here. Once I figured it out, I just checked the docs and found out I can serialize the form and just append any variable I want. The rest is more or less some sugar…I modified the confirm dialog to be flexible with its button etc. Maybe this will ease the pain of some other ajax newbie out there…

[1] http://www.ericmmartin.com/projects/simplemodal/

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s