Build a Titanium Mobile Pizza Ordering App: Order Completion

Build a Titanium Mobile Pizza Ordering App: Order Completion

Tutorial Details
  • Technology: Titanium Mobile
  • Difficulty: Intermediate
  • Estimated Completion Time: 30 Minutes
This entry is part 4 of 4 in the series Build a Titanium Mobile Pizza Ordering App

Welcome to the fourth and final installment in our series demonstrating how to build a pizza ordering application with Titanium Mobile. In this tutorial, we’ll be creating the processing customer orders and e-mail the results to the pizza chef!

Step 1: Client-Side Validation

Before we make a call to our PHP file, we want to do a little bit of form validation. We just want to check if the fields are empty and, if they are, alert the user to fill them in.

Open your details.js file and add this snippet under our cancel button click-event:

//-- Submit order. Check if the text fields are blank
order.addEventListener('click',function() {
	if (names.value == '' || address1.value == '' || address2.value == '')
	{
		alert('All fields are required');
	}
	else
	{
		//-- Disable fields and buttons before making our HTTP request
		names.enabled    = false;
		address1.enabled = false;
		address2.enabled = false;
		order.enabled    = false;
		cancel.enabled   = false;
		//-- Change this URL to where ever yours exists
		orderReq.open('POST','http://yourserver.com/submit_order.php');
		var params = {
			names: names.value,
			address1: address1.value,
			address2: address2.value,
			crust: win.crust,
			toppings: win.toppings
		};
		orderReq.send(params);
	}
});

In the previous tutorial we made the details.js file in which the above code should be placed. We defined the openReq variable at the top of the script in the last tutorial, and this variable will be used to communicate between the client-side code and the server-side PHP file.

If all the fields are filled out, we will disable all the text fields so the user can’t change them once they’ve been sent out. We then call the open() method on our orderReq variable. This will setup the file we are going to open. We also make a new object called params. We give it some keys and values that we will access in the PHP script. Finally, we call the send() method.

It is important to note that if this script was destined for a live application, we would need to perform significant server-side validation in addition to the client-side validation provided above. We will skip this step in this tutorial and instead just focus on building the mobile code.


Step 2: Order Processing

Make a new file called submit_order.php and insert the following:

<?php
//-- POST are variables from details.js
$names    = $_POST['names'];
$address1 = $_POST['address1'];
$address2 = $_POST['address2'];
$crust    = $_POST['crust'];
//-- clean up the javascript array
$toppings = str_replace('"','',substr(substr(stripslashes($_POST['toppings']),1),0,-1));
$toppings = explode(",\n", $toppings);
//-- Where the order will be sent
$to = "yourEmail@yourserver.com";
$subject = "Pizza Order!";
$message = "A new order has been submitted.<br/>";
$message .= $names<br/>";
$message .= $address1 . "<br/>";
$message .= $address2 . "<br/><br/>";
$message .= $crust . " pizza with:<br/>";
$message .= "<ul>";
if (strlen($toppings[0]) == 1)
{
	$message .= "<li>Plain (cheese pizza)</li>";
}
else
{
	for ($i = 0; $i < count($toppings); $i++)
	{
		$message .= "<li>" . $toppings[$i] . "</li>";
	}
}
$message .= "</ul>";
//-- The headers will let us send HTML code as an email
$headers = "From: noreply@thepizzaplace.com\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
//-- if mail gets sent, return true, else return false. This gets handed off the our onload method in details.js
if (mail($to,$subject,$message,$headers))
{
	$response = array('mail' => true);
}
else
{
	$response = array('mail' => false);
}
echo json_encode($response);
?>

Starting from the top, we POST our variables from the param object we made in detais.js. Next, we must convert the Javascript array to a PHP array. The Javascript array was a little tricky because of the line breaks we put in the code (if you guys know of a better method, post it in the comments!). The method I came up with was somewhat clean and simple.

The next part we will output how the message looks. The $to variable is where you should insert the e-mail address where you want the order to be sent. Next, we check the length of the toppings to determine if it was just a cheese pizza or if the orderer specified custom toppings. If it did have toppings, we will make an HTML list to display the toppings.

Next we set the headers. This will allow the e-mail to be HTML formatted when you receive it. Then we call the PHP mail() method. It is formatted like this: mail($to, $subject, $message, $headers). In the code, we test if the mail gets sent, set the $response to true if it does or false if it doesn’t. Finally, the json_encode($response) will allow the details.js file to read the object back. Don’t forget to upload this file to your server!


