javascript - jQuery IF-satser för att beräkna avgifter baserat på två rullgardinsmenyer och ett textfält


Detta är ett exempel på en avgiftskalkylator som kommer att köra en live sammanställning av avgifter (jQuery) baserat på vad användaren väljer och svarar. Jag försöker lista ut hur man skapar många IF-satser för att beräkna avgifter baserat på två rullgardinsmenyer och ett textfält.

Det jag försöker åstadkomma är en jQuery IF-funktion som räknar med en onChange="calculateTotal()" när användaren väljer sin födelsemånad (beräkna månader). Detta kommer att räkna hur många månader det är från nu till deras nästa födelsedag. Men jag behöver ha IF-funktionen baserad på bilens rullgardinsmeny (vehiclebody) och vikten (ew) som matas in; dessa tre saker kommer att bestämma priset. Som exemplen nedan och kodsnutten nedan.

Till exempel (tre scenarier):
[1] om 4-12 månader från nu väljs, samt 2-dörrars fordon med en vikt under 2499, kommer priset att vara 28,10 som läggs till i köravgiftskalkylatorn.
[2] om 4-12 månader från nu väljs, samt 2-dörrars fordon med en vikt mellan 2500 och 3499, kommer priset att vara 36,10 som läggs till i köravgiftskalkylatorn.
[3] om 4-12 månader från nu väljs, samt 2-dörrars fordon med en vikt över 3500, kommer priset att vara 46,10 som läggs till i köravgiftskalkylatorn.

Så lång historia kort vill jag ha en IF-funktion som beräknar onChange="calculateTotal()" och var titleFees=getProofOfOwnership() + (Ny IF-funktion); beroende på vald (vehiclebody) & inmatad (ew) och sedan om (monthsToGo) är lika med 4 till 12 blir priset si och så. Om (monthsToGo) är 1,2,3,13,14 eller 15 kommer priset att beräknas proportionerligt så att det också får sitt eget pris.

Om det är 1 månad bort kräver de att användaren betalar för den månaden och hela året, alltså 13 månader, men om det är 2 månader låter de användaren välja antingen 2 månader eller 14 månader, samma med 3 månader eller 15 månader (som du ser på rullgardinsmenyn för att beräkna månader), men när det är 4 månader (betalar de hela beloppet, oavsett om det är 4-12 månader)

Det som är hemskt är att jag kommer att behöva lista ut typ 50 olika kombinationer av detta, så förhoppningsvis blir det lätt för mig att förstå med min extrema brist på kunskap om jQuery.

All hjälp kommer att vara mycket, mycket, mycket uppskattad!! :)

http://jsfiddle.net/fzy9yev3/15/

Se utdraget nedan:

