Build a Titanium Mobile Pizza Ordering App: Order Form Setup

Build a Titanium Mobile Pizza Ordering App: Order Form Setup

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

Welcome to the third installment in our series demonstrating how to build a pizza ordering application with Titanium Mobile. In this tutorial, we’ll be creating the “Submit Your Order” screen.


Step 1: The Details Window

Now that the user is able to select and deselect toppings, we need to allow the user to actually submit an order. Let’s start by modifying the details click event inside toppings.js:

details.addEventListener('click',function(e){
	var pizzaInfo = [];
	for (var i = 0; i < toppings.length; i++)
	{
		if (toppings[i].container != null)
		{
			pizzaInfo.push(toppings[i].title);
		}
	}
	Ti.App.fireEvent('details',{crust:win.crust,path:win.path,toppings:pizzaInfo});
});

Now when you hit the details button in the toppings window the code above will loop through our giant array of toppings and check the container property for each array item. If the item isn’t null, it will add it to our temp array, called pizzaInfo. After the loop finishes, we will fire a new custom event called details. We will pass three parameters to this event:

  • The selected crust.
  • The image path to the selected crust.
  • The selected toppings (i.e. the temp array called pizzaInfo).

Step 2: Coding the openDetails Event

We need to modify our main.js file to listen for our custom event, so go ahead and open that file now. You are also going to add a new method called openDetails to the code:

var win             = Ti.UI.currentWindow;
//-- Create the sub windows
var crusts          = Ti.UI.createWindow();
var toppings        = Ti.UI.createWindow();
var details         = Ti.UI.createWindow();
//-- We set the background here since this wont change
win.backgroundImage = '../images/bg_main.png';
//-- Include our clock
Ti.include('../includes/clock.js');
//-- This method will close the crusts/details window and open the toppings window
function openToppings(e)
{
	crusts.close();
	toppings.url            = 'toppings.js';
	toppings.crust          = e.crust;
	toppings.path           = e.path;
	toppings.returnToppings = e.toppings;
	toppings.open();
}
//-- The method will close the toppings window and open the crusts window
function openCrust(e)
{
	toppings.close();
	//-- If the event has a crust property, that means the user hit cancel once in the toppings window
	if (e.crust)
	{
		crusts.crust = e.crust;
	}
	crusts.url = 'crusts.js';
	crusts.open();
}
//-- This method will close the toppings window and open the details window
function openDetails(e)
{
	toppings.close();
	details.crust       = e.crust;
	details.path        = e.path;
	details.toppings    = e.toppings;
	details.url         = 'details.js';
	details.open();
}
//-- Have our app listen for our custom events
Ti.App.addEventListener('toppings',openToppings);
Ti.App.addEventListener('cancelToppings',openCrust);
Ti.App.addEventListener('details',openDetails);
openCrust({});

Okay, your main.js file should now match the code above. In the above code, we added a new event listener at the bottom called details and when the app receives that event, we want to call the openDetails method. In the openDetails method, we first close the toppings window. We then set some properties on the details window with the values from the event we passed in toppings.js. We also set the URL property to details.js and finally call the open method.


Step 3: Creating the Details Form

We need to make a new javascript file called details.js and save it in the main_windows folder. What we want to do in this file is add three text fields:

  • Name
  • Address Line 1
  • Address Line 2

NOTE: In a real-world application, we would obviously have more fields, but for the sake of this tutorial we will have only created three fields.

We are also going to have a summary of the pizza the user has ordered with a submit order button. Let's start with the interface for this:

