Warning: This is a long read as there is a lot of information pertaining to the problem in question, so prepare yourself, and thanks for taking the time to go through this if you do!
I'm in the process of writing a very large WebApp for a client and have built numerous interactive applications for this using jQuery, but one which I have developed has left me wondering: is there a standard way to do this, or perhaps just a better way?
This is where I come to you guys, to gain some insight on whether there is a certain way that this should be done, as I'm always looking to build optimal applications.
The Task I had to complete
The app I'm building requires the user to be able to build an invoice of products. This invoice can have any number of products added to it via a search box. Each product is pulled from the database and stored inside that search box, and each product can be added multiple times.
The way the user adds a product is by typing the name of the product in the search box, and pressing an Add Product button. This prepends a processed template to the list of products. Pretty simple right?
The Problem
The big problem I came across was that not only was the invoice able to contain a list of products, but each of these products contained varying customizable details. This meant that each product had a number of it's own fields on it, which the user could edit, which modified a number of things. For instance, one type of product has a quantity, which needs to be multiplied by the cost price of that product to return a total price. Another product has a length, which needs to be multiplied by the width, which then needs to be multiplied by the cost price of that product to return a total price.
As you can imagine, there are many different types of products with many different fields with many different calculations to be made, which, at the end of creation, need to be parsed to a PHP page whilst maintaining full modifiability where the user can add/remove/edit products on the same invoice page without jumping around.
This, to me, was a nightmare, as I needed to have a way to keep this clean and have a bunch of different inputs/jQuery objects not colliding with each other one way or another. I also had to differentiate loaded from newly created products, as some would have grabbed information from the database
The Solution I Built
So when a product is prepended to the list, the product contains a number of fields, a number of details, and a remove button. The first thing I did was make templates with replaceable tokens for each different type of product. That was easy and worked perfectly fine, and when a product was prepended to the list, to every element that was related to that product, I added a rel attribute to it (which I called a "hook") so that I could add handlers that only modified elements with that specific hook.
For the inputs I used input arrays, in the form of <input name="Products[%HOOK%][%PRODUCTDATAKEY%]" value="%PRODUCTDATAVALUE%" />
. Where my main problem arised was where I had to differentiate the products loaded that had already been saved in the database, from the newly added products that had just been added. The way I did this was by adding [Loaded] before all of the input name arrays, so that my PHP script would be able to deal with the [Loaded] array separately.
What is my concern?
My largest concern is how messy everything ended up looking, from the markup to the code. Hooks flying all over the place, Extremely long input names, A couple of thousand lines of code and a headache of different keys and tokens to be replaced. It works perfectly fine, it just feels horrible when I look at it as a developer. Take this "added product" for example:
<div class="item product special-order" rel="7" style="">
<input type="hidden" name="products[7][id]" value="17">
<input type="hidden" name="products[7][item_id]" value="">
<input type="hidden" name="products[7][type]" value="Special Order Flooring">
<input type="hidden" name="retail_price" class="invoice-base-cost" value="18.49">
<h4>Carpet Brand<a name="action" rel="7" class="btn btn-mini btn-danger remove-product initialized" style=""><i class="icon-remove"></i> Remove</a>
</h4>
<div class="item-details">
<div class="detail-column">
<div class="detail">
<span class="detail-label detail-label-medium"> Type:</span>Carpet
</div>
<div class="detail">
<span class="detail-label detail-label-medium">Range:</span> Carpet Brand
</div>
<div class="detail">
<span class="detail-label detail-label-medium">
Colour:</span><input required="" class="detail-input-large left-align" value="" type="text" name="products[7][colour]">
</div>
<div class="detail">
<span class="detail-label detail-label-medium">
Length:</span><input type="text" name="products[7][length]" rel="7" class="length-field initialized" placeholder="Enter the length..." value="1.000"><span class="detail-unit">m</span>
</div>
</div>
<div class="detail-column">
<div class="detail">
<span class="detail-label detail-label-large">Manufacturer:</span>
<select class="detail-input-large" required="" name="products[7][manufacturer_id]">
<option value="14">Manufacturer 1</option>
<option value="16">Manufacturer 2</option>
</select>
</div>
<div class="detail">
<span class="detail-label detail-label-large">Width supplied:</span><input type="text" name="products[7][width]" class="width-field" value="5.000"><span class="detail-unit">m</span>
</div>
<div class="detail">
<span class="detail-label detail-label-large">Retail price per/m:</span> £18.49
</div>
<div class="detail">
<span class="detail-label detail-label-large">Room:</span><input class="detail-input-large left-align" type="text" name="products[7][room]" value="" required="required">
</div>
</div>
<div class="item-cost">
<h5>Item cost</h5>
£<span class="invoice-cost" rel="7">92.45</span>
</div>
</div>
</div>
Is that not overkill? Could I have not mapped these to JSON objects to make it much cleaner somehow? Or is this the right way to do this?
That product had about 5 different event binds on it too, which was messy due to the fact that if the length field was changed, the price had to be recalculated by the width and the base price, and the same with the width just backwards, and this was all done by looking for fields with the same rel (hook)... It just looks like a mess to me. It'd be great to get an expert opinion on how this should have been approached.