//Calculate Months
(function() {
    var monthsToGo;
    var Fee = makeStruct("monthSect vehicleBody ewSect price");
    var fees = [
        new Fee(/*monthSect*/2, /*vehicleBody*/0, /*ewSect*/0, /*price*/45.10),
        new Fee(3, 1, 0, 55.10),
        new Fee(4, 2, 0, 65.10),
        new Fee(13, 3, 0, 75.10),
        new Fee(14, 4, 0, 85.10),
        new Fee(15, 5, 0, 95.10)
    ];

    $(document).ready(function() {
        $('#month2, #month3').hide();
    });
    
    $(document).on('change', '#ownership input', function(){
        calculateTotal();
        //statedropDown($(this).val());
    });
    
    $(document).on('change', '#month1', function() {
        // ignore blank options
        if ($(this).val() == '') { return; }
        
        var vehicle = $('#vehiclebody').val();
        var ew = $('#ew').val();
        var ewSect = 0;
        var monthSect = 0;
        var titleFees = 0;
        
        if(vehicle == '' || ew == ''){
            alert('Please enter Vehicle Type and/or Empty Weight');
            $(this).val('');
            return;
        }
        
        var today = new Date();
        var birthdate = new Date();
        birthdate.setMonth($('#month1').val());

        monthsToGo = (today.getMonth() < birthdate.getMonth())
            ? birthdate.getMonth() - today.getMonth()
            : (12 - today.getMonth()) + birthdate.getMonth()
        ;
        
        $('#month2').hide();
        $('#month3').hide();

        if (monthsToGo == 1) {
            this.monthsToGo = 13;
            alert(this.monthsToGo);
        } else if (monthsToGo == 2) {
            $('#month2').show();
        } else if (monthsToGo == 3) {
            $('#month3').show();
        } else {
            this.monthsToGo = monthsToGo;
            alert(this.monthsToGo);
        }
        
        if(monthsToGo >= 4 || monthsToGo <= 12){
            monthSect = 4;
            //Set monthsToGo to 4 if it is in this range for the linked list so
            //categorizing is easier
            //6 possibilities for dates -- 2, 3, 4, 13, 14, 15
        }
        else monthSect = monthsToGo;
        
        switch(vehicle){
            case 0://2dr
            case 2://4dr
            case 4://convertible
            case 5://van
                if(ew < 2500) ewSect = 0;
                else if(ew >= 2500 && ew < 3500) ewSect = 1;
                else if(ew >= 3500) ewSect = 2;
                break;
            case 1://pickup
                if(ew < 2000) ew = 0;
                else if(ew >= 2000 && ew <= 3000) ewSect = 1;
                else if(ew > 3000 && ew <= 5000) ewSect = 2;
                break;
            case 3://trucks
                if(ew > 5000 && ew < 6000) ewSect = 0;
                else if(ew >= 6000 && ew < 8000) ewSect = 1;
                else if(ew >= 8000 && ew < 10000) ewSect = 2;
                else if(ew >= 10000 && ew < 15000) ewSect = 3;
                else if(ew >= 15000 && ew < 20000) ewSect = 4;
                else if(ew >= 20000 && ew <= 26000) ewSect = 5;
                else if(ew > 26000 && ew < 35000) ewSect = 6;
                else if(ew >= 35000 && ew < 44000) ewSect = 7;
                else if(ew >= 44000 && ew < 55000) ewSect = 8;
                else if(ew >= 55000 && ew < 62000) ewSect = 9;
                else if(ew >= 62000 && ew < 72000) ewSect = 10;
                else if(ew >= 72000 && ew <= 8000) ewSect = 11;
                break;
        }
        
        console.log('Month Section (monthSect): ' + monthSect + '; Vehicle: ' + vehicle + '; EW Section (ewSect): ' + ewSect);
        
        for(var i = 0; i < fees.length; i++){
            console.log('infor');
            if(fees[i].monthSect == monthSect && fees[i].vehicleBody == vehicle && fees[i].ewSect == ewSect) {
                titleFees = getProofOfOwnership() + fees[i].price;
                console.log('Title Found! ' + titleFees);
                $('#totalPrice').text('Estimated Transfer Fees $' + titleFees);
                break;
            }
        }
        
    });

    $('#month2, #month3').on('change', function() {
        this.monthToGo = $(this).val();
        alert(this.monthToGo);
    });
    
}($))

//Fee Calculator
//Setting Proof of Ownership Prices
 //Set up an associative array
 var title_prices = new Array();
 title_prices["MCO"]=68.25;
 title_prices["FL Title"]=78.25;
 title_prices["OOS Title"]=88.25;
 
 // Proof of Ownership Radio Buttons
function getProofOfOwnership()
{  
    var proofOfOwnership=0;
    //Get a reference to the form id="form"
    var theForm = document.forms["form"];
    //Get a reference to the title the user Chooses name=ownerShip":
    var ownerShip = theForm.elements["ownership"];
    //Here since there are 4 radio buttons ownerShip.length = 4
    //We loop through each radio buttons
    for(var i = 0; i < ownerShip.length; i++)
    {
        //if the radio button is checked
        if(ownerShip[i].checked)
        {
            proofOfOwnership = title_prices[ownerShip[i].value];
            //If we get a match then we break out of this loop
            //No reason to continue if we get a match
            break;
        }
    }
    //We return the proofOfOwnership
    return proofOfOwnership;
}
function calculateTotal()
{

    var titleFees = getProofOfOwnership();
    


    var divobj = document.getElementById('totalPrice');
    divobj.style.display='block';
    divobj.innerHTML = "Estimated Transfer Fees $"+titleFees;

}

function hideTotal()
{
    var divobj = document.getElementById('totalPrice');
    divobj.style.display='none';
}