var win      = Ti.UI.currentWindow;
var orderReq = Titanium.Network.createHTTPClient();
//-- Name Text Field
var names = Titanium.UI.createTextField({
	color:'#336699',
	top:100,
	left:10,
	width:300,
	height:40,
	hintText:'Name',
	backgroundImage:'../images/textfield.png',
	paddingLeft:8,
	paddingRight:8,
	keyboardType:Titanium.UI.KEYBOARD_DEFAULT,
	returnKeyType:Titanium.UI.RETURNKEY_NEXT,
	suppressReturn:false
});
//-- Address1 Text Field
var address1 = Titanium.UI.createTextField({
	color:'#336699',
	top:140,
	left:10,
	width:300,
	height:40,
	hintText:'Address 1',
	backgroundImage:'../images/textfield.png',
	paddingLeft:8,
	paddingRight:8,
	keyboardType:Titanium.UI.KEYBOARD_DEFAULT,
	returnKeyType:Titanium.UI.RETURNKEY_NEXT,
	suppressReturn:false
});
//-- Address2 Text Field
var address2 = Titanium.UI.createTextField({
	color:'#336699',
	top:180,
	left:10,
	width:300,
	height:40,
	hintText:'City, State, Zip Code',
	backgroundImage:'../images/textfield.png',
	paddingLeft:8,
	paddingRight:8,
	keyboardType:Titanium.UI.KEYBOARD_DEFAULT,
	returnKeyType:Titanium.UI.RETURNKEY_DEFAULT
});
//-- Listen for the next click on the key board
names.addEventListener('return',function(){address1.focus();});
address1.addEventListener('return',function(){address2.focus();});
win.add(names);
win.add(address1);
win.add(address2);
//-- This method makes a nice formatted summary of the users order
function getFormattedPizza()
{
	var text = win.crust + ' pizza with:\n';
	if (win.toppings.length == 0)
	{
		text += '&bull; Plain (cheese pizza)\n';
	}
	else
	{
		for (var i = 0; i < win.toppings.length; i++)
		{
			text += '&bull; ' + win.toppings[i] + '\n';
		}
	}
	return text;
}
//-- Are formatted text field
var pizzaInfoText = Ti.UI.createLabel({
	text:getFormattedPizza(),
	font:{
		fontFamily:'Verdana',
		fontSize:14
	},
	color:'#fff',
	shadowColor:'#333',
	shadowOffset:{x:1,y:1},
	textAlign:'left',
	width:Ti.Platform.displayCaps.platformWidth,
	height:160,
	top:210,
	left:10
});
win.add(pizzaInfoText);
//-- Order Button
var order = Ti.UI.createButton({
	width:137,
	height:75,
	backgroundImage:'../images/order.png',
	top:385,
	left:165,
	opacity:0
});
//-- Cancel Button
var cancel = Ti.UI.createButton({
	width:137,
	height:75,
	backgroundImage:'../images/cancel.png',
	top:385,
	left:10,
	opacity:0
});
//-- If android OS, use the image property instead of backgroundImage (Ti SDK bug)
if (Ti.Platform.osname == 'android')
{
	order.image = '../images/order.png';
	cancel.image = '../images/cancel.png';
}
win.add(order);
win.add(cancel);
//-- Fade the order button in
order.animate({
	opacity:1,
	duration:500
});
//-- Fade the cancel button in
cancel.animate({
	opacity:1,
	duration:500
});

The above block of code may look scary, but really it is quite simple. We start by defining our win variable as well as our orderReq variable. The orderReq variable will handle our request out to our PHP file which we will cover in the next tutorial of this series.

We then define our three text fields and give them some simple properties. We add a return event listener on the text fields so when you hit next on the keyboard, it jumps to the next text field. We make a label called pizzaInfoText and set its text property to our getFormattedPizza method. This method will return a formatted list of our chosen crust and toppings. If the user selected no toppings, we will display the crust type and a simple cheese pizza. We've also made our order and cancel buttons and faded them in. Your interface should look like this now:


Step 4: Coding the Cancel Button

So you're in the submit order screen and you decide you want to remove mushrooms from your topping list. Well, no problem! The app already knows the toppings you currently have selected, so we will simply pass that temp array back to toppings.js and recheck the toppings. We first need to add an event listener to our cancel button. Scroll to the bottom of your details.js file and add this:

//-- Cancel button event. Goes back to the toppings window and remembers the users selections
cancel.addEventListener('click',function(){
	Ti.App.fireEvent('cancelDetails',{crust:win.crust,path:win.path,toppings:win.toppings});
});

We are firing yet another custom event, this time called cancelDetails, and again we pass three parameters:

  • The selected crust.
  • The image path to the selected crust.
  • The selected toppings (i.e. our temp array).

Step 5: Code the cancelDetails Event

Let's go back to main.js. We need to add a new event listener. Add the following to the end of our event listeners.

Ti.App.addEventListener('cancelDetails',openToppings);
</pre>
<p> Now we already have an <code>openToppings</code> method. We do however need to modify it. </p>
<pre name="code" class="javascript">
//-- This method will close the crusts/details window and open the toppings window
function openToppings(e)
{
	if (e.toppings)
	{
		details.close();
	}
	else
	{
		crusts.close();
	}
	toppings.url            = 'toppings.js';
	toppings.crust          = e.crust;
	toppings.path           = e.path;
	toppings.returnToppings = e.toppings;
	toppings.open();
}

So, with our modified method, we do a check for the toppings property tied to the event. If it isn't null, we want to close the details window. If it is null, we want to close the crust window. We still add our custom properties and then open our toppings window.


Step 6: Checkbox Preselection

When we go back we want to preselect the checkboxes of the topping we previously chose. We also want to add the toppings to the pizza visually. Open up the toppings.js file and scroll down to the createToppingsList method. The difference between your current one and the one below is if win.returnToppings isn't null, it will loop through our larger toppings array and compare it to our temp array. If they match up, recheck the checkbox, add the visual to the crust, and increase our toppings count.

