4
\$\begingroup\$

I am working on a codebase that operates in the following manner:

/*=========================================
=            Table of Contents            =
=========================================*/
/*
    00 - Initializer
    01 - Summary
    02 - Consumer Lead
    03 - General Services
    04 - Products
    05 - Quote
    06 - Status
    07 - Utility
 */ 
/*-----  End of Table of Contents  ------*/

(function($){

    /*========================================
    =            00 - Initializer            =
    ========================================*/

        /*========== Constructor  ==========*/

            /**
             * Constructor for the controller.  Creates the controller object and kicks of the 
             * creation of the app.
             * @class   controller
             * @variation inspection-detail
             * @instance
             */
            var controller = function(){
                var _this = this;
                this.setRec = true;

                dt_utils.setLoadal("show");

                //Scoping all of the reused elements in order to save lookups.
                this.defineEls();

                //Creating instances of all the models and views.
                this.createInstances();

                //One function to rule them all.
                this.setEventListeners();

                this.inspectionuuid = dt_utils.getUrlParameterByName("inspection");

                //As part of setEventListeners, we are listening to the ajaxStop event
                //on the document.  This allows us to call everything async and still ensure
                //that all the data is present before we try to do logix.
                //Makes things super fast.
                this.inspectionDetails.fetch(function(e, inspection){
                    if (e != null){
                        dt_utils.handleError(e, true);
                    } else {                        
                        _this.consumerLead.fetch(function(e){
                            if (e != null) dt_utils.handleError(e, true);
                        }, inspection.consumerleadid);      
                        _this.productQuery.fetchByCarline(
                            inspection.vehicleyear
                            ,inspection.vehiclemake
                            ,inspection.vehiclemodel
                            ,inspection.vehicletrim
                            ,inspection.vehiclepackage
                            ,function(e, p) {
                                if (e != null){
                                    dt_utils.handleError(e, true);
                                } else {
                                    if (p.length){
                                        _this.promotions.fetch(p, 2, function(e){
                                            if (e != null) dt_utils.handleError(e, true);
                                        });                             
                                    };
                                }
                            }
                        );
                        _this.dealerServiceOfferings.fetch(function(e){
                            if (e != null) dt_utils.handleError(e, true);
                        });
                        _this.carlineCatalogYearMakeModelRequest.fetch(function(e){
                            if (e != null) dt_utils.handleError(e, true);
                        });
                        _this.carlineCatalogTrimOptionRequest.fetch(
                            function(e){
                                if (e != null) dt_utils.handleError(e, true);
                            }
                            ,{
                                year: inspection.vehicleyear
                                ,make: inspection.vehiclemake
                                ,model: inspection.vehiclemodel
                                ,mode: "year"
                            }
                        );
                        inspection.inspectionuuid = _this.inspectionuuid;
                        _this.inspectionDetails.setData(inspection);
                    }
                } ,this.inspectionuuid);

                // Content
                this.contentQuery.fetch(function(e, data) {
                    if (e != null){
                        dt_utils.handleError(e, false); 
                    } else {                        
                        for(var i = 0, iLen = data.length; i<iLen; i++) {
                            if (data[i].metaKeyWords === "alignment") {
                                _this.$alignmentContent.empty().append(data[i].contentBody);
                            } else if (data[i].metaKeyWords === "tread depth") {
                                _this.$treadDepthContent.empty().append(data[i].contentBody);               
                            }
                        };
                    }
                },{
                    type : "Inspection Detail"
                    ,section: "Sidebar"
                    ,path : "/inspection-detail/"
                    ,productGroup: "TIR"
                    ,isPublished: 1
                    ,program: contentProgram
                });
            };
;
        /*==========  Define Elements  ==========*/

            /**
             * Scopes all of the reused elements on the page to the
             * this scope to save on additional jQuery lookups.
             * @method   defineEls
             * @memberOf controller(inspection-detail)
             * @instance
             */
            controller.prototype.defineEls = function(){
                //Main El
                this.$el = $("#inspection-detail");

                //Product/Service Lists
                this.$productList = this.$el.find(".comp_ui_listView");
                this.$serviceList = this.$el.find("#alignmentRecommendations");

                //Headers
                this.$primaryHeader = this.$el.find(".header-wrapper");
                this.$secondaryHeader = this.$el.find(".nav-primary-wrapper");
                this.$tertiaryHeader = this.$el.find(".nav-tertiary");

                // Menu
                this.$navMenu = this.$el.find(".nav-secondary");

                //Alignments
                this.$alignmentRecommendations = this.$el.find(".alignment-recommendations");
                this.$alignmentDeclineButton = this.$el.find("#alignmentDeclineButton");
                this.$alignmentRecommendationsHeader = this.$el.find(".alignment-recommendations .panel-header h3");
                this.$alignmentDeclined = this.$el.find(".alignment-recommendations.service-declined");
                this.$alignmentUpsell = this.$el.find(".alignment-recommendations.service-upsell");
                this.$alignmentResults = this.$el.find(".panel-results.alignment-recommendations");
                this.$alignmentMissing = this.$el.find(".alignment-recommendations.service-missing");

                //Tires
                this.$tireRecommendations = this.$el.find(".tire-recommendations");
                this.$tireRecommendationsHeader = this.$el.find(".tire-recommendations .panel-header h3");
                this.$moreTires = this.$el.find(".recommended-tires-buttons");
                this.$tireSizeNext = this.$el.find(".tire-size-next");
                this.$tireDeclined = this.$el.find(".tire-recommendations.service-declined");
                this.$tireUpsell = this.$el.find(".tire-recommendations.service-upsell");
                this.$tireQuestion = this.$el.find(".tire-recommendations.panel-questions");
                this.$tireResults = this.$el.find(".tire-recommendations.panel-results");
                this.$tireMissing = this.$el.find(".tire-recommendations.service-missing");

                //Content
                this.$treadDepthContent = this.$el.find('#treadDepthBar');
                this.$alignmentContent = this.$el.find('#alignmentBar');

                //Quote
                this.$finalize = this.$el.find('.inspection-finalize-quote');
                this.$finalizeNav = this.$el.find('.navmenu a[data-target="finalize"]').parents("li");
            };

        /*==========  Define Instances  ==========*/

            /**
             * Creates all of the instances of the model components, ui components, and inspections
             * views that will be need for the controller.
             * @method   createInstances
             * @memberOf controller(inspection-detail)
             * @instance
             */
            controller.prototype.createInstances = function(){
                //Component - Models
                this.validationLibrary = new comp.model.ValidationLibrary();
                this.inspectionDetails = new comp.model.InspectionDetailsRequest();
                this.productQuery = new comp.model.ProductQuery({
                    filterProducts: true
                    ,filterProductsBy: {
                        productIsOnProgram: true
                        ,productIsDealerPromoted: true
                        ,productGroupCode: "TIR"
                    }
                });
                this.recEngineLite = new comp.model.RecEngineLite();
                this.dealerServiceOfferings = new comp.model.DealerServiceOfferingsListRequest();
                this.consumerLead = new comp.model.ConsumerLeadRequest();
                this.consumerLeadSubmission = new comp.model.ConsumerLeadSubmissionRequest();
                this.carlineCatalogYearMakeModelRequest = new comp.model.CarlineCatalogYearMakeModelRequest({contentType: 'text/xml'});
                this.carlineCatalogTrimOptionRequest = new comp.model.CarlineCatalogTrimOptionRequest({contentType: 'text/xml'});
                this.updInspectionHeader = new comp.model.UpdInspectionHeaderRequest();
                this.vinDecode = new comp.model.VinDecodeRequest();
                this.tireSizeQuestions = new comp.model.TireSizeQuestions();
                this.promotions = new comp.model.Promotions();
                this.dealerPricingInstance = new comp.model.DealerPricingRequest();
                this.wheelAlignmentModel = new comp.model.WheelAlignmentModel( this.inspectionDetails );
                this.treadDepthModel = new comp.model.TreadDepthModel( 
                    this.inspectionDetails
                    ,{
                        contentServer : "/content/hunterInspections/"
                        ,uid : dt_utils.getUrlParameterByName("inspection")
                        ,extension : ".jpg"
                    }
                );
                this.contentQuery = new comp.model.ContentQueryRequest({
                    contentType: 'text/xml'
                    ,showList : true
                });

                //Component - UIs
                this.productCompareGroup = new comp.ui.ProductCompareGroup(
                    this.$el
                    ,{}
                    ,{
                        assetsDomain : assetsDomainPath
                        ,dtLang : dtLang.product
                    }
                );

                this.productTilesGroup = new comp.ui.ProductTilesGroup(
                    this.$productList
                    ,{}
                    ,{
                        assetsDomain : assetsDomainPath
                        ,dtLang : dtLang.product
                    }
                );

                this.generalServicesTile = new comp.ui.GeneralServicesTile(
                    this.$serviceList
                    ,[]
                    ,{
                        dtLang: dtLang.product
                    }
                );

                this.wheelAlignmentUI = new comp.ui.WheelAlignment(
                    this.$el.find("#alignmentDetailsWrapper")
                    ,this.wheelAlignmentModel
                    ,{
                        dtLang: dtLang.inspectionDetail
                        ,imgSrc: assetsDomainPath
                    }
                );

                this.treadDepthUI = new comp.ui.TreadDepth(
                    this.$el.find("#tireTechDetailsWrapper")
                    ,this.treadDepthModel
                    ,{
                        dtLang: dtLang.inspectionDetail
                        ,assetsDomain: assetsDomainPath
                    }
                );

                this.dealerPricingModal = new comp.ui.PricingBreakdownModal(
                    {
                        dtLang : dtLang.product
                        ,assetsDomain : assetsDomainPath
                    }
                );

                this.promotionsModal = new comp.ui.PromotionsModal(
                    {
                        dtLang : dtLang.product
                        ,assetsDomain : assetsDomainPath
                    }
                );

                this.modal = new insp.view.Modal(
                    {
                        dtLang : dtLang.product
                        ,assetsDomain : assetsDomainPath
                    }
                );

                //Inspection - Views
                this.inspectionHeader = new insp.view.InspectionHeader(
                    ".inspection-header"
                    ,"/inspections/tmpl/html/inspectionHeader.hbs"
                    ,this.inspectionDetails
                    ,editable
                );

                this.mixedBagQuestions = new insp.view.MixedBagQuestions(
                    ".panel-questions"
                    ,"/inspections/tmpl/html/tireSizes.hbs"
                );

                this.workQuote = new insp.view.WorkQuote(
                    ".work-quote"
                    ,"/inspections/tmpl/html/workQuote.hbs"
                );

                this.inspectionBar = new insp.view.InspectionBar(
                    ".car-deets"
                    ,"/inspections/tmpl/html/inspectionBar.hbs"
                    ,dtLang.inspection.VIN
                );

                this.inspectionQuote = new insp.view.InspectionQuote(
                    '#inspection-detail'
                    ,"/inspections/tmpl/html/inspectionSummary.hbs"
                );

                this.serviceRecommendations = new insp.view.ServiceRecommendations(
                    '.service-recommendations'
                    ,"/inspections/tmpl/html/serviceRecommendations.hbs"
                );

                this.emailModal = new insp.view.EmailModal(
                    "/inspections/tmpl/html/emailModal.hbs"
                    ,this.validationLibrary
                    ,can_cc
                );
            };

        /*==========  Define Events  ==========*/

            /**
             * Creates all of the event listeners for the controller.  Done in one function
             * in order for quicker debugging.
             * @method   setEventListeners
             * @memberOf controller(inspection-detail)
             * @instance
             */
            controller.prototype.setEventListeners = function(){
                var _this = this;

                //Figures out when the first set of requests for data are all done.
                $(document).one("ajaxStop", function(){
                    $(document).one("ajaxStop", function(){
                        _this.setFunctionality.call(_this);
                    });
                    _this.mainCallback.call(_this);
                });

                //Component - UIs - Product Compare Group
                this.$el.on('comp_ui_productcomparegroup.addProductToCompare', function(e,selectedProductData) {
                    _this.addProductToCompare.call(_this,selectedProductData);
                });
                this.$el.on('comp_ui_productcomparegroup.tileClickRemoveFromCompare', function(e,selectedProductData) {
                    _this.removeProductFromCompare.call(_this,selectedProductData);
                });
                this.$el.on('comp_ui_productcomparegroup.removeProductFromCompare', function(e,nugData) {
                    _this.removeProductFromCompare.call(_this,nugData);
                });
                this.$el.on('comp_ui_productcomparegroup.compareNow', function(e,skuList) {
                    window.location.href = '?event=inspections:product-compare&inspection=' + dt_utils.getUrlParameterByName('inspection') + '&sku=' + skuList;
                });

                //Component - UIs - Product Tiles Group
                this.$el.on('comp_ui_producttilesgroup.popover', function(e) {
                    _this.setProductView.call(_this);
                    _this.$el.find('button.comp_ui_btnBtn').popover({trigger : 'click hover'});
                });
                this.$el.on('comp_ui_producttilesgroup.disableComparison', function(e, bool) {
                    _this.disableComparison.call(_this, bool);
                });
                this.$el.on('comp_ui_producttilesgroup.uncheckComparisonFromTile', function(e,index) {
                    _this.uncheckComparisonFromTile.call(_this, index);
                });
                this.$el.on('comp_ui_producttilesgroup.selectProduct', function(e,tireSelected) {
                    tireSelected.qty === 0 ? _this.noQuantitySelected(tireSelected) : _this.handleRemoveOrSelectProduct.call(_this, true, tireSelected);
                });
                this.$el.on('comp_ui_producttilesgroup.removeProduct', function(e,sku) {
                    _this.handleRemoveOrSelectProduct.call(_this, false, sku);
                });
                this.$el.on('comp_ui_producttilesgroup.detailsHandler', function(e,t) {
                    _this.detailsLinkHandler.call(_this, t);
                });
                this.$el.on('comp_ui_producttilesgroup.quantityChange',function(e,obj) {
                    $('.comp_ui_qty',this.$el).popover('hide');
                    _this.quantityChange.call(_this, obj);
                });
                this.$el.on('change', '.comp_ui_qtySelect', function(e) {
                    _this.productQuantityUpdate.call(_this, e);
                });
                this.$el.on('comp_ui_producttilesgroup.promotions',function(e, sku){
                    _this.promotionsHandler.call(_this,sku);
                });
                this.$el.on('comp_ui_producttilesgroup.pricingBreakdown',function(e,sku) {
                    _this.pricingBreakdown.call(_this, sku);
                });
                this.$el.on('click', '.decline-tire-btn',function() {
                    _this.declineUndeclineTires.call(_this, true);
                });
                this.$el.on('click', '.tire-recommendations .btnUndecline', function(){
                    _this.declineUndeclineTires.call(_this, false);
                });
                this.$el.on('click', '.tire-recommendations .btn-upsell', function(){
                    _this.upsellTires.call(_this);
                });


                //Component - UIs - General Services Tiles
                this.$el.on('comp_ui_generalservicestile.buttonHandler', function(e,obj) {
                    _this.updateServices.call(_this, obj);
                });
                this.$el.on('click', '#alignmentDeclineButton',function() {
                    _this.declineUndeclineAlignment.call(_this, true);
                });
                this.$el.on('click', '.alignment-recommendations .btnUndecline', function(){
                    _this.declineUndeclineAlignment.call(_this, false);
                });
                this.$el.on('click', '.alignment-recommendations .btn-upsell', function(){
                    _this.upsellAlignment.call(_this);
                });

                //Component - UIs - Select Group
                this.$el.on("comp_ui_selectGroup.selection", ".carlineYMM", function(){
                    _this.disableTrimOption.call(_this);
                });

                this.$el.on("comp_ui_selectGroup.selectionComplete", '.carlineYMM', function(e, obj){
                    _this.updateTrimDropDown.call(_this, obj);
                });

                //Inspection - Views - Inspection Header
                this.$el.on('insp_view_inspectionHeader.render', function(e){
                    _this.setVehicleDropDowns.call(_this);
                });
                this.$el.on('insp_view_inspectionHeader.change', function(e, obj){
                    _this.handleHeaderChange.call(_this, obj);
                });
                this.$el.on('insp_view_inspectionHeader.updateHeaders', function(e){
                    _this.handleHeaderUpdate.call(_this);
                });

                //Inspection - Views - Mixed Bag Questions
                this.$el.on('insp_view_mixedBagQuestions.answerStaggered', function(e, bool){
                    _this.setStaggered.call(_this, bool);
                });

                this.$el.on('insp_view_mixedBagQuestions.answerTireSize', function(e, val){
                    _this.setTireSize.call(_this, val);
                });

                //Inspection - Views - Work Quote
                this.$el.on('insp_view_workQuote.removeProduct', function(e,sku) {
                    _this.deleteProductFromQuote.call(_this, sku);
                });

                this.$el.on('insp_view_workQuote.removeService', function(e,obj) {
                    _this.updateServices.call(_this, obj);
                });

                //Inspection - Views - Inspection Quote
                this.$el.on('insp_view_InspectionQuote.removeProduct', function(e,sku) {
                    _this.deleteProductFromQuote.call(_this, sku);
                });

                this.$el.on('insp_view_InspectionQuote.removeService', function(e,obj) {
                    _this.updateServices.call(_this, obj);
                });
                this.$el.on('insp_view_InspectionQuote.updateQty', function(e,obj) {
                    _this.updateQtyFromQuote.call(_this, obj);
                });

                //Email Customer
                this.$el.on('click', '.email-customer', function(){
                    var rec = _this.serviceRecommendations.getRecommendationText();
                    var lead = _this.consumerLead.getData();
                    var vehicleText = lead.vehicle.year + " " + lead.vehicle.make + " " + lead.vehicle.model;
                    var inspection = _this.inspectionDetails.getData();
                    _this.emailModal.render(rec, vehicleText, inspection.inspectionuuid, inspection.consumeremail);
                });

                //Unattached
                this.$el.on('click', '.approve', function(){
                    _this.updateStatus(3)
                    _this.updateInspection(_this.inspectionDetails.getData());
                    _this.setFunctionality();
                });

                // this.$el.on('click', '.nav-secondary, .nav-secondary li, .nav-secondary a, .media a, .media img', function(e){
                // this.$el doesn't work on iOS for whatever reason.
                $('.page-wrapper').on('click', '.nav-secondary a, .media a, .media img', function(e){
                    _this.handleNavClick.call(_this, e);
                });

                this.$el.on('click', '.tire-size-next', function(e){
                    _this.submitTireSize.call(_this);
                });
            };

        /*==========  Main Callback  ==========*/

            /**
             * The main callback function for the controller.  This function will execute upon
             * the "ajaxStop" event occuring on the document.  This kicks off building all of the
             * views as at this point, we can be assured that all the fetches for data have been completed.
             * @method   mainCallback
             * @memberOf controller(inspection-detail)
             * @instance
             */
            controller.prototype.mainCallback = function(){
                var _this = this;
                var inspection = this.inspectionDetails.getData();

                //Only proceed if we got inspection details back.
                if (!_.isEmpty(inspection)){                    
                    //Render the inspection header
                    this.inspectionHeader.render();
                    this.inspectionBar.render(inspection);

                    //Update the page title
                    this.setPageTitle(inspection);

                    //Get Alignment & Tread Depth Recommendations
                    this.wheelAlignmentModel.buildTemplateObj(function(waData){
                        _this.treadDepthModel.parseInspection(function(){
                            _this.wheelAlignmentUI.showAlignmentStuffs();
                            _this.treadDepthUI.showTreadDepthStuffs();
                            _this.setInspectionResults();
                            _this.initServiceDisplay();
                            _this.initProductDisplay();
                            _this.updateQuote();                
                        });
                    });
                };
            };

            controller.prototype.setPageTitle = function(inspection){
                var title = dtLang.inspection.Inspections + ": ";
                var attr = [
                    inspection.consumerfirstname
                    ,inspection.consumerlastname
                    ,inspection.vehiclemake
                    ,inspection.vehiclemodel
                ];

                for (var i = 0, len = attr.length; i < len; i++){
                    if ($.trim(attr[i]).length){
                        title += " " + attr[i];
                    };
                };

                document.title = title;
            };

    /*-----  End of 00 - Initializer  ------*/
})(jQuery);

