Shared posts

21 Oct 03:37

Solutions to WordPress eCommerce Challenges

by Patrick Mwachugu

In the previous part of this series, we looked at some of the challenges come up when implementing an eCommerce solution using WordPress themes and plugins.

In this particular post, I intend to offer solutions to each of the aforementioned challenges with the ultimate goal being that implementing eCommerce in your WordPress setup will be a joyful experience.

Let's begin.

WordPress Automatic Versus Manual Updates

One of the problems that we've previous discussed is how automatic WordPress updates can negatively affect plugins that are installed on the current site.

From WordPress version 3.7 forward, WordPress core allows minor security updates to be performed automatically and the major core to be done manually by the user via the dashboard

By editing the wp-config.php on the root folder of your website file, and inserting the following lines of code you can choose to either have updates be handled automatically or update them manually.

In each case below, you will have to choose true or false depending with the result you wish to obtain

  • This particular line of code will enable or disable major core updates of the WordPress core define( 'WP_AUTO_UPDATE_CORE', true );
  • In case you need the plugins  to update automatically you insert the following line in wp-config.php  add_filter( 'auto_update_plugin', '__return_true' );
  • If you are using a theme downloaded directly from the WordPress repository, you can add the following line to enable automatic updates:  add_filter ( 'auto_update_theme','__return_true' );

Themes and plugins are an integral part of your website, so always keep tabs with the developers to check compatibility of the theme and/or plugins installed with the new WordPress update.

In case you're required to modify a theme extensively, always use a child theme. This way, when a developer updates a theme, the website will be minimally affected.

Please note that WordPress security updates are essential to your website. Each update fixes a particular issue in the application, so I highly recommend leaving the default settings.

Website Security

Since most WordPress eCommerce sites use third-party payment methods and merchants to handle cash transactions for the site, it is important to implement security measures that will protect users usernames and passwords when interacting with your site during the shopping process.

Fortunately, the WordPress plugin repository has a number of security plugins that can help with that. The following are some of the security features you should look out for in a WordPress security plugin:

1. User Login and Account Security

A good security plugin should be able to protect against a brute force attack with a lock-down feature, a monitor, and should enforce a limited number of login attempts. It should also use a password strength tool to check users passwords.

2. File-System Security

Folder permissions are a very important part of any WordPress website regardless of it's an eCommerce site or not. It is essential a plugin to aid in checking the relevant  file permissions and correct them accordingly, another feature along with permissions, is the ability to back-up your original .htaccess and wp-config.php files.

3. Database Security

A good security plugin should enable you to change the default prefix of the database to a value of your choice and schedule automatic backups of your site. This will protect the site from hackers who can sneak through the server to collect data from the database.

4. Firewall Security

Firewall security involves protecting the site against WordPress pingback vulnerabilities by denying bad or malicious query strings, protecting the site against cross site scripting, and other preventive measures that will help block malicious attackers from gaining access to your website.

Website security is not reserved just for eCommerce sites, but for all websites. Choosing the right security plugin will go a long way to ensuring that your site serves its intended purpose and does so securely. 

Some of the security plugins I recommend include:

Variety of WordPress eCommerce Solutions

It can be very difficult to find one plugin solution that will offer all the features you need, but again there quite a number of them that offer exceptional features that meet various criteria of different eCommerce sites. 

In a previous post, Factors to Consider in WordPress eCommerce plugins, we covered some of the essential factors one needs to consider when choosing an eCommerce plugin. We also did a comparison of the different features each plugin has, and what plugin best works for what type of eCommerce site.

As you review that post, it's important to mention that different eCommerce sites require different solutions. Because of that, it is up to you as the site owner to look at what your website needs, the products and services your are offering, and the features of each plugin and select one that best meets your requirements.

Website Scaling and Growth

Most of the popular WordPress eCommerce plugins, such as WooCommerce  and Easy Digital Downloads, take advantage of built-in WordPress custom post types meaning they interact with the site database just like any other WordPress installation. 

Heavy traffic is something that most websites are not always prepared to handle, in most cases you will find a site moving from a shared hosting, to virtual private server, and finally to a dedicated server.

As painful as it may be to hear, opting for a new web host plan may be essential as your eCommerce site grows and continues to receive heavy traffic, but this is a good problem to have as it indicates the growth of your site.

The WordPress Plugin Repository has a few plugins that can come in handy when it comes to website catching. One that I highly recommend is W3 Total Cache. In this particular series, Configuring W3 Total Cache, Ahmad Awais explains in simple steps how to best configure this plugin. Together with W3 Total Cache, varnish cache can be used to help store pre-built pages in memory and serve them quickly.

Summary

In this post, we highlighted a number of solutions to the problems we mentioned in the first part of this series. It is essential you come up with a strategy of how to handle WordPress core, theme, and plugin updates, implement effective strategies when it comes to your website security and scaling and finally analyzing your site requirements before choosing an eCommerce plugin.

I highly recommend you read through the various tutorials mentioned and all the related tutorials to gather all the necessary knowledge you require to build your next eCommerce site 

In the mean time, feel free to leave any questions or comments below!

13 Feb 22:19

Creating A Client-Side Shopping Cart

by Gabriele Romanato

  

Session storage is a new feature introduced by the W3C’s “Web Storage” specification. It’s supported in Internet Explorer 8+, Firefox, Chrome, Safari and Opera Desktop (for a complete list, please consult “Can I Use”). In this series of articles, we’ll cover in depth a practical implementation of session storage by creating a complete e-commerce shopping cart with the sessionStorage object and jQuery.

Bear in mind that, in these articles, I’m not going to propose a new technique to replace existing server-side techniques, but rather just a proof of concept of session storage.

Session Storage: A Quick Reminder

We use sessions to store data and share such data across several pages. Usually, a user would pick a product, and we’d save the product’s name along with the chosen quantity and price.

Then, the user would fill out a form with their personal information, and we’d save it in the current session before the end of the process, which is typically the checkout page and the subsequent redirection to the payment gateway (for example, PayPal).

How are shopping carts built? PHP, for instance, makes frequent use of associative arrays to create the basic structure of a shopping cart. Associative arrays enable PHP Web developers to keep session data structured and organized.

JavaScript sessions work differently. Generally, a session expires when the user closes their browser (but bear in mind that the concept of “closing a browser” is not clear on mobile devices). When a session expires, all data stored in the session storage of a Web browser is removed. There’s no need to explicitly initialize a session because in JavaScript a session takes the form of the global sessionStorage object and is always present. It’s up to us to write data into the current session.

Session data comes in the form of key-value pairs, and the value of each key may contain only strings. To write data, we can use the sessionStorage.setItem( name, value ) method:


sessionStorage.setItem( "total", 120 );

In this case, the key named total now contains the value 120 as a string, although we’ve used an integer in our call to the .setItem() method. This value will be available until the session expires, unless we use sessionStorage.removeItem( "total" ) to remove the named key or we call sessionStorage.clear() to entirely remove all keys and values from the session storage.

Note that when a key doesn’t exist in session storage, its value is always null. Then, when we remove a key from session storage and try again to get its value, we’d simply get null.

As you may have guessed, our key now is always available, even as the user navigates the pages of our website. To get its value, we simply write the following:


var total = sessionStorage.getItem( "total" );
console.log( total ); // '120', a string

We can also update its value by using sessionStorage.setItem() again with a new value:


var total = parseInt( sessionStorage.getItem( "total" ) );
var quantity = 2;
var updatedTotal = total * quantity;
sessionStorage.setItem( "total", updatedTotal ); // '240', a string

Now, the key named total has a value of 240 with our last update. Why did we call parseInt()? This is a simple technique to convert a numerical string into a true number, ensuring that our calculation will be consistent. Remember that all values in session storage are strings, and our calculations must be between only numbers.

But wait! What about objects? Objects may be stored in session storage by first turning them into JSON strings (with JSON.stringify()) and then back into JavaScript objects (with JSON.parse()):


var cart = {
	item: "Product 1",
	price: 35.50,
	qty: 2
};
var jsonStr = JSON.stringify( cart );
sessionStorage.setItem( "cart", jsonStr );
// now the cart is {"item":"Product 1","price":35.50,"qty":2}
var cartValue = sessionStorage.getItem( "cart" );
var cartObj = JSON.parse( cartValue );
// original object

To update our object, we simply extend it and then repeat the procedure above.

Security Considerations

Security is important. If we read the security notes of the W3C’s specification, then we’d be aware of the security risks of even a client-side technology such as Web storage.

The US Computer Emergency Readiness Team’s technical paper on website security (PDF) clearly states:

“Every community organization, corporation, business, or government agency relies on an outward-facing website to provide information about themselves, announce an event, or sell a product or service. Consequently, public-facing websites are often the most targeted attack vectors for malicious activity.”

Even if a browser session ends when the browser itself is closed, malicious attacks can still take place, especially if the browser has been compromised by certain exploits. Moreover, compromised websites can often be used to spread malware that targets particular browsers.

For this reason, make sure your website is safe before relying on any technique to store data in the browser. Keeping a website safe is beyond the scope of this article, but by simply following security best practices, you should be able to benefit from Web storage without worrying too much about its security implications.

Our Sample Project: Winery

Our sample project is an online store that sells wine. It’s a simple e-commerce website whose only complication is in how its shipping charges are calculated.

In short, wines are sold in packages of six bottles. This means that the total quantity of bottles sold must always be in multiples of six. Shipping charges are calculated, then, according to the total quantity of bottles sold.

Our store will rely on PayPal, so we’ll have to create a Business account in PayPal Sandbox to test our code.

The user may add and remove products from their shopping cart, update the cart, change the quantity of each product, and empty the cart. They have to fill a form with their contact information, specifying whether their billing address is the same as their shipping address.