/*
This method creates the topping list. Since iOS doesn't have checkmark components, I made my own using a view, a button and swapping out the background image
*/
function createToppingsList()
{
	scrollView.opacity = 0;
	scrollView.top = 155;
	scrollView.height = 120;
	scrollView.contentWidth = Ti.Platform.displayCaps.platformWidth;
    scrollView.contentHeight = 'auto';
    scrollView.showVerticalScrollIndicator = true;
    win.add(scrollView);
	for (i = 0; i < toppings.length; i++)
	{
		//-- The label
		var toppingLabel = Ti.UI.createLabel({
			text:toppings[i].title,
			font:{
				fontFamily:'Verdana',
				fontWeight:'bold',
				fontSize:14
			},
			color:'#fff',
			shadowColor:'#333',
			shadowOffset:{x:1,y:1},
			textAlign:'left',
			width:Ti.Platform.displayCaps.platformWidth - 10,
			left:10
		});
		//-- We add a custom property 'selected' to our checkbox view
		var checkbox = Ti.UI.createView({
			width:340,
			height:16,
			backgroundImage:'../images/checkbox_no.png',
			selected:false,
			toppingID:i
		});
		//-- if the user hits cancel in the details window, we go back and repopulate the list with previously checked toppings
		if (win.returnToppings)
		{
			for (j = 0; j < win.returnToppings.length; j++)
			{
				if (win.returnToppings[j] == toppings[i].title)
				{
					var aTopping = Ti.UI.createView({
						backgroundImage:toppings[i].path
					});
					if (Ti.Platform.osname == 'android')
					{
						aTopping.image = toppings[i].path;
					}
					else
					{
						aTopping.opacity = 0;
						aTopping.animate({
							opacity:1,
							duration:500
						});
					}
					toppingsHolder.add(aTopping);
					toppings[i].container = aTopping;
					checkbox.backgroundImage = '../images/checkbox_yes.png';
					checkbox.selected = true;
					numToppings += 1;
				}
			}
		}
		var toggler = Ti.UI.createView({
			width:Ti.Platform.displayCaps.platformWidth,
			height:20,
			top: i * 20
		});
		//-- We use the singletap event rather than the click since its in a scroll view
		checkbox.addEventListener('singletap',toppingListClick);
		toggler.add(toppingLabel);
		toggler.add(checkbox);
		scrollView.add(toggler);
	}
	scrollView.animate({
		opacity:1,
		duration:500
	});
}

Conclusion

In this tutorial we created the "Submit Oder" screen that is the last screen we will need in this tutorial series. We also added two more custom events to our app that allowed us to jump between the "Choose Crust" and "Submit Order" screens.

The next part of this series will go over doing the form authentication necessary to handle submissions and then e-mailing the order chosen along with the customer information submitted (a web server with a mail client and PHP installed will be required in order to send the e-mail order notification).

Series Navigation«Build a Titanium Mobile Pizza Ordering App: Topping SelectionBuild a Titanium Mobile Pizza Ordering App: Order Completion»

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
  • Ed

    Bravo Ronnie – this series was excellent and enjoyed going through every bit of it!!!

    The article and demo app were both well thought out, concise and relevant.

    Hope you do some more of these types of series!

    Ed

    • http://ronnieswietek.com Ronnie Swietek
      Author

      Thank Ed. I’m glad you liked this series. I too like the idea of making demo apps and even though they most likely aren”t going to be used, the demo app utilizes many different skills. More of understanding the concept and ideas to get your app off the ground.

  • http://ivani.co Ivan Frantar

    Great tutorial and hoping to see more coming ;)

  • Robert

    Ronnie, is the source code of your tutorial in a public repository?

    • http://ronnieswietek.com Ronnie Swietek
      Author

      Hey Robert, at the top of each tutorial is a link to download the source files.

  • paul

    very nice tutorial. Really a good way to understand the concepts…

  • http://in.linkedin.com/in/santoshbhandarkarindia Santosh Bhandarkar

    This is a great tutorial. I have one observation – maybe it’s just with my development environment. (building app for android).

    In the code snippet –

    //– Cancel button event. Goes back to the toppings window and remembers the users selections
    cancel.addEventListener(‘click’,function(){
        Ti.App.fireEvent(‘cancelDetails’,{crust:win.crust,path:win.path,toppings:win.toppings});
    });

    I got an compiler error and just could not figure out for a few hours… In fact to drill down to this level was a pain as the errors thrown by Titanium Developer is so weird. (some parser error with unknown token found or something)

    I had to join remove the line breaks (shown below) and then it worked. I wonder if there some problem with the editor at my end.:

    cancel.addEventListener(‘click’,function(){Ti.App.fireEvent(‘cancelDetails’,{crust:win.crust,path:win.path,toppings:win.toppings});});

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

    A correction in Step 5.

    I guess the syntax highlighter plugin is behaving weird. I don’t see code formatted correctly with comments, refer my comment on part 1 of this series.

    Btw, I’m closing in on the tutorial, can’t wait to try my concept with Titanium after completing this. Thanks once again for the tutorial.

  • Philipp

    Very great tutorial series.

    I have one problem with formatting the text of a label.
    Titanium doesn’t seem to interpret html code like “•”, it just prints it out on the app. So I replaced it by the unicode character code \u2022 which works fine.
    Is there a way to tell titanium that the text contains html code?