function makeStruct(names) {
  var names = names.split(' ');
  var count = names.length;
  function constructor() {
    for (var i = 0; i < count; i++) {
      this[names[i]] = arguments[i];
    }
  }
  return constructor;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<form name="form">
    <div id="ownership">
        <label class='radiolabel'><input type="radio"  name="ownership" required="yes" message="Please select proof of ownership." value="MCO"/>Manufacturer's Statement of Origin&nbsp;&nbsp;</label><br/>
        <label class='radiolabel'><input type="radio"  name="ownership" value="FL Title"/>Florida Certificate of Title&nbsp;&nbsp;</label><br/>
                <label class='radiolabel'><input type="radio"  name="ownership" value="OOS Title"/>Out-of-state Certificate of Title&nbsp;&nbsp;</label>
        </div>

    <br/>
    <br/>

<label for='month1'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Calculate Months:</label>
<select name="month1" id="month1" size="1">
    <option value="">Choose a Month</option>
    <option value="0">January</option>
    <option value="1">February</option>
    <option value="2">March</option>
    <option value="3">April</option>
    <option value="4">May</option>
    <option value="5">June</option>
    <option value="6">July</option>
    <option value="7">August</option>
    <option value="8">September</option>
    <option value="9">October</option>
    <option value="10">November</option>
    <option value="11">December</option>
</select>
<select name="month2" id="month2" size="1">
    <option value="">Choose an Option</option>
    <option value="2">2</option>
    <option value="14">14</option>
</select>
<select name="month3" id="month3" size="1">
    <option value="">Choose an Option</option>
    <option value="3">3</option>
    <option value="15">15</option>
</select>
    <br/>
    

<select name="vehiclebody" id="vehiclebody" required="yes" message="Please select body." size="1">
    <option value="">Choose a Vehicle</option>
    <option value="0">2Dr</option>
    <option value="1">Pickup</option>
    <option value="2">4dr</option>
    <option value="3">Truck</option>
    <option value="4">Convertible</option>
    <option value="5">Van</option>
</select>

<label for="ew">Empty Weight:</label>
    <input type="text" name="ew" id="ew"/>
    <br/>
    <br/>
    <div id="totalPrice"></div>
</form>

Svar #1

Jag skulle föreslå att ta ett tillvägagångssätt som inte bara rensar upp din kod, utan också gör det lättare att beräkna, testa, validera och lägga till nya funktioner. Lite uppstädning kan gå långt för att göra din applikation lättare att utöka och felsöka, men viktigare för din situation, lite uppstädning kommer att hjälpa dig att beräkna de värden du letar efter och lösa ditt problem på ett direkt sätt.

Förslag #01: Organisera din kod

Just nu har du några funktioner som försöker göra för mycket på en gång. Funktioner är lättare att testa och felsöka om du håller dem enkla och låter varje funktion "specialisera" sig på något specifikt. Till exempel, i din on.("change") hanterare för din $("#month1") rullgardinsmeny försöker du göra allt på en gång, vilket är där du stöter på problem. Istället kommer du vilja tilldela en .on("change") hanterare till din #month1 rullgardinsmeny, som kommer att avgöra om #month2 eller #month3 behöver visas, och sedan tilldela en separat . on("change")-hanteraren för att samla värdet från dessa två rullgardinsmenyer. Och medan du ändå håller på, kommer du vilja bryta ut all "specialiserad" funktionalitet till sin egen funktion för att hålla din kod ren.

Ett exempel på din #month1 change-hanterare som helt enkelt försöker göra för mycket på en gång:

$(document).on('change', '#month1', function() {
    // 1. Stop if Empty Weight or Vehicle Type are Blank
    // 2. Get the user's birth month
    // 3. Calculate the number of months until the user's Birthday
    // 4. Calculate the monthSect value
    // 5. Calculate the ewSect value
    // 6. Lookup the Proof of Ownership Fees
    // 7. Lookup prorated Title Fees
});

Istället för att låta denna ena funktion försöka utföra alla dessa uppgifter på ett ställe, är det bättre att flytta all 'specialiserad' funktionalitet till enkla, lättsmälta funktioner som var och en har ett tydligt syfte. Du kan skapa funktionsnamn som:

// Lookup the Prorated Fee
getProratedFeeFromFeeList(monthSect, vehicleBody, ewSect);

// Get the "Payment Plan Details" based on user's birth month
getPaymentPlanDetailsBasedOnBirthMonth(birthMonth);

// Lookup the ewSect (Empty Weight Section)
getWeightSectionFromVehicleClassAndEmptyWeight(vehicleClass, emptyWeight);

Observera: Jag gillar att använda långa, beskrivande funktionsnamn så att syftet med funktionen är mycket tydligt, vilket förbättrar kodens läsbarhet. Långa funktionsnamn som dessa lägger inte till särskilt mycket extra skrivande om du använder en kodredigerare som har bra funktioner för kodkomplettering, men för mig personligen njuter jag av fördelen med mycket läsbar, själv-dokumenterande kod.

Förslag #02: Håll dina formulärvärden sparade på ett ställe.

Fortsätter med förslag #1, eftersom ditt formulär är avsett att ha en "live-tally", kommer det att spara dig mycket huvudvärk om du har ett system på plats för att hålla en ögonblicksbild av formulärets värden hela tiden. Du kan enkelt göra detta med ett Javascript-objekt.

Detta är praktiskt av flera skäl:

  1. Det gör alla formulärvärden "globalt tillgängliga" utan att störa det globala namnutrymmet.
  2. Det gör att dina formulärvärden blir enkla att "beräkna" inom din calculateTotal()-funktion eftersom du bara någonsin behöver fråga det här objektet efter värden att sätta in i din ekvation.
  3. Du har en "live-ögonblicksbild" av hela ditt formulär hela tiden. Detta är praktiskt för testning, felsökning, validering eller för att skriva ut formulärresultat till konsolen eller webbläsaren.
  4. Det låter dig fortsätta lägga till funktioner/värden i din avgiftsräknarapp utan att bryta upp ditt arbetsflöde.
  5. Genom att alltid ha en fullständig ögonblicksbild blir det lättare att upptäcka fel i funktionaliteten och se hur dina individuella indata påverkar de andra indatana eller den övergripande beräkningen.

Så jag skulle skapa ett globalt objekt kallat FC (kort för "Fee Calculator") för att lagra alla värden du behöver för att köra dina avgiftsberäkningar. Skapa detta "Fee Calculator Object" på detta sätt:

var FC = FC || {};

Nu kommer detta FC-objekt att vara tillgängligt för alla dina funktioner som har åtkomst till FC:s scope. Att lägga till värden till ditt FC-objekt kan göras på ett av två sätt:

// First method: In FC's Declaration
var FC = { monthSect: 0, vehicleBody: null };

// Second Method: Using dot notation
var FC = {};
FC.monthSect = 0;
FC.vehicleBody = null;

Obs: Tänk på att du alltid kan använda punktnotation för att tilldela värden till ett objekt som redan har deklarerats, så en kombination av dessa två metoder är fullt giltig.

Nu kan du uppdatera och läsa värden från ditt FC-objekt med punktnotation, till exempel:

// Get the monthSect variable
var monthSect = FC.monthSect;

// Assign or update FC.monthSect
FC.monthSect = someNewValue; 

Min lösning på ditt problem

Genom att använda några av de förslag jag nämnde ovan, är detta hur jag skulle gå tillväga för att lösa ditt problem:

Steg #01 : Varje formulärfält lyssnar på ändringar, uppdaterar FC och anropar calculateTotal()

// When the user updates the #month2 or #month3 dropdown
// Update FC.monthSect 
$(document).on('change', '#month2, #month3', function() {
    FC.monthSect = $(this).val();
    calculateTotal();
});

// When someone updates the Empty Weight value
// update FC.emptyWeight 
$(document).on('change blur keyup', '#ew', function() {

    var emptyWeight = $(this).val();
    FC.emptyWeight = emptyWeight ? emptyWeight : null;

    var vehicle = $('#vehiclebody').val();

    if (vehicle && emptyWeight) {
        FC.ewSect = FCUtils.getWeightSectionFromVehicleClassAndEmptyWeight(FC.vehicleBody, FC.emptyWeight);
         FCUtils.getProratedFeeFromFeeList(FC.monthSect, FC.vehicleBody, FC.ewSect);
    }

    calculateTotal();

});


// When the user updates the #month1 drop-down 
// Update FC.monthSect and assign FC.proratedPrice
$(document).on('change', '#month1', function() {

    // Reset to the defaults
    $("#month2").hide();
    $("#month3").hide();
    FC.monthSect = null;

    // ignore blank options
    if ($(this).val() == '') {
        calculateTotal();
        return;
    };

    var selecteduserBirthday = $(this).val();

    var paymentPlanDetails = FCUtils.getPaymentPlanDetailsBasedOnBirthMonth(selecteduserBirthday);
    var monthsToGo = paymentPlanDetails.monthsUntilBirthday;

    if (paymentPlanDetails.paymentPlan == "2or14months") {
        $("#month2").show();
    } else if (paymentPlanDetails.paymentPlan == "3or15months") {
        $("#month3").show();
    }

    // Assign FC.monthSect if monthSect = 4-12 | 1 | 13
    if (monthsToGo >= 4 && monthsToGo <= 12) {
        FC.monthSect = 4;
    } else if (monthsToGo == 1 || monthsToGo == 13) {
        FC.monthSect = 13;
    }

    FCUtils.getProratedFeeFromFeeList(FC.monthSect, FC.vehicleBody, FC.ewSect);
    calculateTotal();

});

Observera: Du kanske har märkt att vissa funktionsnamn föregås av FCUtils, vilket är ett objekt jag har kopplat alla "utility"-funktioner till. Du behöver nödvändigtvis inte göra detta i ditt eget program, du kan bara skriva dina funktioner och kalla på dem som du normalt skulle göra, men i mitt exempelprogram använde jag ett designmönster som kallas The Revealing Module Pattern, vilket hjälper till att gruppera vanlig funktionalitet och är ett ytterligare steg mot att göra koden mer läsbar.

Steg #02 : Fråga FC-objektet i calculateTotal()

Nu när dina formulärfält är självständiga och de alla vet hur de ska uppdatera sitt respektive värde på FC-objektet, är allt din calculateTotal()-funktion behöver göra att fråga FC-objektet om värden för att beräkna:

function calculateTotal() {

    var titleFees =     FC.rvPrice + 
                        FC.titlePrice + 
                        FC.lienPrice + 
                        FC.purchaseDatePrice + 
                        FC.IRFPrice + 
                        FC.proratedPrice + 
                        FC.specialFee;

    FC.totalPrice = titleFees;

    // Now you can update the browser with this data
    $("#totalPrice").text(titleFees);

}

Eftersom calculateTotal()-funktionen anropas vid varje "change"-händelse på ditt formulär, är det den som ger ditt formulär den där "levande" känslan. Du kan också uppdatera andra delar av din app från denna funktion.

Du kan granska en jsfiddle där jag har satt alla dessa idéer i praktiken här. Jag har lagt till en HUD (den "svarta lådan" som visas i det övre högra hörnet) som ger dig en live-översikt av datan som finns i FC-objektet hela tiden. Om HUD:en stör, helt enkelt "klicka för att dra" den runt på skärmen för att flytta den ur vägen.

Hoppas detta hjälper! Lycka till!

Svar #2

Du är på rätt spår när du inte vill skriva ett enormt IF-ELSE för hand - särskilt om det blir repetitivt. (du har redan missat en "0" i sista raden, och ibland är det ">" och ibland ">=").

Det du kan göra är att sätta upp en metod för att spara olika tröskelvärden och värdet när tröskelvärdet överskrids. Så koda inte användningsfallet utan algoritmen. Du kan till och med bli mer kreativ, till exempel ändra this.get=function(wt) till this.get=function(wt, default_value) och mata in ett standardvärde. Och så vidare.

var LevelFinder = function(){
    this.range = []; // always sorted when new values added
    this.add = function( over_wt, sel_value ) {
        this.range.push({over_wt:over_wt, sel_value:sel_value});
        this.range.sort(function(x,y){return x.over_wt - y.over_wt;});
    };
    this.get = function(wt){
        var x, i;
        for ( i = 0; i < this.range.length; ++i ) {
            if ( this.range[i].over_wt > wt ) {
                if ( i > 0 ) x = this.range[i-1].sel_value;
                return x;
            }
        }
        return this.range[i-1].sel_value;
    }
} ;
var ewFinder = new LevelFinder();
ewFinder.add(10000, 3);
ewFinder.add(20000, 5);
ewFinder.add(26000, 6);
ewFinder.add(15000, 4); // this is entered out of sequence but will get sorted by the adder

ewFinder.get(17000); // produces 4 in console
ewFinder.get(22000); // 5
ewFinder.get(0); // undefined
ewFinder.get(999000); // 6

Hoppas detta hjälper.