Before being redirected to PayPal, the user will see a summary page with their personal data, their cart, and the cart’s total price plus shipping charges.

After completing their purchase, the user should be redirected back to our website. This is the only step of the process that we can’t handle only with JavaScript. PayPal will send back various data over an HTTP request that has to be processed with a server-side language (such as PHP). If you need more information to get started with this kind of processing, please consult PayPal’s tutorial.

HTML Structure

Our project is made up of the following sections:

  • index.html
    This contains the list from which users may add products to their shopping cart, specifying the quantity for each product.
  • cart.html
    This is the shopping cart page where users may update or empty their cart. Alternatively, they can go back to the main page to continue shopping or proceed to the checkout page.
  • checkout.html
    On this page, users fill out a form with their personal information — specifically, their billing and shipping addresses.
  • order.html
    This page contains a brief summary of the user’s order plus the PayPal form. Once a user submits the form, they will be redirected to PayPal’s landing page.

We’ll go over the markup for this project in the following sections.

index.html

The main components of this page are the forms that enable the user to add products to their shopping cart.


Wine #1

€ 5

Quantity

The data attributes used here for storing product names and prices can be accessed via jQuery using the .data() and $.data() methods.

cart.html

Our shopping cart page is made up of three components: a table with the product’s information, an element that displays the subtotal, and a list
of cart actions.



	
Item Qty Price

Sub Total:

The table contained in this page is empty, and we’ll fill it with data via JavaScript. The element that displays the subtotal works just as a placeholder for JavaScript. The first two actions, “Update Cart” and “Empty Cart,” will be handled by JavaScript, while the latter two actions are just plain links to the product’s list page and the checkout page, respectively.

checkout.html

This page has four components:

  • a table that shows the ordered items (the same table shown earlier in the shopping cart section), plus the final price and shipping charges;
  • a form in which the user must fill in their billing details;
  • a form with shipping information;
  • a checkbox to enable the user to specify that their billing details are the same as their shipping details.

Item Qty Price

Shipping:

Total:

Your Details

Billing
Name
Email
City
Address
ZIP Code
Country SelectUSAItaly
Same as Billing
Shipping

Data attributes are used here for validation. The data-type attribute specifies the type of data we’re validating, and data-message contains the error message to be shown in case of failure.

I didn’t use the email validation built into Web browsers just for the sake of simplicity, but you could use it if you want.

order.html

This final page contains a brief recap of the user’s order, their details and the PayPal form.


Your Order

Item Qty Price

Shipping:

Total:

Your Data

The PayPal form and other elements of this page are initially empty, except for those fields that don’t need to be generated dynamically.

JavaScript Code

The CSS layout of this project will have no actual influence on the goal we want to achieve. Even if we disabled CSS entirely, the project would continue to function, thanks to the strong relationship between the HTML’s structure and the JavaScript’s behavior.

We’ll use an object-oriented approach because of the complexity of our goals. Our object will be based on a simple constructional pattern and will use both private and public methods.

Object Structure

Our object has a very simple structure. The constructor function both initializes the top-level element that wraps our DOM’s entire structure and invokes the initialization method.


(function( $ ) {
	$.Shop = function( element ) {
		this.$element = $( element ); // top-level element
		this.init();
	};

	$.Shop.prototype = {
		init: function() {
			// initializes properties and methods
		}
	};

	$(function() {
		var shop = new $.Shop( "#site" ); // object's instance
	});

})( jQuery );

The object’s instance is created when the DOM is ready. We can test that everything has worked fine as follows:


$(function() {
	var shop = new $.Shop( "#site" );
	console.log( shop.$element );
});

This outputs the following:


x.fn.x.init[1]
	0: div#site
	context: document
	length: 1
	selector: "#site"

Now that we know our object has been instantiated correctly, we can define its properties.

Object Properties

The properties of our object break down into two categories: first, the properties for handling calculations, forms and validation, and secondly, the references to HTML elements.


$.Shop.prototype = {
	init: function() {
		// Properties

			this.cartPrefix = "winery-"; // prefix string to be prepended to the cart's name in session storage
			this.cartName = this.cartPrefix + "cart"; // cart's name in session storage
			this.shippingRates = this.cartPrefix + "shipping-rates"; // shipping rates key in session storage
			this.total = this.cartPrefix + "total"; // total key in the session storage
			this.storage = sessionStorage; // shortcut to sessionStorage object


			this.$formAddToCart = this.$element.find( "form.add-to-cart" ); // forms for adding items to the cart
			this.$formCart = this.$element.find( "#shopping-cart" ); // Shopping cart form
			this.$checkoutCart = this.$element.find( "#checkout-cart" ); // checkout form cart
			this.$checkoutOrderForm = this.$element.find( "#checkout-order-form" ); // checkout user details form
			this.$shipping = this.$element.find( "#sshipping" ); // element that displays the shipping rates
			this.$subTotal = this.$element.find( "#stotal" ); // element that displays the subtotal charges
			this.$shoppingCartActions = this.$element.find( "#shopping-cart-actions" ); // cart actions links
			this.$updateCartBtn = this.$shoppingCartActions.find( "#update-cart" ); // update cart button
			this.$emptyCartBtn = this.$shoppingCartActions.find( "#empty-cart" ); // empty cart button
			this.$userDetails = this.$element.find( "#user-details-content" ); // element that displays the user's information
			this.$paypalForm = this.$element.find( "#paypal-form" ); // PayPal form


			this.currency = "€"; // HTML entity of the currency to be displayed in layout
			this.currencyString = "€"; // currency symbol as text string
			this.paypalCurrency = "EUR"; // PayPal's currency code
			this.paypalBusinessEmail = "yourbusiness@email.com"; // your PayPal Business account email address
			this.paypalURL = "https://www.sandbox.paypal.com/cgi-bin/webscr"; // URL of the PayPal form

			// object containing patterns for form validation
			this.requiredFields = {
				expression: {
					value: /^([\w-\.]+)@((?:[\w]+\.)+)([a-z]){2,4}$/
				},

				str: {
					value: ""
				}

			};

			// public methods invocation
	}
};

Let’s go over these properties one by one.

Storage and other properties:

  • cartPrefix
    A prefix to be prepended to the cart’s name key in session storage
  • cartName
    The cart’s name key in session storage (combines the cartPrefix string with the cart string)
  • shippingRates
    The shipping rate key in session storage
  • total
    The total’s key in session storage
  • storage
    Shortcut to the sessionStorage object.
  • currency
    An HTML entity used to display the current currency in the layout
  • currencyString
    The current currency symbol used in the element’s text
  • paypalCurrency
    PayPal’s currency text code
  • paypalBusinessEmail
    The email address of your PayPal Business account
  • paypalURL
    The URL of PayPal’s form (defaults to the URL of PayPal Sandbox)
  • requiredFields
    An object containing the patterns and rules for form validation

References to elements:

  • $formAddToCart
    The forms for adding products to the shopping cart
  • $formCart
    The shopping cart form
  • $checkoutCart
    The checkout’s shopping cart form
  • $checkoutOrderForm
    The checkout’s form where users input their personal information
  • $shipping
    The element that contains and displays shipping rates
  • $subTotal
    The element that contains and displays the total charges
  • $shoppingCartActions
    The elements that contain the actions related to the shopping cart
  • $updateCartBtn
    The button to update the shopping cart
  • $emptyCartBtn
    The button for emptying the cart
  • $userDetails
    The element that contains and displays the information entered by the user
  • $paypalForm
    PayPal’s form

All of the elements are prefixed with the $ sign, meaning that they’re jQuery objects. But not all of these elements are available on all pages. To check whether a jQuery element exists, simply test its length property:


if( $element.length ) {
	// the element exists
}

Another approach, not used in our project, is to add a particular ID or class to the body element and perform actions conditionally:


var $body = $( "body" ),
	page = $body.attr( "id" );

	switch( page ) {
		case "product-list":
			// actions for handling products
			break;
		case "shopping-cart":
			// actions for handling the shopping cart
			break;
		case "checkout":
			// actions for handling the checkout's page
			break;
		default:
			break;
	}

Object Methods

The actions of our code take place in our object’s methods, which, in turn, can be divided into public and private methods. Private methods operate in the background, so to speak, and help the public methods perform their tasks. These methods are prefixed with an underscore and are never used directly.

Public methods, meanwhile, operate directly on page elements and data, and they’re unprefixed. We’ve already seen the init() method, which simply initializes properties and other public methods in the object’s constructor function. The other methods will be explained below.

Private Methods (Helpers)

The first private method, _emptyCart(), simply empties the current session storage in the browser:


$.Shop.prototype = {
	// empties session storage

	_emptyCart: function() {
		this.storage.clear();
	}
};

To format a number by a set number of decimal places, we implement the _formatNumber() method:


/* Format a number by decimal places
 * @param num Number the number to be formatted
 * @param places Number the decimal places
 * @returns n Number the formatted number
*/


_formatNumber: function( num, places ) {
	var n = num.toFixed( places );
	return n;
}

This method makes use of JavaScript’s toFixed() method of the Number object. Its role in our project is to properly format prices.

Because not all of the prices in our pages are contained in data attributes, we need a specialized method to extract the numeric portion of a string from text nodes. This method is named _extractPrice():


/* Extract the numeric portion from a string
 * @param element Object the jQuery element that contains the relevant string
 * @returns price String the numeric string
 */


_extractPrice: function( element ) {
	var self = this;
	var text = element.text();
	var price = text.replace( self.currencyString, "" ).replace( " ", "" );
	return price;
}

Above, self is a reference to the $.Shop object, and we’ll need it every time we want to access a property or a method of our object without worrying much about scope.

You can bulletproof this method by adding a further routine that strips out all trailing white space:


var text = $.trim( element.text() );

Bear in mind that jQuery’s $.trim() method removes all new lines, spaces (including non-breaking spaces) and tabs from the beginning and end of a string. If these white space characters occur in the middle of a string, they are preserved.

Then, we need two methods to convert strings into numbers and numbers into strings. This is necessary to perform calculations and to display the results on our pages.


/* Converts a numeric string into a number
 * @param numStr String the numeric string to be converted
 * @returns num Number the number, or false if the string cannot be converted
 */

_convertString: function( numStr ) {
	var num;
	if( /^[-+]?[0-9]+\.[0-9]+$/.test( numStr ) ) {
		num = parseFloat( numStr );
	} else if( /^\d+$/.test( numStr ) ) {
		num = parseInt( numStr );
	} else {
		num = Number( numStr );
	}

	if( !isNaN( num ) ) {
		return num;
	} else {
		console.warn( numStr + " cannot be converted into a number" );
		return false;
	}
},

/* Converts a number to a string
 * @param n Number the number to be converted
 * @returns str String the string returned
 */

_convertNumber: function( n ) {
	var str = n.toString();
	return str;
}

Above, _convertString() runs the following tests:

  1. Does the string have a decimal format? If so, it uses the parseFloat() function.
  2. Does the string have an integer format? If so, it uses the parseInt() function.
  3. If the format of the string cannot be detected, it uses the Number() constructor.
  4. If the result is a number (tested with the isNaN() function), it returns the number. Otherwise, it outputs a warning to the JavaScript console and returns false.

By contrast, _convertNumber() simply invokes the toString() method to convert a number into a string.

The next step is to define two methods to convert a JavaScript object into a JSON string and a JSON string back into a JavaScript object:


/* Converts a JSON string to a JavaScript object
 * @param str String the JSON string
 * @returns obj Object the JavaScript object
 */

_toJSONObject: function( str ) {
	var obj = JSON.parse( str );
	return obj;
},

/* Converts a JavaScript object to a JSON string
 * @param obj Object the JavaScript object
 * @returns str String the JSON string
 */


_toJSONString: function( obj ) {
	var str = JSON.stringify( obj );
	return str;
}

The first method makes use of the JSON.parse() method, while the latter invokes the JSON.stringify() method (see Mozilla Developer Network’s article on “Using Native JSON”).

Why do we need these methods? Because our cart will also store the information related to each product using the following data format (spaces added for legibility):

Key Value
winery-cart { "items": [ { "product": "Wine #1", "qty": 5, "price": 5 } ] }

The winery-cart key contains a JSON string that represents an array of objects (i.e. items) in which each object shows the relevant information about a product added by the user — namely, the product’s name, the quantity and the price.

It’s pretty obvious that we also now need a specialized method to add items to this particular key in session storage:


/* Add an object to the cart as a JSON string
 * @param values Object the object to be added to the cart
 * @returns void
 */


_addToCart: function( values ) {
	var cart = this.storage.getItem( this.cartName );
	var cartObject = this._toJSONObject( cart );
	var cartCopy = cartObject;
	var items = cartCopy.items;
	items.push( values );

	this.storage.setItem( this.cartName, this._toJSONString( cartCopy ) );
}

This method gets the cart’s key from session storage, converts it to a JavaScript object and adds a new object as a JSON string to the cart’s array. The newly added object has the following format:


this._addToCart({
	product: "Test",
	qty: 1,
	price: 2
});

Now, our cart key will look like this:

Key Value
winery-cart { "items": [ { "product": "Wine #1", "qty": 5, "price": 5 }, { "product": "Test", "qty": 1, "price": 2 } ] }

Shipping is calculated according to the overall number of products added to the cart, not the quantity of each individual product:


/* Custom shipping rates calculated based on total quantity of items in cart
 * @param qty Number the total quantity of items
 * @returns shipping Number the shipping rates
 */

_calculateShipping: function( qty ) {
	var shipping = 0;
	if( qty >= 6 ) {
		shipping = 10;
	}
	if( qty >= 12 && qty = 30 && qty  60 ) {
		shipping = 0;
	}

	return shipping;

}

You can replace this method’s routines with your own. In this case, shipping charges are calculated based on specific amounts.

We also need to validate the checkout form where users insert their personal information. The following method takes into account the special visibility toggle by which the user may specify that their billing information is the same as their shipping information.


/* Validates the checkout form
 * @param form Object the jQuery element of the checkout form
 * @returns valid Boolean true for success, false for failure
 */


_validateForm: function( form ) {
		var self = this;
		var fields = self.requiredFields;
		var $visibleSet = form.find( "fieldset:visible" );
		var valid = true;

		form.find( ".message" ).remove();
			
	$visibleSet.each(function() {

		$( this ).find( ":input" ).each(function() {
		var $input = $( this );
		var type = $input.data( "type" );
		var msg = $input.data( "message" );
				
		if( type == "string" ) {
			if( $input.val() == fields.str.value ) {
				$( "" ).text( msg ).
				insertBefore( $input );

				valid = false;
			}
		} else {
			if( !fields.expression.value.test( $input.val() ) ) {
				$( "" ).text( msg ).
				insertBefore( $input );

				valid = false;
			}
		}

	});
	});

	return valid;
}

When validation messages are added upon the form being submitted, we need to clear these messages before going any further. In this case, we take into account only the fields contained in a fieldset element that is still visible after the user has checked the visibility toggle.

Validation takes place by checking whether the current field requires a simple string comparison (data-type="string") or a regular expression test (data-type="expression"). Our tests are based on the requiredFields property. If there’s an error, we’ll show a message by using the data-message attribute of each field.

Note that the validation routines used above have been inserted just for demonstration purposes, and they have several flaws. For better validation, I recommend a dedicated jQuery plugin, such as jQuery Validation.

Last but not least is registering the information that the user has entered in the checkout form:


/* Save the data entered by the user in the checkout form
 * @param form Object the jQuery element of the checkout form
 * @returns void
 */


_saveFormData: function( form ) {
	var self = this;
	var $visibleSet = form.find( "fieldset:visible" );
			
	$visibleSet.each(function() {
		var $set = $( this );
		if( $set.is( "#fieldset-billing" ) ) {
			var name = $( "#name", $set ).val();
			var email = $( "#email", $set ).val();
			var city = $( "#city", $set ).val();
			var address = $( "#address", $set ).val();
			var zip = $( "#zip", $set ).val();
			var country = $( "#country", $set ).val();

			self.storage.setItem( "billing-name", name );
			self.storage.setItem( "billing-email", email );
			self.storage.setItem( "billing-city", city );
			self.storage.setItem( "billing-address", address );
			self.storage.setItem( "billing-zip", zip );
			self.storage.setItem( "billing-country", country );
		} else {
			var sName = $( "#sname", $set ).val();
			var sEmail = $( "#semail", $set ).val();
			var sCity = $( "#scity", $set ).val();
			var sAddress = $( "#saddress", $set ).val();
			var sZip = $( "#szip", $set ).val();
			var sCountry = $( "#scountry", $set ).val();

			self.storage.setItem( "shipping-name", sName );
			self.storage.setItem( "shipping-email", sEmail );
			self.storage.setItem( "shipping-city", sCity );
			self.storage.setItem( "shipping-address", sAddress );
			self.storage.setItem( "shipping-zip", sZip );
			self.storage.setItem( "shipping-country", sCountry );

		}
	});
}

Again, this method takes into account the visibility of the fields based on the user’s choice. Once the form has been submitted, our session storage may have the following details added to it:

Key Value
billing-name John Doe
billing-email jdoe@localhost
billing-city New York
billing-address Street 1
billing-zip 1234
billing-country USA

Public Methods

Our public methods are invoked in the initialization method (init()). The first thing to do is create the initial keys and values in session storage.


// Creates the cart keys in session storage

createCart: function() {
	if( this.storage.getItem( this.cartName ) == null ) {

		var cart = {};
		cart.items = [];
			
		this.storage.setItem( this.cartName, this._toJSONString( cart ) );
		this.storage.setItem( this.shippingRates, "0" );
		this.storage.setItem( this.total, "0" );
	}
}

The first check tests whether our values have already been added to session storage. We need this test because we could actually overwrite our values if we run this method every time a document has finished loading.

Now, our session storage looks like this:

Key Value
winery-cart {“items”:[]}
winery-shipping-rates 0
winery-total 0

Now, we need to handle the forms where the user may add products to their shopping cart:


// Adds items to shopping cart

handleAddToCartForm: function() {
	var self = this;
	self.$formAddToCart.each(function() {
		var $form = $( this );
		var $product = $form.parent();
		var price = self._convertString( $product.data( "price" ) );
		var name =  $product.data( "name" );

		$form.on( "submit", function() {
			var qty = self._convertString( $form.find( ".qty" ).val() );
			var subTotal = qty * price;
			var total = self._convertString( self.storage.getItem( self.total ) );
			var sTotal = total + subTotal;
			self.storage.setItem( self.total, sTotal );
			self._addToCart({
				product: name,
				price: price,
				qty: qty
			});
			var shipping = self._convertString( self.storage.getItem( self.shippingRates ) );
			var shippingRates = self._calculateShipping( qty );
			var totalShipping = shipping + shippingRates;

			self.storage.setItem( self.shippingRates, totalShipping );
		});
	});
}

Every time a user submits one of these forms, we have to read the product quantity specified by the user and multiply it by the unit price. Then, we need to read the total’s key contained in session storage and update its value accordingly. Having done this, we call the _addToCart() method to store the product’s details in storage. The quantity specified will also be used to calculate the shipping rate by comparing its value to the value already stored.

Suppose that a user chooses the first product, Wine #1, whose price is €5.00, and specifies a quantity of 5. The session storage would look like this once the form has been submitted:

Key Value
winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5}]}
winery-shipping-rates 0
winery-total 25

Suppose the same user goes back to the product list and chooses Wine #2, whose price is €8.00, and specifies a quantity of 2:

Key Value
winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5},{"product":"Wine #2","price":8,"qty":2}]}
winery-shipping-rates 0
winery-total 41

Finally, our eager user returns again to the product list, chooses Wine #3, whose price is €11.00, and specifies a quantity of 6:

Key Value
winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5},{"product":"Wine #2","price":8,"qty":2},{"product":"Wine #3","price":11,"qty":6}]}
winery-shipping-rates 10
winery-total 107

At this point, we need to accurately display the cart when the user goes to the shopping cart page or checkout page:


// Displays the shopping cart

displayCart: function() {
	if( this.$formCart.length ) {
		var cart = this._toJSONObject( this.storage.getItem( this.cartName ) );
		var items = cart.items;
		var $tableCart = this.$formCart.find( ".shopping-cart" );
		var $tableCartBody = $tableCart.find( "tbody" );


		for( var i = 0; i " + product + "" + "" + "" + price + "";

			$tableCartBody.html( $tableCartBody.html() + html );
		}

		var total = this.storage.getItem( this.total );
		this.$subTotal[0].innerHTML = this.currency + " " + total;
	} else if( this.$checkoutCart.length ) {
		var checkoutCart = this._toJSONObject( this.storage.getItem( this.cartName ) );
		var cartItems = checkoutCart.items;
		var $cartBody = this.$checkoutCart.find( "tbody" );

		for( var j = 0; j " + cartProduct + "" + "" + cartQty + "" + "" + cartPrice + "";

			$cartBody.html( $cartBody.html() + cartHTML );
		}

		var cartTotal = this.storage.getItem( this.total );
		var cartShipping = this.storage.getItem( this.shippingRates );
		var subTot = this._convertString( cartTotal ) + this._convertString( cartShipping );

		this.$subTotal[0].innerHTML = this.currency + " " + this._convertNumber( subTot );
		this.$shipping[0].innerHTML = this.currency + " " + cartShipping;
			
	}
}

If the cart’s table is on the shopping cart page, then this method iterates over the array of objects contained in the winery-cart key and populates the table by adding a text field to allow users to modify the quantity of each product. For the sake of simplicity, I didn’t include an action to remove an item from the cart, but that procedure is pretty simple:

  1. Get the items array, contained in session storage.
  2. Get the product’s name, contained in the td element with the pname class.
  3. Create a new array by filtering out the item with the product’s name, obtained in step 2 (you can use $.grep()).
  4. Save the new array in the winery-cart key.
  5. Update the total and shipping charge values.

var items = [
	{
		product: "Test",
		qty: 1,
		price: 5
	},
	{
		product: "Foo",
		qty: 5,
		price: 10
	},
	{
		product: "Bar",
		qty: 2,
		price: 8
	}
];


items = $.grep( items, function( item ) {
	return item.product !== "Test";

});

console.log( items ); 

/*
	Array[2]
		0: Object
			price: 10
			product: "Foo"
			qty: 5
		1: Object
			price: 8
			product: "Bar"
			qty: 2
*/

Then, we need a method that updates the cart with a new quantity value for each product:


// Updates the cart

updateCart: function() {
		var self = this;
	if( self.$updateCartBtn.length ) {
		self.$updateCartBtn.on( "click", function() {
			var $rows = self.$formCart.find( "tbody tr" );
			var cart = self.storage.getItem( self.cartName );
			var shippingRates = self.storage.getItem( self.shippingRates );
			var total = self.storage.getItem( self.total );

			var updatedTotal = 0;
			var totalQty = 0;
			var updatedCart = {};
			updatedCart.items = [];

			$rows.each(function() {
				var $row = $( this );
				var pname = $.trim( $row.find( ".pname" ).text() );
				var pqty = self._convertString( $row.find( ".pqty > .qty" ).val() );
				var pprice = self._convertString( self._extractPrice( $row.find( ".pprice" ) ) );

				var cartObj = {
					product: pname,
					price: pprice,
					qty: pqty
				};

				updatedCart.items.push( cartObj );

				var subTotal = pqty * pprice;
				updatedTotal += subTotal;
				totalQty += pqty;
			});
				
			self.storage.setItem( self.total, self._convertNumber( updatedTotal ) );
			self.storage.setItem( self.shippingRates, self._convertNumber( self._calculateShipping( totalQty ) ) );
			self.storage.setItem( self.cartName, self._toJSONString( updatedCart ) );

		});
	}
}

Our method loops through all of the relevant table cells of the cart and builds a new object to be inserted in the winery-cart key. It also recalculates the total price and shipping charge by taking into account the newly inserted values of the quantity fields.

Suppose that a user changes the quantity of Wine #2 from 2 to 6:

Key Value
winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5},{"product":"Wine #2","price":8,"qty":6},{"product":"Wine #3","price":11,"qty":6}]}
winery-shipping-rates 20
winery-total 139

If the user wants to empty their cart and start over, we simply have to add the following action:


// Empties the cart by calling the _emptyCart() method
// @see $.Shop._emptyCart()

emptyCart: function() {
	var self = this;
	if( self.$emptyCartBtn.length ) {
		self.$emptyCartBtn.on( "click", function() {
			self._emptyCart();
		});
	}
}

Now, session storage has been emptied entirely, and the user may start making purchases again. However, if they decide to finalize their order instead, then we need to handle the checkout form when they insert their personal information.


// Handles the checkout form by adding a validation routine and saving user’s info in session storage

handleCheckoutOrderForm: function() {
	var self = this;
	if( self.$checkoutOrderForm.length ) {
		var $sameAsBilling = $( "#same-as-billing" );
		$sameAsBilling.on( "change", function() {
			var $check = $( this );
			if( $check.prop( "checked" ) ) {
				$( "#fieldset-shipping" ).slideUp( "normal" );
			} else {
				$( "#fieldset-shipping" ).slideDown( "normal" );
			}
		});

		self.$checkoutOrderForm.on( "submit", function() {
			var $form = $( this );
			var valid = self._validateForm( $form );
	
			if( !valid ) {
				return valid;
			} else {
				self._saveFormData( $form );
			}
		});
	}
}

The first thing we need to do is hide the shipping fields if the user checks the toggle that specifies that their billing information is the same as their shipping information. We use the change event, combined with jQuery’s .prop() method. (If you’re curious about the difference between .prop() and .attr(), StackOverflow has a good discussion of it.)

Then, we validate the form by returning a false value in case of errors, thus preventing the form from being submitted. If validation succeeds, we save the user’s data in storage. For example:

Key Value
winery-cart {“items”:[{"product":"Wine #1","price":5,"qty":5},{"product":"Wine #2","price":8,"qty":6},{"product":"Wine #3","price":11,"qty":6}]}
winery-shipping-rates 20
winery-total 139
billing-name John Doe
billing-email jdoe@localhost
billing-city New York
billing-address Street 1
billing-zip 1234
billing-country USA

The final step is the page with the PayPal form. First, we need to display the user’s information gathered on the checkout page:


// Displays the user's information