As you can see, the models are setup as null then, after event listeners are established, the models are filled with data (typically via AJAX) and the event listener (usually ajaxStop) is fired with a callback.

Is there a better way to architect this? It seems rather "spaghetti" style. Perhaps there is an existing pattern that will support asynchronous operations but not have the coder jumping between callback after callback to find out what's going on.

\$\endgroup\$
  • \$\begingroup\$ @200_success I have added the real code, but since it was significantly over the character limit, I only included the initializer. Not sure how you'll really be able to read this code without the corresponding models which accept the various callback methods, but you asked for the real code! :) \$\endgroup\$ – user1477388 Feb 10 '16 at 19:11
  • 7
    \$\begingroup\$ Awesome! Looking at your ToC it seems you have material for... 8 posts! (make them on separate days, to leave yourself the time to incorporate feedback from previous posts) \$\endgroup\$ – Mathieu Guindon Feb 10 '16 at 19:14
  • \$\begingroup\$ To be clear, this code, and any of it's potential imperfections, was inherited ;) \$\endgroup\$ – user1477388 Feb 10 '16 at 19:23
  • 2
    \$\begingroup\$ @user1477388 You can call that "job security"! \$\endgroup\$ – Phrancis Feb 10 '16 at 19:25
  • 2
    \$\begingroup\$ Thanks for revising. Now we know the true nature of the beast! \$\endgroup\$ – 200_success Feb 10 '16 at 19:31
2
\$\begingroup\$

Due to the volume of code, I would be inclined not to touch it. Recoding and revalidating would be a lengthy exercise.

I wonder if the way ahead might be to enhance the (already good) comments, to provide better guidance to the existing architecture.

  • controller.prototype.mainCallback (and the way it is called) is effectively the app's "digest cycle". For anyone coming from a declaritive background, particularly Angular, that term will be familiar and something like "This is effectively the app's digest cycle" could sensibly be included in mainCallback's description block.
  • Your own paragraph in the question, "As you can see, the models are ..." is a good introduction to the overall architecture and could be also worked into the mainCallback's description block.

If I was to make any changes to the code at all, the first thing would be to give the overloaded Controller (instance) namespace some internal structure. To make it clearer what's what in the rest of the code-base, you could put :

  • 17 x new comp.model... - into a this.models object,
  • 7 x new comp.ui... - into a this.uis object,
  • 7 x new insp.view... - into a this.views object.

Possibly also the 27 jQuery collections, though the $ prefix already identifies them pretty well.

Then do a lengthy, careful trawl to pick up on the new structures.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.