t3hl33td4rg0n
New member
Firstly, I will say that I don't really consider myself a programming expert by any means, so I am making an effort to ensure that I am not learning any bad habits. I've been coding in JS using this methodology for a couple years now and wanted to get some opinions on whether this is the way to go or if I should be using different methods.
What I have below is a real basic order entry/scheduling system which has a PHP backend and all the calls will return either JSON or HTML (it's a little messy right now).
The basic layout.
Page Done Loading initial call (I give it a very short delay in execution as sometimes the DOM tree isn't 100% ready to go).....
Below this section is where the bulk of the code goes. Heres the layout of one of these. This is the customer "object".
When in the browser console, the data can be retrieved in the other objects in this way without sending additional queries to the server. It seems fairly organized to me IMO, but there may be better ways of storing this.
For orders, appointments, invoices, etc, I have a layout that controls how the forms behave for create/modify/delete. Here is what orders look like. Another thing of note is that since there are multiple forms on one page, each form element usually has a prefix (cco=create customer order, uco=update customer order, etc). I couldn't think of any other way to do this without running into naming conflicts with the different forms when using jQuery.serialize().
Usually at the bottom of the script I will throw in miscellaneous functions that serve a specific pupose and doesn't tie into the objects themselves.
There's a bunch more, but you get the idea
I know that some of this code a bit sloppy and could be compartmentalized a bit better. So far scoping hasn't been much of an issue, except in one instance, but I believe the main benefit of writing in this manner without instancing the objects allows for the customer data to be shared across the objects without passing around all over the place. I'm no expert, but I believe these would be considered static objects?
Should I rethink this type of layout and structure, or am I missing something here?
What I have below is a real basic order entry/scheduling system which has a PHP backend and all the calls will return either JSON or HTML (it's a little messy right now).
The basic layout.
Page Done Loading initial call (I give it a very short delay in execution as sometimes the DOM tree isn't 100% ready to go).....
Code:
$(document).ready(function() { setTimeout("pageInit()", 100); });
function pageInit () {
.... // Mostly stuff to manipulate a few default events for some elements
}
Below this section is where the bulk of the code goes. Heres the layout of one of these. This is the customer "object".
Code:
/* Current Customer */
var customer = {
cid: false, // Customer ID
data: false, // Customer residential information
orders: false, // Associative Array
appointments: false, // Associative Array
invoices: false, // Associative Array
reports: false, // Associative Array
address: false, // String of customer's address for header and google
appointmentTotals: false,
orderTotals: false,
refreshCustomer: function () {
customer.getCustomerInfo(customer.cid);
},
// Flow for retrieving all customer information (data, appointments, orders) then displaying the information
// There are better ways of doing this, but this will do for now :)
getCustomerInfo: function (cid) {
// Paramters for each AJAX request
var qCustomer = { url: ajaxFile, data: { q: 'getCustomerByCID', uid: cid }, type: 'POST', success: function (data) { customer.data = jQuery.parseJSON(data) } };
var qOrders = { url: ajaxFile, data: { q: 'getOrdersByCID', cid: cid }, type: 'POST', success: function (data) { customer.orders = jQuery.parseJSON(data) } };
var qAppointments = { url: ajaxFile, data: { q: 'getAppointmentsByCID', cid: cid }, type: 'POST', success: function (data) { customer.appointments = jQuery.parseJSON(data) } };
var qInvoices = { url: ajaxFile, data: { q: 'getInvoicesByCID', cid: cid }, type: 'POST', success: function (data) { customer.invoices = jQuery.parseJSON(data) } };
var qReports = { url: ajaxFile, data: { q: 'getReportsByCID', cid: cid }, type: 'POST', success: function (data) { customer.reports = jQuery.parseJSON(data) } };
// Populate data on-screen once requests are finished
$.when($.ajax(qCustomer), $.ajax(qAppointments), $.ajax(qOrders), $.ajax(qInvoices)). then ( function () {
$('#customerData').css('display', 'block'); //Unhide customer info pane
customer.cid = customer.data[0].uid;
customer.outputCustomerData();
customer.outputCustomerOrders();
customer.outputCustomerAppointments();
customer.outputCustomerInvoices();
});
},
// Depends: customer.data
// Output: HTML -> Current Customer Information
outputCustomerData: function () {
var r = customer.data[0];
// For GMaps
var gm = String('https://www.google.com/maps/place/');
var add = r.addStreet+ '+' +r.addBldg+ '+' +r.addCity+ '+' +r.addState+ '+' +r.addZip;
customer.address = add;
$('#ciAddy').attr('href', gm+add);
$('#ciAddy').attr('target', '_new');
$('#ciDirections').attr('href', 'https://www.google.com/maps/dir/2642+Berne+St,+akron,+OH+44312/'+add);
$('#ciDirections').attr('target', '_new');
// Refresh Entire Pane
$('#customerRefreshButton').html ('<input class="stdButtonSm" type="button" onclick="customer.getCustomerInfo('+r.uid+')" value="Refresh" />');
// Populate HTML
$('#ciName').html(r.lName+', '+r.fName);
$('#ciAddy').html(r.addStreet+' '+r.addUnit+' '+r.addBldg+'<br />'+r.addCity+', '+r.addState+' '+r.addZip);
$('#cid').html(r.uid);
$('#ciPhone1').html(r.phone1);
$('#ciPhone2').html(r.phone2);
$('#ciEntryDate').html(r.newDate);
// Quick Stats
var orderTotals = customer.getStatusTotals (customer.orders);
customer.orderTotals = orderTotals;
$('#ciOrderTotals').html (orderTotals[0]+' / '+orderTotals[1]+' / '+orderTotals[2]);
var appointmentTotals = customer.getStatusTotals (customer.appointments);
customer.appointmentTotals = appointmentTotals;
$('#ciAppointmentTotals').html (appointmentTotals[0]+' / '+appointmentTotals[1]+' / '+appointmentTotals[2]);
// Make the create buttons
$('#createOrderButtonContainer').html(' <input class="stdButtonSm" type="button" onclick="order.formReadyCreate();" value="Create" />');
$('#createAppointmentButtonContainer').html(' <input class="stdButtonSm" type="button" onclick="appointment.formReadyCreate();" value="Create" />');
$('#createInvoiceButtonContainer').html(' <input class="stdButtonSm" type="button" onclick="invoice.formReadyCreate();" value="Create" />');
},
// Depends: customer.orders
// Output: HTML -> Current Customer Information -> Orders
outputCustomerOrders: function () {
var r = customer.orders;
if (customer.orders.length > 0) {
var table = Array();
table.push(['ID','5%']);
table.push(['Status','8%']);
table.push(['Type','10%']);
table.push(['Begin Date','12%']);
table.push(['End Date','12%']);
table.push(['Services','22%']);
table.push(['Report(s)','9%']);
table.push(['Invoice','12%']);
table.push(['Action','10%']);
var out = String(createTableHeader('dataTable', 'width:100%;margin:auto;clear:both;', table));
for (x = 0; x < r.length; x++) {
out += tr;
out += td+ r[x].uid +tdc;
out += td+ r[x].status +tdc;
out += td+ clrNull(r[x].type) +tdc;
out += td+ r[x].newDate +tdc;
out += td+ clrNull(r[x].completeDate) +tdc;
out += td+ r[x].services +tdc;
if (r[x].reportID == 0) {
out += td+ '<a onclick="report.formReadyCreate();">'+ 'Create...' +'</a>' +tdc;
} else {
out += td+ clrNull(r[x].reportID) +tdc;
}
if (r[x].invoiceID == 0) {
out += td+ '<a onclick="invoice.formReadyCreate();">'+ 'Create...' +'</a>' +tdc;
} else {
out += td+ '<a onclick="invoice.view();">'+ r[x].invoiceID +'</a>' +tdc;
}
out += td;
out += ct+'<div><img src="../img/icon_edit.png" onclick="order.formReadyUpdate('+r[x].uid+')" title="Edit" alt="Edit" /> ';
out += '<img src="../img/icon_delete.png" onclick="order.formReadyDelete('+r[x].uid+')" title="Delete" alt="Delete" /></div>'+ctc;
out += tdc;
out += trc;
}
out += tc;
} else {
out = '<p style="text-align:center;">No orders found!</p>';
}
$('#ciOrders').html(out);
},
// Depends: customer.appointments
// Output: HTML -> Current Customer Information -> Appointments
outputCustomerAppointments: function () {
var r = customer.appointments;
if (customer.appointments.length > 0) {
var table = Array();
table.push(['ID', '5%']);
table.push(['Status', '8%']);
table.push(['Type', '15%']);
table.push(['Date', '12%']);
table.push(['Slot', '10%']);
table.push(['Assist', '15%']);
table.push(['ETT', '5%']);
table.push(['Notes', '20%']);
table.push(['Action', '10%']);
var out = String(createTableHeader('dataTable', 'width:100%;margin:auto;clear:both;', table));
for (x = 0; x < r.length; x++) {
out += tr;
out += td+ r[x].uid +tdc;
out += td+ r[x].status +tdc;
out += td+ r[x].type +tdc;
out += td+ r[x].date +tdc;
out += td+ r[x].slot +tdc;
out += td+ '<a title="'+r[x].assist+'" style"cursor:pointer;>'+r[x].assist.substr(0,30)+'</a>';
out += td+ r[x].tripTime +tdc;
out += td;
if (r[x].notes == null) { out += " "; } else { out += '<a title="'+r[x].notes.substr(0,40)+'" style"cursor:pointer;>'+r[x].assist.substr(0,40)+'...</a>';; };
out += tdc;
out += td;
out += ct+'<div><img src="../img/icon_edit.png" onclick="appointment.formReadyUpdate('+r[x].uid+')" title="Edit" alt="Edit" /> ';
out += '<img src="../img/icon_delete.png" onclick="appointment.formReadyDelete('+r[x].uid+')" title="Delete" alt="Delete" /></div>'+ctc;
out += tdc;
out += trc;
}
out += tc;
} else {
out = '<p style="text-align:center;">No appointments found!</p>';
}
$('#ciAppointments').html(out);
},
outputCustomerInvoices: function () {
var r = customer.invoices;
if (customer.appointments.length > 0) {
var table = Array();
table.push(['I:ID', '5%']);
table.push(['Date', '10%']);
table.push(['Total', '7%']);
table.push(['Paid', '7%']);
table.push(['Services(Q): Amount', '31%']);
table.push(['Notes', '20%']);
table.push(['Action', '20%']);
var out = String(createTableHeader('dataTable', 'width:100%;margin:auto;clear:both;', table));
} else {
out = '<p style="text-align:center;">No invoices found!</p>';
}
for (x = 0; x < r.length; x++) {
out += tr;
out += td+ r[x].invoiceID +tdc;
out += td+ r[x].invoiceDate +tdc;
out += td+ '$'+r[x].totalCharges +tdc;
out += td+ '$'+r[x].paidAmount +tdc;
out += td;
for (n = 0; n < r[x].invoiceData.length; n++) {
out += r[x].invoiceData[n].service + '(' + r[x].invoiceData[n].qty + ')' + ': ';
out += '$' + r[x].invoiceData[n].charge + '<br />';
}
out += tdc;
out += td+ r[x].notes.substr(0.40) +tdc;
out += trc;
}
out += tc;
$('#ciInvoices').html(out);
},
outputCustomerReports: function () {
},
/* Misc Functions */
getCustomerByCID: function (cid) {
$.post(ajaxFile, 'q=getCustomerByCID&uid='+cid, function(data) {
customer.data = jQuery.parseJSON(data);
});
},
getAppointmentsByCID: function (cid) {
$.post(ajaxFile, 'q=getAppointmentsByCID&cid='+cid, function(data) {
customer.appointments = jQuery.parseJSON(data);
});
},
getOrdersByCID: function (cid) {
$.post(ajaxFile, 'q=getOrdersByCID&cid='+cid, function(data) {
customer.orders = jQuery.parseJSON(data);
});
},
getStatusTotals: function (data) {
var statusNew = new Number (0);
var statusWIP = new Number (0);
var statusDone = new Number (0);
for (x = 0; x < data.length; x++) {
switch (data[x].status) {
case 'New': statusNew++; break;
case 'WIP': statusWIP++; break;
case 'Done': statusDone++; break;
}
}
var out = Array (statusNew, statusWIP, statusDone);
return out;
},
testout: function () {
console.log('done');
console.log(customer.data);
console.log(customer.data.length);
console.log(customer.appointments);
console.log(customer.appointments.length);
console.log(customer.orders);
console.log(customer.orders.length);
},
mrDebug: function () { }
};
When in the browser console, the data can be retrieved in the other objects in this way without sending additional queries to the server. It seems fairly organized to me IMO, but there may be better ways of storing this.
For orders, appointments, invoices, etc, I have a layout that controls how the forms behave for create/modify/delete. Here is what orders look like. Another thing of note is that since there are multiple forms on one page, each form element usually has a prefix (cco=create customer order, uco=update customer order, etc). I couldn't think of any other way to do this without running into naming conflicts with the different forms when using jQuery.serialize().
Code:
var order = {
// Populate form data to create an order
formReadyCreate: function () {
showDialog('createOrder');
var r = customer.data[0];
$('#ccoInfo').html(r.lName+', '+r.fName+'<br />'+r.addStreet+' '+r.addUnit+' '+r.addBldg+'<br />'+r.addCity+', '+r.addState+' '+r.addZip);
$('#ccoDate').datepicker({dateFormat:"yy-mm-dd"}).datepicker("setDate",new Date());
},
create: function (makeAppt) {
var returnQuery = new String('');
var formStuff = new String('');
// Generate URL and append extra stuff
formStuff += $('#createCustomerOrder').serialize();
formStuff += '&q=createCustomerOrder';
formStuff += '&cid='+customer.data[0].uid;
formStuff += '&makeAppt='+makeAppt;
// Once PHP is done, return success or fail
$.post(ajaxFile, formStuff, function(data) {
var r = jQuery.parseJSON(data);
$().toastmessage('showNoticeToast', data);
$("#createCustomerOrder")[0].reset();
$('#createOrder').dialog('close');
customer.refreshCustomer();
});
},
formReadyUpdate: function (uid) {
showDialog('updateOrder');
var formStuff = new String('');
formStuff += 'q=getOrderById';
formStuff += '&uid='+uid;
$('#updateOrder').html('');
// Create the form inside the dialog window.
$.post(ajaxFile, formStuff, function(data) {
var parsed = jQuery.parseJSON(data);
var r = parsed[0];
var out = new String('');
out += '<div>';
out += '<span style="font-size:125%;">Updating Order #'+uid+'...</span>';
out += '<div style="clear:both;"> </div>';
out += '<form id="updateCustomerOrder" name="updateCustomerOrder" action="./">';
out += '<label class="stdLabel" for="ucoStatus">Status:</label>';
out += '<select id="ucoStatus" name="ucoStatus" onchange="updateCompleteDate(\'ucoStatus\', \'ucoEndDate\');" >';
(r.status == 'New') ? out += '<option value="New" selected="selected">New</option>' : out += '<option value="New">New</option>';
(r.status == 'WIP') ? out += '<option value="WIP" selected="selected">WIP</option>' : out += '<option value="WIP">WIP</option>';
(r.status == 'Done') ? out += '<option value="Done" selected="selected">Done</option>' : out += '<option value="Done">Done</option>';
out += '</select>';
out += '<br />';
out += '<input type="hidden" id="ucoUID" name="ucoUID" value="'+r.uid+'" />';
out += '<label class="stdLabel" for="ucoServices">Services:</label><input type="text" class="stdInputSpaced" id="ucoServices" name="ucoServices" size="28" value="'+r.services+'" />';
out += '<input class="stdButtonSm" type="button" onclick="showServiceCodes(\'ucoServices\');" value="+" /><br />';
out += '<label class="stdLabel" for="ucoNewDate">Begin Date:</label><input type="text" class="stdInputSpaced" id="ucoNewDate" name="ucoNewDate" size="11" value="'+r.newDate+'" /><br />';
out += '<label class="stdLabel" for="ucoWIPDate">WIP Date:</label><input type="text" class="stdInputSpaced" id="ucoWIPDate" name="ucoWIPDate" size="11" value="'+clrNull(r.wipDate)+'" /><br />';
out += '<label class="stdLabel" for="ucoEndDate">End Date:</label><input type="text" class="stdInputSpaced" id="ucoEndDate" name="ucoEndDate" size="11" value="'+clrNull(r.completeDate)+'" /><br />';
out += '<label class="stdLabel" for="ucoCheckout">Checkout Item(s):</label><input type="text" class="stdInputSpaced" id="ucoCheckout" name="ucoCheckout" size="24" value="'+r.checkout+'" /><br />';
out += '<label class="stdLabel" for="ucoCheckIn">Check-in Item(s):</label><input type="text" class="stdInputSpaced" id="ucoCheckIn" name="ucoCheckIn" size="24" value="'+r.checkin+'" /><br />';
out += '<label class="stdLabel" for="ucoLocalStore">Local Store Item(s):</label><input type="text" class="stdInputSpaced" id="ucoLocalStore" name="ucoLocalStore" size="24" value="'+clrNull(r.localStore)+'" /><br />';
out += '<label class="stdLabel" for="ucoReportID">Report #:</label><input type="text" class="stdInputSpaced" id="ucoReportID" name="ucoReportID" size="5" value="'+clrNull(r.reportID)+'" /><br />';
out += '<label class="stdLabel" for="ucoInvoiceID">Invoice #:</label><input type="text" class="stdInputSpaced" id="ucoInvoiceID" name="ucoInvoiceID" size="5" value="'+clrNull(r.invoiceID)+'" /><br />';
out += '<label class="stdLabel" for="ucoPaidAmount">Paid Amount:</label>$<input type="text" class="stdInputSpaced" id="ucoPaidAmount" name="ucoPaidAmount" size="6" value="'+r.paidAmount+'" placeholder="0.00" /><br />';
out += '<label class="stdLabel" for="ucoNotes" style="vertical-align:top;">Notes:</label><textarea id="ucoNotes" name="ucoNotes" class="stdInputSpaced" cols="45" rows="2">'+clrNull(r.notes)+'</textarea><br />';
out += '<div style="clear:both;"> </div>';
out += '<input class="stdButton" type="button" onclick="order.update();" value="Update" />';
out += '</form>';
out += '</div>';
$('#updateOrder').html(out);
$('#ucoNewDate').datepicker({ dateFormat: "yy-mm-dd" });
$('#ucoWIPDate').datepicker({ dateFormat: "yy-mm-dd" });
$('#ucoEndDate').datepicker({ dateFormat: "yy-mm-dd" });
});
},
update: function () {
var sqlDate = $.datepicker.formatDate('yy-mm-dd', new Date());
var formStuff = new String('');
// Generate URL and append extra stuff
formStuff += $('#updateCustomerOrder').serialize();
formStuff += '&q=updateCustomerOrder';
formStuff += '&uid='+$('#ucoUID').val();
$.post(ajaxFile, formStuff, function(data) {
$().toastmessage('showNoticeToast', data);
$("#updateCustomerOrder")[0].reset();
$('#updateOrder').dialog('close');
$('#updateOrder').html('');
customer.refreshCustomer();
});
},
formReadyDelete: function (uid) {
// Just a simple confirmation dialog
showDialog('deleteOrder');
$("#deleteOrder").dialog("option", "width", '60%' );
var out = new String('');
out += '<h2 align="center">Are you Sure?</h2>';
out += ct+'<div>';
out += '<input class="stdButton" type="button" onclick="order.deleteOrder(\''+uid+'\');" value="Yes" /> ';
out += '<input class="stdButton" type="button" onclick="closeDialog(\'deleteOrder\')" value="No" /> ';
out += '</div>'+ctc;
$('#deleteOrder').html(out);
},
deleteOrder: function (uid) {
var formStuff = new String('');
formStuff += '&q=deleteOrder';
formStuff += '&uid='+uid;
// Send delete request to server
$.post(ajaxFile, formStuff, function(data) {
$().toastmessage('showNoticeToast', data);
$('#deleteOrder').dialog('close');
$('#deleteOrder').html('');
customer.refreshCustomer();
});
},
mrDebug: function () {}
};
Usually at the bottom of the script I will throw in miscellaneous functions that serve a specific pupose and doesn't tie into the objects themselves.
Code:
/* Misc Page Functions */
function showDialog (ele) {
var winWidth = $(window).width();
var threshWidth = new Number(1080);
var width = new Number();
if (winWidth < threshWidth) {
width = '99%';
} else {
width = '80%';
}
$('#'+ele).dialog({
width: width,
height: 'auto',
modal: true,
show: {
effect: "fade",
duration: 500
},
hide: {
effect: "fade",
duration: 500
}
});
}
function closeDialog (ele) {
$('#'+ele).dialog('close');
}
function day (n) {
var o = '';
switch (n) {
case 0: o = 'Sun'; break;
case 1: o = 'Mon'; break;
case 2: o = 'Tue'; break;
case 3: o = 'Wed'; break;
case 4: o = 'Thu'; break;
case 5: o = 'Fri'; break;
case 6: o = 'Sat'; break;
}
return o;
}
function leadZero (n) {
var x = String('')
if (n.length == 1 || n < 10) {
x = '0' + n;
} else {
x = n;
}
return x;
}
There's a bunch more, but you get the idea
I know that some of this code a bit sloppy and could be compartmentalized a bit better. So far scoping hasn't been much of an issue, except in one instance, but I believe the main benefit of writing in this manner without instancing the objects allows for the customer data to be shared across the objects without passing around all over the place. I'm no expert, but I believe these would be considered static objects?
Should I rethink this type of layout and structure, or am I missing something here?