Step 3: Handling the onload event

Let’s go back to details.js. Under our click event for the order button, we need to make a new onload event and while we are here, let’s add our onerror event.

//-- onLoad method for our http request
orderReq.onload = function()
{
	var json = this.responseText;
	var response = JSON.parse(json);
	//-- Mail was sent
	if (response.mail == true)
	{
		var alertDialog = Titanium.UI.createAlertDialog({
			title: 'Success',
			message: 'Your order has been submitted (check the email you used in your submit_order.php file)',
			buttonNames: ['OK']
		});
		alertDialog.show();
		alertDialog.addEventListener('click',function(e)
		{
			Ti.App.fireEvent('resetApp');
		});
	}
	else
	{
		//-- Mail failed
		alert("PHP failed to send the order to the email provided in submit_order.php. Are you sure you have a mail client on your server?");
		names.enabled    = true;
		address1.enabled = true;
		address2.enabled = true;
		order.enabled    = true;
		cancel.enabled   = true;
	}
};
//-- Network error
orderReq.onerror = function(event)
{
	alert('Network error: ' + JSON.stringify(event));
	names.enabled    = true;
	address1.enabled = true;
	address2.enabled = true;
	order.enabled    = true;
	cancel.enabled   = true;
};

We go back to our orderReq var and add an onload and onerror event. We make a new var called json and set it equal to this.responseText. That contains the data sent back from our PHP file. Next, we have to parse the json string by calling JSON.parse(json). We can now text if response.mail is true meaning the mail was sent successfully. If it was successful, alert the user that the order has been submitted. We want to listen for the click event on the OK button this time so we make an alert a little different than we’ve been using it. We listen for the click event and on click we fire a new custom event called resetApp. We will cover that in the next step.

Our onerror event will hopefully tell you if something goes wrong. If onload returned false, chances are your server doesn’t support mail(). Another good possibility is you have a mispelling. Try access the script directly in your web browser and see if it contains any syntax errors.

Go ahead and give it a try now. Submit your order. Hopefully you will see something similar to the screen below:

If you got a success message, go and check the e-mail you provided in the PHP script. This is what mine looked like coming through Gmail:


Step 4: Handling the resetApp Event

Let’s open up the main.js file again. We are going to add our final custom event:

//-- Have our app listen for our custom events
Ti.App.addEventListener('toppings',openToppings);
Ti.App.addEventListener('cancelToppings',openCrust);
Ti.App.addEventListener('details',openDetails);
Ti.App.addEventListener('cancelDetails',openToppings);
Ti.App.addEventListener('resetApp',resetApp);

Now, to handle the custom event we need to make a new method called resetApp().

//-- This gets called after an order is submitted. It basically starts the app over.
function resetApp()
{
	details.close();
	openCrust({});
}

After a successful order submission, the app will be reset and you will be taken back to the crusts screen.


Conclusion

We used our orderReq var to handle the PHP script. We sent some variables to PHP and parsed them, attempted to send an email and handled the response from the script. Hopefully you all have made it this far with no issues! I hope you all enjoyed this series as much as I enjoyed writing it. It was a fun app to create and hopefully you can use what you learned and apply the principles to your own apps!

Series Navigation«Build a Titanium Mobile Pizza Ordering App: Order Form Setup