displayUserDetails: function() {
	if( this.$userDetails.length ) {
		if( this.storage.getItem( "shipping-name" ) == null ) {
			var name = this.storage.getItem( "billing-name" );
			var email = this.storage.getItem( "billing-email" );
			var city = this.storage.getItem( "billing-city" );
			var address = this.storage.getItem( "billing-address" );
			var zip = this.storage.getItem( "billing-zip" );
			var country = this.storage.getItem( "billing-country" );

			var html = "
"; html += "

Billing and Shipping

"; html += "
    "; html += "
  • " + name + "
  • "; html += "
  • " + email + "
  • "; html += "
  • " + city + "
  • "; html += "
  • " + address + "
  • "; html += "
  • " + zip + "
  • "; html += "
  • " + country + "
  • "; html += "
"; this.$userDetails[0].innerHTML = html; } else { var name = this.storage.getItem( "billing-name" ); var email = this.storage.getItem( "billing-email" ); var city = this.storage.getItem( "billing-city" ); var address = this.storage.getItem( "billing-address" ); var zip = this.storage.getItem( "billing-zip" ); var country = this.storage.getItem( "billing-country" ); var sName = this.storage.getItem( "shipping-name" ); var sEmail = this.storage.getItem( "shipping-email" ); var sCity = this.storage.getItem( "shipping-city" ); var sAddress = this.storage.getItem( "shipping-address" ); var sZip = this.storage.getItem( "shipping-zip" ); var sCountry = this.storage.getItem( "shipping-country" ); var html = "
"; html += "

Billing

"; html += "
    "; html += "
  • " + name + "
  • "; html += "
  • " + email + "
  • "; html += "
  • " + city + "
  • "; html += "
  • " + address + "
  • "; html += "
  • " + zip + "
  • "; html += "
  • " + country + "
  • "; html += "
"; html += "
"; html += "

Shipping

"; html += "
    "; html += "
  • " + sName + "
  • "; html += "
  • " + sEmail + "
  • "; html += "
  • " + sCity + "
  • "; html += "
  • " + sAddress + "
  • "; html += "
  • " + sZip + "
  • "; html += "
  • " + sCountry + "
  • "; html += "
"; this.$userDetails[0].innerHTML = html; } } }

Our method first checks whether the user has inputted either billing or shipping information or both. Then, it simply builds an HTML fragment by getting the user’s data from session storage.

Finally, the user may buy the products by submitting the PayPal form. The form redirects them to PayPal, but the fields need to be filled in properly before the form can be submitted.


// Appends the required hidden values to PayPal's form before submitting

populatePayPalForm: function() {
	var self = this;
	if( self.$paypalForm.length ) {
		var $form = self.$paypalForm;
		var cart = self._toJSONObject( self.storage.getItem( self.cartName ) );
		var shipping = self.storage.getItem( self.shippingRates );
		var numShipping = self._convertString( shipping );
		var cartItems = cart.items; 
		var singShipping = Math.floor( numShipping / cartItems.length );

		$form.attr( "action", self.paypalURL );
		$form.find( "input[name='business']" ).val( self.paypalBusinessEmail );
		$form.find( "input[name='currency_code']" ).val( self.paypalCurrency );

		for( var i = 0; i " ).html( "" ).
			insertBefore( "#paypal-btn" );
			$( "
" ).html( "" ). insertBefore( "#paypal-btn" ); $( "
" ).html( "" ). insertBefore( "#paypal-btn" ); $( "
" ).html( "" ). insertBefore( "#paypal-btn" ); $( "
" ).html( "" ). insertBefore( "#paypal-btn" ); } } }

First, we get some important information from session storage — namely, the shipping rate and the total number of items in the cart. We divide the total shipping amount by the number of items to get the shipping rate for each item.

Then, we set the URL for the action attribute of the form, together with our business email and currency code (taken from the paypalBusinessEmail and paypalCurrency properties, respectively).

Finally, we loop through the items of our cart, and we append to the form several hidden input elements containing the quantities, the names of the products, the number of items for each product, the prices (amounts), and the unit shipping rates.

The monetary values are formatted as 00,00. Explaining all of the possible values of a PayPal form and the various types of PayPal forms goes well beyond the scope of this article, If you want to go deeper, I recommend the following reading:

Preview And Source Code

The following video shows the result. I’ve omitted PayPal’s landing page to protect my account’s data, but you can see it as a screenshot.

Get the code from the GitHub repository. Just change the paypalBusinessEmail property of the $.Shop object to your PayPal Sandbox email account.

Other Resources

(al, ea)


© Gabriele Romanato for Smashing Magazine, 2014.

11 Jan 03:51

Four Ways To Build A Mobile Application, Part 2: Native Android

by Peter Traeg

  

This article is the second in a series of four articles covering four ways to develop mobile applications. The last article covered how to accomplish this using native iOS development tools. In this article, we’ll look at how to build the same sort of application using native Android tools.

We’ve been building a simple tip calculator. As with the iOS application, this one contains two screens: a main view and a settings view. The settings view persists the default tip percentage to local storage using Android’s SDK support. (The source code for each app is available on GitHub.)

Here’s the UI we’re following for this project:

android-example

Native Android Development: The Tools

For the Android platform, the Java language is used, along with the Eclipse integrated development environment (IDE). Google provides the Android Developer Tools (ADT) plugin for the standard Eclipse IDE to support things like graphical layout definition and debugging. As of the time of writing, Google has also released a new IDE, called Android Studio, in early-access preview. This product is based on JetBrains’ IntelliJ IDE and will eventually replace Eclipse as the official development tool for Android applications. Because Android Studio is still in prerelease form, we’ll use Eclipse to develop this application. Eclipse is very stable, and many Android training resources using Eclipse abound.

The Android development environment is supported on Windows, Mac and Linux. There is no charge for the development tools, and applications may be freely deployed to devices. To submit apps to the Google Play marketplace, developers must sign up for publishing and pay a one-time fee of $25. Submitting apps to Google Play is notably less involved than for iOS. When your application is submitted to Google, it is scanned for malware and common threats. Generally, the application becomes available to the general public within a few hours.

An overview of the toolset can be found on the Android Developers website. Successful Android development entails use of the following tools:

  • Eclipse IDE
  • Android SDK
  • Android ADT
  • Android system images for the Android emulator
  • an Android device (technically not required but highly recommended)

Grab an Installation Bundle to Ease Your Pain

In the past, you had to download, install and configure the tools individually. This took a fair amount of time and often led to confusion for new Android developers. To make the process easier, Google now offers “bundles,” which simplify the installation process. Bundles are offered for each operating system and are available on the page where you download the developer SDK.

Installing Eclipse, the SDK, the emulator and so on is as easy as unpacking a ZIP file into a directory. If you have an existing installation of Eclipse and would prefer to use that instead, there are instructions for adding Android support to it.

Loading An Existing Application Into Eclipse

Once the Android development tools have been installed, you may wish to import an existing project, such as the source code for our sample app. In Eclipse, go to File → Import in the menu, which will display the following dialog:

android-import-step1

Once you’ve chosen the option to import an existing Android project, click the “Next” button, whereupon you will be able to specify the directory where the code that you wish to work with in Eclipse is located.

android-import-step2

Once you’ve selected a directory via the “Browse” button, Eclipse will automatically find any Android project in that directory and show it in the list of projects to import. Simply click the “Finish” button, and the project will appear in your list of projects along the left side of the IDE.

Steps To Building An Application

There are several steps to building an Android application. We’ll cover each in detail later in this article. The steps include the following:

  1. Define the user interface. The UI for an application is generally defined as a series of layout files. These are XML-based files that describe the controls on a screen and the relationship of their layouts relative to one another.
  2. Add image assets, language translations and other resources. Android refers to non-code assets of a project as resources. These are placed in the project in a directory structure defined by the Android SDK. At runtime, Android dynamically loads content from this directory structure. We’ll see later on how different assets and layouts can be loaded to support the wide variety of Android device configurations available in the market today.
  3. Write Java code to respond to various events that occur from the controls on a given screen and from changes in the lifecycle of an application. Java code is also responsible for loading the layout and menu files associated with each screen. And it’s used to control the flow from one screen to the next.
  4. Export the completed Android application as a file that can be uploaded to Google Play or shared with others directly.

The Eclipse ADT For Android Development

The Eclipse IDE provides the standard source-code editing tools, along with a source-level debugger that allows applications to be debugged on both the simulator and a physical device. In contrast to the storyboards used with iOS, a layout editor is used to define screen layouts:

ADT layout editor
(Large view)

The layout editor works on a single screen at a time and is not able to define segues between screens, as in the iOS storyboard editor. However, the Android layout editor does have a very flexible layout system that supports the wide range of screen sizes and resolutions of Android devices.

Unlike the storyboard editor in iOS, you can edit the layouts in either visual mode, as shown above, or an XML-based editor. Simply use the tabs at the bottom of the layout editor to switch between the two views. As the layouts become more complex, being able to edit the layout files directly comes in handy. A snippet of layout XML looks like this:




    

Android Resources: Support For Various Device Capabilities

The Android SDK was designed from the outset to support a wide variety of device capabilities. Much has been written about the “fragmentation” of devices on the Android platform. However, from the beginning, the Android SDK was designed to support this variety of device attributes, including screen size, pixel density and Android API versions.

Screen layouts, image assets (called drawables), styles and other configuration files are all incorporated in a series of subdirectories underneath a master “resource” directory. The Android SDK documentation illustrates how multiple versions of the same file can be placed in uniquely named directories within this resource structure, so that the proper one is loaded depending on the capabilities and orientation of the device at runtime.

Consider this directory structure:

android-file-structure

The structure above shows uniquely named directories for drawable assets, with suffixes of -hdpi, -ldpi, -mdpi and so on. This permits the developer to supply different image content to correspond with the DPI resolution of a given screen. Similar capabilities extend to things like the layout files, which may supply unique content according to screen size, landscape or portrait orientation, etc. In the example above, we see unique folders for values-v11 and values-v14. This allows for two different styles.xml files to be used for version 11 (Android 3.x) and version 14 (Android 4.x) of the Android operating system.

Note, also, the regular values directory. Android versions prior to version 11 would obtain their styles.xml from this directory. This fallback mechanism is in place for all resources. The SDK will attempt to find the resource in the particular directory and then fall back to a more “generic” resource if it’s not available. All of this occurs without the developer having to write any special code. Simply drop the resources into the proper directories, and the Android runtime will take care of the rest. Each of your resources may be accessed from both the XML and Java files in your application.

The resource system is quite powerful and supports many types of resources. In addition to the drawable, layout, menu and styles for your application, it can also be used to hold application constants for arrays or dimensional values in your application. In this manner, you can load different constant values for various device configurations simply by placing the files in the proper directory structure. You can also use this system to support localization. This is accomplished through the strings.xml file. Here’s an example of the strings.xml file associated with our application:


FasTipHello World!SettingsBill AmountCalculate TipTip AmountTotal AmountSettingsSave SettingsTip Percentage%

The base string file is placed in the res/values directory of the application. If you wanted to offer a Spanish version of the same file, you would place it in the res/values-es directory.

Resource IDs

In the process of defining the layouts, you should associate an ID value with each control (or UI widget) that you wish to reference from code. This can be done by specifying an ID value in the properties inspector in the layout editor:

android-eclipse-id-property-500
(Large view)

Layout Managers

Android layout files may use a number of layout managers, or ViewGroups, to arrange the controls on the screen. This is Android’s approach to laying out views, much like the constraints system we saw in iOS. Here are some of the more common ViewGroups used in Android apps:

  • LinearLayout
    This is used to lay out a series of controls in either horizontal or vertical orientation.
  • RelativeLayout
    This is used to position controls relative to one another or to the bounds of their parents’ layout. This is a flexible layout system and is often used as an alternative to nesting linear layouts.
  • ListView
    This is a view group that presents a series of vertically scrolling items, much like a UITableView for those familiar with iOS. In Android, you write a ListAdapter to provide a view for each row of data in a data source.
  • GridView
    This is similar to a list view, but it provides items in a two-dimensional, scrollable grid. Just like the list view, it also uses an adapter to provide view contents for each cell in the grid.

In our sample application, two layout files have been created: activity_main.xml for the main screen and activity_settings.xml for the settings screen. So far, we’ve just defined the appearance of things. Unlike the iOS storyboard editor, Android has no “assistant” tool to directly link the controls in the visual layout editor to the code. We’ll need to write some code to connect these components together and build the application.

Android: Activities And Intents

In iOS, we loaded the logic specific to a given screen into a ViewController. In Android, these separate screens are treated as separate “activities.” Just like in an iOS UIViewController, there is a defined life cycle for an activity that governs when the activity starts, pauses, resumes and stops.

activity-lifecycle

The diagram above comes straight from the Android Developers documentation and shows the lifecycle of an activity. It’s up to the developer to place code in the various methods of the activity to respond to the various states of the lifecycle.


@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    tipPctTextView = (TextView)this.findViewById(R.id.tipPctTextView);
    tipAmountTextView = (TextView)this.findViewById(R.id.tipAmtTextView);
    totalAmountTextView = (TextView)this.findViewById(R.id.totalAmtTextView);
    calcTipAmountButton = (Button)this.findViewById(R.id.calcTipButton);
    billAmountTextView = (EditText)this.findViewById(R.id.billAmtEditText);

    calcTipAmountButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            calculateTip();
        }
    });
}

The code above is executed early on in the activity’s lifecycle. In this case, we are loading the layout file that was defined earlier, via this statement:


setContentView(R.layout.activity_main);

Upon successfully loading the layout, we obtain references to the various controls that make up the layout. Notice that we are referencing the layouts and the IDs via the R. notation. These are the resource IDs that we defined earlier in the layout manager. Additionally, we attach a click listener to define what code should run when the user taps on the “Calculate Tip” button.

In the iOS example, we used NSUserDefaults to save and restore the user’s preference for a tip percentage. In Android, the equivalent is sharedPreferences. This can be seen in the code snippet below, where we restore the persisted value for tipPercentage in the local variable tipPctString. Notice that the first time the user runs this app, a default value of 15.0 is used.


private void loadTipPercentage() {
    SharedPreferences preferences =
                this.getSharedPreferences("AppPreferences", MODE_PRIVATE);
    String tipPctString = preferences.getString("tipPercentage", "15.0");
    tipPctTextView.setText(String.format("%s%%", tipPctString));
    tipPercentage = Double.parseDouble(tipPctString) / 100;
}

Defining a Menu for Our Activity

Android applications do not use the NavigationController approach of iOS applications. In Android 4.x devices, the choices that appear in the upper-right of the screen are part of the Android “action bar.”

android-action-bar
The action bar is the dark header in this screenshot. Note the settings button in the upper-right, which is defined by the menu.

We populate the action bar by defining a series of menu options in an XML file, just as we did with the screen layout. The menu is placed in a menu directory within the resources directory. Here are the contents of the menu file used on the main screen of our application:


Note that, in this case, we have just one menu option to access the settings screen. If more options were placed in the menu than could fit on the screen, then the remaining items would flow into a drop-down menu, accessible via the three vertical dots often seen in Android menus. To use the menu in our activity, we must load it at the correct time. This is done in the onCreateOptionsMenu method:


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}

// Respond to menu selections
@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle item selection
    switch (item.getItemId()) {
        case R.id.menu_settings:
            this.startSettings();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

Notice how the onOptionsItemSelected method is used to determine what code is run when a given menu option is chosen.

Starting Another Activity

As we’ve seen from the code above, when the user chooses the settings icon from the action bar, the startSettings() method is invoked. This is the code that launches the second activity to display the settings screen:


private void startSettings() {
    Intent settingsIntent = new Intent();
    settingsIntent.setClass(this, SettingsActivity.class);
    startActivity(settingsIntent);
}

Android launches activities via an intent object. In this simple example, we are creating a very specific intent to launch the SettingsActivity class, which contains the code to load and drive the settings screen. Most Android applications use this technique to move the user from one screen to the next in an application.

Using Intents for App Integration

Intents are considerably more flexible than this. It’s possible to use intents to launch a variety of activities. A common intent is to send an item to be shared via social networking apps. If you’ve used an Android device before, then you’ll know that doing something like this typically brings up a menu of applications that support a sharing intent. This list often contains applications such as Facebook and Twitter.

android-share-list-338

This particular sharing intent is known as ACTION_SEND. All of the applications that support sharing are listening for a common intent. When an Android application is installed on a device, its manifest file describes the intents it can process. In this way, several applications may be installed, each of which supports the same intent.

If only one application supports the intent, then that activity is immediately launched. Whereas if multiple applications are installed for the same intent, then Android would show a menu of options, from which the user would make a selection before the intent is sent to the chosen activity.

The intent system in Android is a unique feature that can be used to create workflows across several applications. When building for the Android platform, you should keep this capability in mind because it offers a way to integrate your application with others.

The Settings Screen

Much of what occurs here has already been covered in the section on the main activity. This activity has similar code in the onCreate method to load the appropriate layout file and obtain references to the controls. When the “Save Settings” button is pressed, the following code is run to save the tip percentage value to storage:


private void saveSettings() {
    if (validateSettings()) {
        SharedPreferences preferences =
            this.getSharedPreferences("AppPreferences", MODE_PRIVATE);
        SharedPreferences.Editor prefEditor = preferences.edit();
        prefEditor.putString("tipPercentage",
            tipPercentageEditText.getText().toString());
        prefEditor.commit();
        this.finish();
    }
}

Notice that the last line in the saveSettings method calls the finish() method. This causes the current activity to stop running and returns the user to the prior activity, which in our case is the main screen. Contrast this with iOS’ use of the NavigationController and the way we popped the current view controller off the stack of view controllers.

Running Your Application

Android provides two basic ways to test an application: in the Android emulator or on a regular device such as a phone or tablet. Unlike with iOS, you don’t have to pay any fees to deploy your app to your own device or to share it with other Android users. The only fee that is required is if you wish to feature your app in Google Play.

The emulator enables you to emulate a variety of Android versions, as well as different screen aspect ratios and resolutions. While testing your application on a real device is always a good idea, the emulator helps you to test on device configurations that are not readily available.

android-emulator

To use the emulator, you must create an Android Virtual Device (AVD). Think of this as a virtual phone or tablet. Creating multiple AVDs, each with a unique configuration, is possible to test your app on different screen sizes and memory capacities. Android Developers provides details on how to use the AVD manager to set up these devices.

android-avd-manager

Use Hardware Virtualization to Speed Up That Slow Emulator

The Android emulator has long been maligned for its poor performance on many machines. This is because, in the past, the emulator has been running actual code compiled for the ARM processor on a phone, rather than for the x86 CPU that’s likely on your development machine.

Recently, an x86 hardware-virtualized version has been made available. This version starts up and runs considerably faster than the traditional emulator. The hardware-virtualized version will generally run on a Windows or Mac OS X machine with an Intel Core i3 or later processor that supports hardware virtualization. AMD processors are currently supported only on Linux.

Creating an AVD that uses hardware virtualization requires that you set up Intel’s HAXM support first. The developer documentation provides details on the process to follow for your operating system. Make sure that you are running the latest HAXM support; if you’re on Mac OS X Mavericks, a recently released hotfix resolves issues there as well.

Testing on a Device

To test the application on your own device, you’ll need to enable USB debugging on the device. Google provides detailed instructions on setting up your device for debugging.

android-usb-debug

For Android 4.0 devices, go to Settings → Developer Options in the menu and you’ll see a checkbox to enable this. If you’re on Windows, you might need to install some USB drivers for your device; these can usually be downloaded from the manufacturer’s website. For Mac OS X, you usually will not need any drivers and can just plug in the device via a USB cable.

The Eclipse IDE provides a “targets” option that enables you to specify which device, or AVD, should be used to run your application. This can be found in Run → Run Configurations and Run → Debug Configurations in the menu. The “Targets” tab allows you to select which AVD should be used by default or to set a prompt each time to select a run or debug destination for the application:

android-run-target-500

If you select “Always prompt to pick device,” then the IDE will present this dialog each time you choose to run or debug the application:

android-device-chooser-500

Every time you run or debug the application, an APK file is built and written to the /bin directory of your project’s root directory. This is the application distribution package used for Android devices. It’s analogous to the IPA file for iOS applications. When you select a deployment target via the process above, the Android development tools will automatically install the APK on the target device or emulator and run it.

Sharing Your Application With Others

Submitting an application to Google Play is beyond the scope of this article. The documentation provides an overview of the process. You can also use the export wizard in Eclipse to export an APK file. The resulting APK file can then be shared with others outside of Google Play via open distribution, and it is very useful when sharing copies of your application for testing prior to general release.

Android: Learning Resources

As with iOS, some excellent resources exist for getting started with Android. I recommend the following books and websites:

  • The Busy Coder’s Guide to Android Development, Mark L. Murphy
    This book is comprehensive, and the writing style is easy to follow. The author even offers a free version of the book based on an earlier version (Android 3.x) of the SDK.
  • Beginning Android 4 Application Development, Wei-Meng Lee
    For those looking for a shorter book, this does a good job of covering the basics, without as much detail or scope as Murphy’s book above.
  • Vogella
    This series of brief tutorials shows how to perform common operations in Android applications.
  • Android Weekly
    This weekly newsletter will help you stay current on new blog posts and libraries of interest to the Android development community.

Summary

That wraps up our coverage of developing a native app for Android. We’ve seen some unique capabilities of the platform, such as the resource system, layouts, and the way that activities and intents work together. In the next article, we’ll take the tip calculator cross-platform, looking at what’s involved in developing our app for both iOS and Android using PhoneGap.

(al, ea, il)


© Peter Traeg for Smashing Magazine, 2014.

29 Jun 06:43

This Week's Most Popular Posts: June 21st to 27th

by Whitson Gordon

This Week's Most Popular Posts: June 21st to 27th

This week we revisited the best Google Reader alternatives, dealt with our slow internet, crushed procrastination, and checked out some new office furniture. Here's a look back.

This Week's Most Popular Posts: June 21st to 27th

Google Reader Is Shutting Down; Here Are the Best Alternatives

Google is closing Google Reader's doors on July 1st, meaning you'll need to find a new way to get your news fix. Here's how to export all your feeds and put them into a new reader (and which ones you should check out).

This Week's Most Popular Posts: June 21st to 27th

Top 10 Ways to Deal With a Slow Internet Connection

Sometimes, slow internet is the universe's way of telling you to go play outside. Other times, it's the universe's cruel joke to destroy your productivity. Here are 10 ways to troubleshoot, fix, or just survive a slow internet connection.

This Week's Most Popular Posts: June 21st to 27th

Six Scientifically Supported Ways to Crush Procrastination

Procrastination is something that everyone deals with. It’s hard to place too much blame on ourselves though, as the internet offers an unlimited amount of alternatives to doing our work. Since that’s the case, what are some proven ways to combat procrastination? Let’s take a look!

This Week's Most Popular Posts: June 21st to 27th

The Best Home Office Furniture You've Probably Never Heard Of

You can't have a great workspace without a great desk and chair. While we know of thepopular options, a lot of inexpensive but awesome office furniture exists that you've probably never heard of. Here's a look at some of the best we've found (and their DIY alternatives).

This Week's Most Popular Posts: June 21st to 27th

How to Pay Off Your Debt with the Stack Method

Whether it’s consumer debt on credit cards, student loans, or a mortgage, most people find themselves weighed down by debt at some point in their lives. This can keep us working jobs we hate just to pay the bills and keep our heads above water. By learning how to pay off debt, you can release the burden and remove some some stress. I’m going to explain how to pay off your debt as fast as possible using the "stack method."

This Week's Most Popular Posts: June 21st to 27th

The Best Ways to Apologize When You Screw Up At Work or At Home

After promising your boss you would complete an important assignment on time, you realize you're behind and it's going to be late. You unintentionally leave a colleague out of the loop on a joint project, causing him or her to feel frustrated and a bit betrayed. On the subway, you aren't paying attention and accidentally spill hot coffee all over a stranger's expensive suit. It's time for a mea culpa.

This Week's Most Popular Posts: June 21st to 27th

How I Went From Barely Jogging to Running 100 Miles Per Month

I started running a year ago. I had just begun a new job after leaving my own startup. I was tired of being a founder and desperately needed a break. I wanted to have a calmer lifestyle and I wanted a hobby. Running seemed like a great choice for a hobby. As a computer programmer, my brain gets a great workout but my butt sits in a computer chair all day. I thought that a little bit of exercise would balance it out.

This Week's Most Popular Posts: June 21st to 27th

How I Earn My Living Buying and Selling Appliances on Craigslist

I’ve always been an entrepreneurial sprinkler, chasing business ideas in every direction. This drive was finally channeled three years ago. Some close friends of mine challenged me to pick a business idea and pour myself into it. I chose Craigslist.

This Week's Most Popular Posts: June 21st to 27th

The Best Gmail IFTTT Recipes

Gmail and If This Then That are already two of our favorite services. When you combine the two together they become seriously powerful. Here are a few of our favorite IFTTT recipes that make Gmail even more awesome.

This Week's Most Popular Posts: June 21st to 27th

Why Your Memory Sucks (and What You Can Do About It)

Human memory is quirky, complicated, and unreliable. Even when we think we're remembering everything accurately, chances are things have gotten twisted along the way. Let’s take a look at why your memory sucks, and how you can change that.

This Week's Most Popular Posts: June 21st to 27th

When Gadgets Should Be Repaired, Not Replaced

When I was 14, my stereo broke. Opening it up, I found a small piece of metal had been disconnected from the circuit board at the base. I grabbed a lighter, and melted the piece back in place. I plugged the stereo back in, and turned it on. It worked. It was the first time I actually got something I tried to fix working.

This Week's Most Popular Posts: June 21st to 27th

My 7-Day Workweek Experiment (and Why I Won't Stick With It)

For the first two weeks of last month, I religiously tried to follow a new routine I created for myself: a 7-day work week routine. The idea was quite simple: I would work 7 days a week, rest 7 days a week, go to the gym 7 days a week, reflect 7 days a week. This was less about working lots, and more about feeling fulfilled every day.

This Week's Most Popular Posts: June 21st to 27th

All the New Stuff in Windows 8.1

Microsoft showed off an in-depth look at Windows 8.1 today, and released a preview for everyone to try out. Here are all the new features you'll find in the next version of Windows.

This Week's Most Popular Posts: June 21st to 27th

Save Surprising Kitchen Scraps To Grow Infinite Food

If you want to put homegrown fruits and vegetables on the table, you don't have to go out and buy seeds; you can generate all the food you want with old kitchen scraps.

26 May 02:56

Break the Cycle of of Busyness with These Three Questions

by Shep McAllister

Break the Cycle of of Busyness with These Three Questions

Humans are habitual creatures. We fall into habits and routines to help us get through the day, but after awhile, some of those habits might become unnecessary, and can cause us to constantly feel busy or overwhelmed. Every few months, it's worth stepping back and taking inventory to break out of the cycle of busyness.

Jessica Latham at Tiny Buddha worked hard all day, every day for years, and eventually suffered a neck injury due to stress and lack of rest. During her recovery, she asked herself three questions to find a healthier routine:

First, ask: What am I doing in the day that does not serve me? Do I need to spend three hours every weekend cleaning the house or can my family divide, conquer, and clean in only one hour?

Do I need to spend two hours each day updating my social media status or can I update my profile once a week? What am I willing to sacrifice for internal sanity and calm?

Second, ask: Why do I do all that I do? You might be shocked to see that you cling to a number of superfluous tasks for money, pride, power, or recognition.

Third, ask: What would happen if I stopped doing this? Clearly, if you abruptly quit your job you might face immense challenges. Maybe start by identifying something small to erase from your over-packed day.

Be as specific as writing down each hour in your day to see where you spend most of your time and what you can remove from your day. You might surprise yourself when you see how much television you watch or how much time you spend driving around to do errands.

Once you've asked yourself these questions, Jessica offers up five suggestions to help you act on them, and break away from being unnecessarily busy. For example, you could look at your life from someone else's perspective, and try to identify what you're doing throughout the day that doesn't make sense anymore. Be sure to check out the source link for more suggestions.

5 Tiny Steps to Move Away from Unnecessary Busyness | Tiny Buddha

Photo by Steve Cuckrov (Shutterstock).

24 May 23:35

Cargo-Culting in JavaScript

by James Padolsey

Cargo-cult programming is what a programmer does when he or she doesn't know a particular language or paradigm well enough, and so ends up writing redundant and possibly harmful code. It rears its head quite often in the land of JavaScript. In this article, I explore the concept of cargo-cult programming and places to watch out for it in JavaScript.

Dogmatic rules surface and spread, until they are considered the norm.

Cargo-culting is sometimes defined as "the extreme adherence to the form instead of content." The form, in programming, being the syntax, paradigms, styles and patterns that we employ. The content being the abstract thing that you are seeking to represent through your code — the very substance of your program. A person with lacking understanding in an area is likely to copy the form of others without truly understanding, and thus their content — their program — can suffer.

Cargo-culting is curiously common in JavaScript, probably because of the general low barrier to entry in the front-end development world. You can whip up an HTML page with a bit of JavaScript in seconds. As a result, there are many people who become sufficiently proficient in these technologies to feel comfortable creating and imposing rules on themselves and others. Eventually, other newcomers copy these rules. Dogmatic rules surface and spread, until they are considered the norm:

  • Always use strict equality operators
  • Never use eval
  • Always use a single var declaration per scope
  • Always use an IIFE – it “protects” you

A rule continues to spread until a programmer is only using a given technique because of its popularity, instead of considering each specific use-case independently.


JavaScript Abuzz with Semicolons

If you've had the opportunity to witness the witty banter and rhetoric of the software developer over the years, you will have spotted a tendency to discuss seemingly tiny things at great lengths. Things like the semicolon, the comma, white-space or the curly brace.

Syntax like semicolons or white-space may seem to purely be elements of form, not of content. But many of these subtle syntax rules can have significant effects in JavaScript. If you don't understand the 'form' then you cannot begin to understand the 'content'.

So in this article, we will identify what areas of form in JavaScript are frequently cargo-culted off of — that is, copied without understanding.

How JavaScript can seem
How JavaScript can seem… an image from Angus Croll’s "The Politics Of JavaScript" presentation

Undefined

Angus Croll, in a recent presentation, titled "The Politics Of JavaScript", highlighted one of the most common pieces of JS dogma that people cargo-cult off of:

if (typeof myObject.foo === 'undefined') {...}

Most of the time, doing such a long-winded check for undefined is pointless. The technique became common because people were copying other people, not because of it's actual value.

Of course, there are times when:

typeof x === 'undefined'

… is preferable to:

x === undefined

But, equally, there are times when the latter is preferred. A quick overview of the options:

// Determine if `x` is undefined:
x === undefined
typeof x == 'undefined'
typeof x === 'undefined'
x === void 0

// Determine if `x` is undefined OR null:
x == null
x == undefined

People started using the typeof approach because they were protecting themselves against:

  • A potentially undeclared variable (non-typeof approaches would throw TypeErrors)
  • Someone overwrote undefined globally or in a parent scope. Some environments allow you to overwrite undefined to something like true. You have to ask yourself: “Is it likely that someone overwrote undefined, and should my script have to pander to such silliness?

But most of the time they're protecting themselves from having to worry. It's a catch-all avoidance of having to know the details. Knowing the details can help you though. Every character of your code should exist with a purpose in mind.

The only time that you should need to use a typeof check for undefined is when you are checking for a variable that may not have been declared, e.g. checking for jQuery in the global scope:

if (typeof jQuery != 'undefined') {
    // ... Use jQuery
}

The thing is, if jQuery does exist, then we can be sure that it's an object – a "truthy" thing. So this would be sufficient:

// or:
if (window.jQuery) {

}

The Great Strict/non-strict Debate

Let's take something very common and generally considered good advice, solely using strict-equality:

a === b

Strict-equality is said to be good because it avoids ambiguity. It checks both the value and the type, meaning that we don't have to worry about implicit coercion. With non-strict equality, we do have to worry about it though:

1 == 1    // true — okay, that's good
1 == "1"  // true — hmm
1 == [1]  // true — wat!?

So it would seem sensible advice to entirely avoid non-strict equality, right? Actually, no. There are many situations where strict-equality creates large amounts of redundancy, and non-strict equality is preferable.

When you know, with 100% certainty, that the types of both operands are the same, you can avoid the need for strict-equality. For example, I always know that the typeof operator returns a string, and my right-hand operand is also a string (e.g. "number"):

// With strict-equals
typeof x === 'number'

// With non-strict-equals:
typeof x == 'number'

They're both effectively identical. I am not necessarily suggesting that we abandon strict-equals in this case — I am suggesting that we remain aware of what we're doing so that we can make the best choices given each situation.

Another quite useful example is when you want to know if a value is either null or undefined. With strict equality, you might do this:

if (value === undefined || value === null) {
    // ...
}

With non-strict equality, it's far simpler:

if (value == null) {
    // ...
}

There is no catch here — it is doing exactly what we want, only, arguably, less visibly. But, if we know the language, then what's the problem? It's right there in the spec:

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  • If x is null and y is undefined, return true.
  • If x is undefined and y is null, return true.

If you're writing JavaScript with the intention of it being read, if at all, by people that know JavaScript, then I would argue that you shouldn't feel bad taking advantage of implicit language rules, like this.


hasOwnProperty

The hasOwnProperty method is used to determine whether a property is directly owned by an object. Is it commonly found in for..in loops to ensure that you only mess with direct properties and not inherited properties.

for (var i in object) {
    if (object.hasOwnProperty(i)) {
        // We can do stuff with `object[i]`
    }
}

It’s important to note that the for-in statement will only loop through enumarable properties. Native inherited methods, for example, are not enumerable and so you don't need to worry about them anyway.

The hasOwnProperty check is specifically preventing you from touching properties that you or some third-party script has defined, i.e. when your object's prototype has enumerable properties.

If you know that your object's prototype (or its prototype’s prototype etc.) doesn't have any enumerable properties, then you don't have to worry about using hasOwnProperty in your for-in loops. And, if your object is initialized, via ES5's Object.create(null), then you won't even be able to call hasOwnProperty directly on the object (no prototype means no inherited native methods). This means that using hasOwnProperty by default in all of your for-in loops may actually break sometimes.

One potential solution for objects with null prototypes is to use a saved reference to hasOwnProperty, like so:

var hasOwnProperty = Object.prototype.hasOwnProperty;

// Later in your code:
for (var i in someObject) {
    if (hasOwnProperty.call(someObject, i)) {
        // ...
    }
}

That will work even if the object has no prototype (in the case of Object.create(null)). But, of course, we should only do this in the first place if we know we need it. If you're writing a third-party script for a "hostile" environment, then yes, definitely check for enumerable inherited properties. Otherwise, it may not be necessary all the time.

Note: IE9 and Safari 2.0 complicate the matter further when you're trying to identify enumerable properties that are already defined as non-enumerable. It's worth checking out a truly cross-browser forOwn loop implementation.

To conclude: your use of hasOwnProperty should depend on the object being looped over. It depends on what assumptions you can safely make. Blindly protecting yourself using the hasOwnProperty will not suffice in all cases. Be wary of cross-browser differences too.


Over-Parenthesising

Another common redundancy that creeps into JS code is the parenthesis. Within expressions, it is used to force specific grouping of sub-expressions. Without them, you are at the mercy of operator precedences and associativities. For example:

A && B || C
A && (B || C)
(A && B) || C

One of those is not like the other. The parentheses force a specific grouping, and many people prefer the extra clarity. In this case, the logical AND operator has a higher precedence than the logical OR operator, meaning that it is the first and last lines that are equivelant. The second line is an entirely different logical operation.

Higher precedence means that it will occur before other operations in a series of operations.

To avoid this complexity, developers frequently opt for a "parentheses policy" — where you keep adding parentheses until it is abundantly clear which operations are occurring, both for you and potential readers of the code. It can be argued that this verbosity ends up making things less clear though.

It’s tricky for a reader sometimes. One must consider that any given parentheses may have been added because:

  • It was needed to override default precedence/associativity
  • For no functional reason at all, just for "protection" or "clarity"

Take this example:

A && B ? doFoo() : doBaz()

Without knowledge of operator precedence rules, we can see two possible operations here:

(A && B) ? doFoo() : doBaz()
A && (B ? doFoo() : doBaz())

In this case, it's the logical AND that has the higher precedence, meaning that the equivalent parenthesised expression is:

(A && B) ? doFoo() : doBaz()

We should feel no obligation to add these parentheses in our code, though. It happens implicitly. Once we recognize that it happens implicitly, we are free to ignore it and focus on the program itself.

There are, of course, valid arguments to retain the parentheses where implicit grouping is unclear. This really comes down to you and what you're comfortable with. I would, however, implore you to learn the precedences and then you can be fully empowered to take the best route, dependent on the specific code you're dealing with.


Object Keys

It's not rare to see redundant quotes in object literals:

var data = {
  'date': '2011-01-01',
  'id': 3243,
  'action': 'UPDATE',
  'related': { '1253': 2, '3411': 3 }
};

In addition to strings, JavaScript allows you to use valid identifier names and numbers as object literal keys, so the above could be re-written to:

var data = {
  date: '2011-01-01',
  id: 3243,
  action: 'UPDATE',
  related: { 1253: 2, 3411: 3 }
};

Sometimes, you may prefer the added consistency of being able to use quotes, especially if a field-name happens to be a reserved word in JavaScript (like 'class' or 'instanceof'). And that's fine.

Using quotes is not a bad thing. But it is redundant. Knowing that you don't have to use them is half the battle won. It is now your choice to do what you want.


Comma Placement

There is a huge amount of subjective preference, when it comes to punctuation placement in programming. Most recently, the JavaScript world has been abuzz with rhetoric and discontent over the comma.

Initialising an object in traditionally idiomatic JavaScript looks like this:

var obj = {
    a: 1,
    b: 2,
    c: 3
};

There is an alternative approach, which has been gaining momentum though:

var obj = {
      a: 1
    , b: 2
    , c: 3 
};

The supposed benefit of placing the commas before each key-value pair (apart from the first) is that it means you only have to touch one line in order to remove a property. Using the traditional approach, you would need to remove "c: 3" and then the trailing comma on the line above. But with the comma-first approach you're able to just remove ", c: 3". Proponents claim this makes trailing commas less likely and also cleans up source-control diffs.

Opponents, however, say that this approach only achieves getting rid of the trailing-comma "problem" by introducing a new leading-comma problem. Try removing the first line and you're left with a leading comma on the next line. This is actually considered a good thing by comma-first proponents, because a leading comma would immediately throw a SyntaxError. A trailing comma, however, throws nothing, except in IE6 and 7. So if the developer fails to test their JS in those versions of IE, then the trailing commas can often creep into production code, which is never good. A leading comma throws in all environments, so is less likely to be missed.

Of course, you might argue that this entire thing is moot. We should probably be using linters like JSLint or the kinder JSHint. Then we're free to use the punctuation and whitespace placement that makes the most sense to us and our coworkers.

Let's not even get started on the comma-first style in variable declarations..

var a = 1
  , b = 2
  , c = 3
  ;

Thou Shalt Code for Psychopaths?

We should endeavour to learn the languages we use to a good enough level that we're able to avoid cargo-culting and over-protective catch-all coding techniques. And we should trust our coworkers and other developers to do the same.

We’ve also discussed the abandonement of cruft in favor of taking advantage of a language’s idiosyncracies and implicit rules. To some, this creates maintainability issues, especially if someone more junior in their acquisition of a given language approaches the code. For example, what if they don’t know about JavaScript’s weak vs. strict equality?

On the topic of maintainability, we’re reminded by this famous quote:

Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live.

I don’t know if that is truly good advice. Even taken metaphorically, it suggests a distrust of the fictional maintainer’s competency — and the need to worry about their understanding above everything else. I would rather write code in the knowledge that it will be taken care of by people that know their stuff. So as a possible contradiction or even an addendum to that quote, I offer:

Always code as if the person who ends up maintaing your code is knowledgeable about the language and its constructs and is seeking to gain understanding of the problem domain through reading your code.

While this may not always be true, we should seek for it be so. We should endeavour to ensure that people working on a specific technology have the sufficient understanding to do so. The learned cargo-culter says:

If I forever pander to a lower level of understanding in my code — treading softly — strictly abiding to conventions and style guides and things I see the “experts” do, then I am never able to advance my own understanding, nor take advantage of a language in all its weirdness and beauty. I am happily and blissfully settled in this world of rules and absolutes, but to move forward, I must exit this world and embrace higher understanding.

22 Mar 01:58

6 Classic Nightmares and their Modern Equivalents

6 Classic Nightmares and their Modern Equivalents - Image 2



Like this? Follow Caldwell on