Ronnie Swietek is rondog on Activeden
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Danny

    Wow really, really very cool en very good for learning purposes. Thank you for giving this to the community (and to me).

    If you are planning new series of tutorials I would love to see an example of loading images from an external server (different categories show thumbnails and onclick you get the bigger image). Something similar to http://itunes.apple.com/us/app/getty-images/id376812381?mt=8

    Another tutorial would be great about external audio files: create a playlist (tableView) and a nice player. Can’t seem to find any good examples on the web.

    Again thank you very much,
    Danny

    • http://ronnieswietek.com Ronnie Swietek
      Author

      Both those ideas are definitly doable with Titanium. I haven’t worked with audio API yet, but I’m sure it isn’t too hard. External audio files meaning streamed from a server or embedded in the apps file system? I would assume off of a server. Maybe I could combine your two ideas. The thumbnails would be albums and clicking an album would take you to the tracks. Clicking a track would launch the audio player. The only thing missing would be going from thumbnail to a larger image.

      • Danny

        Hi Ronnie,

        Thanks for your comment. Hope to see you do some of those tuts because a lot of apps use the image gallery option but I searched the hole net and can’t find a good example on how to create this (with images on a external database. Exact the same for the audio

        Thanks anyway and keep up doing the good work
        Danny

  • http://ronnieswietek.com Ronnie Swietek
    Author

    …or even access your itunes library on the device! That would be awesome!

    • http://ronnieswietek.com Ronnie Swietek
      Author

      I mean iPod

    • Han

      Ya ya, I also want to know how to access iTunes Lib from Titanium :)

      • Patrick

        to open your gallery on a device on titanium you use the call Titanium.Media.openPhotoGallery();
        You can download kitchensink from their git and see how it’s done there its not really that hard

  • Eric

    Thanks for the tutorial Ronnie. Much needed for the community.

    @Danny…Have a look at this tutorial from Query7. It covers a sound app…

    • Danny

      Hi Eric,

      Thanks for the link but for some reason I can’t get this app working. Not on android nor on iphone.

      Still looking futher for some great examples to accomplish what I want.

      Danny

  • Han

    Hi Ronnie, Appreciate first for your complete series. Yes sure it is possible way to read local file or server steaming because there are some example already at KitchenSink. Please continue another awesome series again. Keep in touch :)

  • Ibrahim

    Great tutorial!!!
    Just got a quick question for you !
    When i compiled on my iphone, on the second screen(toppings screen), the pizza does not appear at all(the toppings do, and the checkboxes work) , on the simulator it run’s without any problems!, all the windows appear!
    What could be the problem?
    I am using iOS 4.2.1 and i just download Titanium,so i guess it’s the latest version!
    Thank you!!

    • http://ronnieswietek.com Ronnie Swietek
      Author

      Hmm that is odd. So on the emulate it works and the iphone the pizza images aren’t appearing? You know I never deployed this to the phone so it is hard to say what the issue is exactly. I would try removing the animation (where it fades in) and see if you get different results.

    • http://boydlee.com Boydlee Pollentine

      Delete all the files from your “build/iphone” folder, and then do a re-compile. This often fixes weird issues like the one you are describing, especially after a Titanium SDK update.

  • http://www.professorperformance.com Rod

    For a simpler solution, you can use the JSON.stringify() function built into Titanium. Sending that to the server, php 5.2+ can parse the json file very easily.

  • Bill

    When using a main “controller” such as main.js, how can one pass data to another child window? I would like to follow a quasi-MVC pattern where I centralize all the event handling logic in main.js and fire events from child windows to keep them functionally independent. I have no problems firing/handling the events, but am not having much luck being able to pass the event data on from main.js into the child windows.

    For example, assume that main.js has created WindowA and WindowB. I can fire an event from WindowA for which main.js listens and can pass data back to main from the event-firing window with no problem. The issue I have is then passing that block of data to WindowB. The workflow is as follows:

    1. main creates WindowA and WindowB.
    2. main opens WindowA…eventually WindowA fires an event that main is listening for and receives the data in the event from WindowA.
    3. the event handler in main.js calls a local function in main.js whose purpose is to open WindowB and somehow pass the data on to WindowB.

    I can get all the way to the point where I can open WindowB, but am not able to pass the data to WindowB. I’ve tried using a custom variable in WindowB to hold the data, but the variable is not getting set prior to opening WindowB.

    Some example code to illustrate:

    ——————–
    in main.js
    ——————–

    var windowA = Ti.UI.createWindow();
    var windowB = Ti.UI.createWindow();

    /*
    1. Open windowA and do stuff…
    2. WindowA fires event that main.js listens for…
    */
    windowA.open();

    /*
    3. Event handler (elided in this example) in main.js calls local openWindowB() function passing the event data in “e”…
    4. Finally, pass event data “e” on to WindowB ???
    */
    function openWindowB(e)
    {
    // event data “e” is correct…

    windowA.close();
    windowB.url = ‘windows/windowB.js’;

    // how to pass “e” to WindowB?
    windowB.foo = e; // does not work?

    windowB.open();
    }

    ———————–
    in windowB.js
    ———————–

    // var to hold data passed from main.js
    var foo;

    // should display contents of “e” passed from main.js?
    alert(foo);

    I’m guessing I have some kind of scoping issue or execution context problem which prevents me from setting the variable in WindowB, but it seems like this shouldn’t be this difficult.

    Any help would be great.

    Thanks,
    -bill

    • Bill

      I think I figured it out. In WindowB, the variable should be referenced as “win.foo” rather than just “foo”. I am now able to set a dynamically-created variable in windowB from main.js and then display it in windowB.

      For anyone else who might come across this later…

      in main.js
      —————–
      function openWindowB(e)
      {
      windowA.close();

      windowB.foo = e;
      windowB.open();
      }

      in WindowB
      ——————
      alert(win.foo);

  • http://www.romansanchez.com Roman Sanchez

    Awesome tut! I always come here to learn new stuff. Definitely learned a lot, but what sucks is that the whole titanium platform it too buggy with Android. We might as well be better of writing Java than hacking js for it to work with Android. Otherwise thanks!

  • http://dr-palaniraja.blogspot.com Palaniraja

    First thing I want to convey here is, I was able to follow this series and able to compile app with almost all functionality’s on both iOS & Android platform.

    Sorry to sound like nitpicking guy, but I guess it could help people who just try copy & paste coding to try the tutorial series with success.

    Please edit submit_order.php line no 16 to

    $message .= $names.”<br/ >”;

    Some issues with the textbox background images on Android, Please refer – http://i.imgur.com/RqgnK.png

    And the details of toppings in email content is having some issues ( I’m not good in Java, ca’t fix myself)
    http://i.imgur.com/XITEE.png

    In a nutshell, I recommend people to try this tutorial to learn about Titanium and how to code most used actions of any app, like navigation, http calls etc., with Titanium.

    • John

      I have been unable to get images to display on the “toppings” button. Actually the button doesn’t show up at all. Also the crustHeaderBg.png won’t show up either.

      I have tried many different solutions, but nothing works. I have tried moving the image into the Resources folder, moving the image into a folder that other images are displaying from. I have tried using backgroundImage and image. I have tried converting the image to jpeg.

      Palaniraja could you show your toppings button code that works on Android?

      Here’s what we have that doesn’t work:

      var next = Ti.UI.createButton({
      width:137,
      height:75,
      image:’../images/toppings_next.png’,
      top:385,
      opacity:0
      });

  • http://kerebus.com kalle bertell

    This example creates a new javascript context for each window, which will lead to memory leaks and other bad things. For a coding style which will not crash and burn on memory leaks check out Tweetanium, https://github.com/appcelerator-titans/tweetanium/

  • ahsan

    Hi,
    I am getting an error
    “Error: ConnectionHttp:/\ localhost refused”. Can you please help me.

  • Danny

    Hey,

    thank you very much for this interesting and working Tutorial!!

  • Sonny

    I’ve walked through the tutorial and it looks like I have “clean” code as there are no errors being thrown. Everything works up to the point where the user clicks the Order button. The result I get is different than the implied one. First, I get a Warning in Titanium “[WARN] Exception in event callback. {” and the email message that I get lacks the contact info and pizza crust type and add-ons. Here is an example:

    “A new order has been submitted.

    pizza with:

    I’ve pored over the code and the PHP file to no avail… but it appears to me to be an issue with the orderReq.open line in the Details.js file as the parameters don’t seem to get passed to the PHP script.

    When I open the script in a web browser I see the line “{“mail”:true}” so I’m assuming it’s working as it shoul. Any suggestions to getting it fully functional?

    • Sonny

      Found and fixed my error. It’s working fine now. It tourned out to be a pair of typos associated with the address2 var. Changing the Log level helped.

  • Ibrahim

    Thanks not enough ;)

    really really great job

    ap ap ap appreciate it

    :)

  • Diego

    Excellent tutorial, I wrote nearly everything, with the exception of the PHP Script, which like someone point out, had an error on line 16, that left me scratching my head for a while.

    Great stuff, didn’t fully work with Android, but great for semi-beginner users

  • james priso

    Very good tutorial well done.

  • Paul

    I got it to work in iPad2 (device) running iOS6 and works well for the first pizza order. The images and headers are not placed well on the page, but everything works.

    However, when I try to place a second order, the images are gone on the Details page and the buttons are gone as well.

    On my iPhone 4s running iOS6, the Crust page appears fine, but the Details page is missing the images and the nav buttons right off the bat, which means that you are stuck on the Details page.

    Do you know why that is or how I can solve that problem?

    I’ve tested on all simulators running iOS 6 including the iPhone 5 simulator and everything works beautifully including the PHP script which I have hosted on a server.

    Great tutorial, but this would be really kick ass if it worked on the devices as well!

    Cheers!