Gravity Forms custom field

User
Assistant
You’re seeing only the hidden value saved because Gravity Forms only stores inputs whose names follow its expected pattern: `input_.`. Your current HTML uses plain inputs (no GF-style names), so GF ignores them.

Below are the **minimal changes** to make your custom field a proper **multi-input** field so that GF saves every selection/value separately (height unit, height cm/ft+in, weight unit, weight kg/st+lb, and the computed BMI).

### 1) Define sub-inputs (so GF knows what to save)
**Add** a constructor inside `GF_Field_Bmi_Calculator`:

public function __construct($data = array()) {
    parent::__construct($data);
    // Define sub-inputs with labels; IDs are <fieldId>.<index>
    $this->inputs = array(
        array('id' => "{$this->id}.1", 'label' => __('Height Unit', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.2", 'label' => __('Height (cm)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.3", 'label' => __('Height (feet)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.4", 'label' => __('Height (inches)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.5", 'label' => __('Weight Unit', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.6", 'label' => __('Weight (kg)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.7", 'label' => __('Weight (stones)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.8", 'label' => __('Weight (pounds)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.9", 'label' => __('BMI', 'bmi-calculator-gf')),
    );
}

*(This tells GF to create/save separate entry meta for `.1` … `.9`.)*

### 2) Name your inputs with Gravity Forms’ naming convention
**Replace** your `get_field_input()` markup so every control uses `name="input_{$this->id}.X"` (and keep the hidden BMI too). Also prefill checked/values from posted data so it works on validation failures:

public function get_field_input($form, $value = '', $entry = null) {
    $field_id = (int) $this->id;

    // Pull previously posted values so UI reflects them after validation errors
    $h_unit   = rgpost("input_{$field_id}.1") ?: 'cm';
    $h_cm     = rgpost("input_{$field_id}.2");
    $h_ft     = rgpost("input_{$field_id}.3");
    $h_in     = rgpost("input_{$field_id}.4");
    $w_unit   = rgpost("input_{$field_id}.5") ?: 'kg';
    $w_kg     = rgpost("input_{$field_id}.6");
    $w_st     = rgpost("input_{$field_id}.7");
    $w_lb     = rgpost("input_{$field_id}.8");
    $bmi_val  = rgpost("input_{$field_id}.9");

    ob_start(); ?>
    <div class="bmi-calc-wrap" data-field-id="<?php echo esc_attr($field_id); ?>">

        <div class="bmi-calc-section" style="margin-top:25px;">
            <label class='gfield_label gform-field-label'><?php _e('Enter your height', 'bmi-calculator-gf'); ?></label>
            <div class="bmi-calc-row">
                <label>
                    <input type="radio"
                           name="input_<?php echo esc_attr($field_id); ?>.1"
                           value="cm" <?php checked($h_unit, 'cm'); ?>> cm
                </label>
                <label>
                    <input type="radio"
                           name="input_<?php echo esc_attr($field_id); ?>.1"
                           value="ft_in" <?php checked($h_unit, 'ft_in'); ?>> feet/inches
                </label>
            </div>
            <div class="bmi-calc-row bmi-height-cm" style="<?php echo ($h_unit==='cm') ? '' : 'display:none;'; ?>">
                <input type="number" step="any" min="0"
                       class="bmi-height-input"
                       name="input_<?php echo esc_attr($field_id); ?>.2"
                       placeholder="0"
                       value="<?php echo esc_attr($h_cm); ?>">
            </div>
            <div class="bmi-calc-row bmi-height-ftin" style="<?php echo ($h_unit==='ft_in') ? '' : 'display:none;'; ?>">
                <input type="number" min="0"
                       class="bmi-height-feet"
                       name="input_<?php echo esc_attr($field_id); ?>.3"
                       placeholder="Feet"
                       value="<?php echo esc_attr($h_ft); ?>">
                <input type="number" min="0"
                       class="bmi-height-inches"
                       name="input_<?php echo esc_attr($field_id); ?>.4"
                       placeholder="Inches"
                       value="<?php echo esc_attr($h_in); ?>">
            </div>
        </div>

        <div class="bmi-calc-section">
            <label class='gfield_label gform-field-label'><?php _e('Enter your weight', 'bmi-calculator-gf'); ?></label>
            <div class="bmi-calc-row">
                <label>
                    <input type="radio"
                           name="input_<?php echo esc_attr($field_id); ?>.5"
                           value="kg" <?php checked($w_unit, 'kg'); ?>> kg
                </label>
                <label>
                    <input type="radio"
                           name="input_<?php echo esc_attr($field_id); ?>.5"
                           value="st_lb" <?php checked($w_unit, 'st_lb'); ?>> stones/pounds
                </label>
            </div>
            <div class="bmi-calc-row bmi-weight-kg" style="<?php echo ($w_unit==='kg') ? '' : 'display:none;'; ?>">
                <input type="number" step="any" min="0"
                       class="bmi-weight-input"
                       name="input_<?php echo esc_attr($field_id); ?>.6"
                       placeholder="0"
                       value="<?php echo esc_attr($w_kg); ?>">
            </div>
            <div class="bmi-calc-row bmi-weight-stlb" style="<?php echo ($w_unit==='st_lb') ? '' : 'display:none;'; ?>">
                <input type="number" min="0"
                       class="bmi-weight-stones"
                       name="input_<?php echo esc_attr($field_id); ?>.7"
                       placeholder="Stones"
                       value="<?php echo esc_attr($w_st); ?>">
                <input type="number" min="0"
                       class="bmi-weight-pounds"
                       name="input_<?php echo esc_attr($field_id); ?>.8"
                       placeholder="Pounds"
                       value="<?php echo esc_attr($w_lb); ?>">
            </div>
        </div>

        <div class="bmi-calc-result">
            <label class='gfield_label gform-field-label'><?php _e('Your BMI:', 'bmi-calculator-gf'); ?></label>
            <span class="bmi-calc-value"><?php echo $bmi_val !== null && $bmi_val !== '' ? esc_html($bmi_val) : '--'; ?></span>
        </div>

        <!-- Hidden BMI value saved as sub-input .9 -->
        <input type="hidden"
               name="input_<?php echo esc_attr($field_id); ?>.9"
               class="bmi-calc-hidden"
               value="<?php echo esc_attr($bmi_val); ?>">
    </div>
    <?php
    return ob_get_clean();
}

### 3) (Optional but nice) show sublabels in the editor
**Extend** your field settings so the sublabels are visible/configurable (optional):

public function get_form_editor_field_settings() {
    return array(
        'label_setting',
        'description_setting',
        'css_class_setting',
        'sub_labels_setting', // optional
    );
}

### 4) No change needed to your JS/CSS enqueues
Keep your existing enqueues. Just ensure your `js/bmi-calculator.js` still writes the computed BMI into the hidden input which is now named `input_.9` (your selector `.bmi-calc-hidden` is fine).

If your JS is toggling visibility, it should also toggle based on the **radio values now tied to `input_.1` and `input_.5`** (your current selectors should still work).

## What you’ll get in the entry
– `input_.1` → height unit (`cm` or `ft_in`)
– `input_.2` → height (cm)
– `input_.3` → height (feet)
– `input_.4` → height (inches)
– `input_.5` → weight unit (`kg` or `st_lb`)
– `input_.6` → weight (kg)
– `input_.7` → weight (stones)
– `input_.8` → weight (pounds)
– `input_.9` → BMI (your computed value)

Gravity Forms will now save **all** of these automatically—no extra hooks needed.

User
we only want to save cm or feet/inches and kg or stone/pound
User
not radio selections
Assistant
Got it 👍 — you don’t want every single input stored, only the **actual entered height and weight values**, regardless of unit.
So the DB entry should have:

– One input for **height** (cm *or* ft+in converted to cm, whichever was used)
– One input for **weight** (kg *or* st+lb converted to kg, whichever was used)
– (Optional) the BMI value

The radio selections are just UI helpers; they don’t need to be saved.

### 🔧 How to do it
The cleanest way in GF is to keep your custom radios/fields for the UI, but only save into **two hidden inputs** that follow GF’s naming pattern:

– `input_.1` → Height (normalized, e.g. cm)
– `input_.2` → Weight (normalized, e.g. kg)
– (Optional) `input_.3` → BMI

Then, in your JavaScript (`bmi-calculator.js`), when the user enters data and selects units:

1. Convert height to cm
– If unit = `cm` → just copy.
– If unit = `ft_in` → `(feet * 30.48) + (inches * 2.54)`.

2. Convert weight to kg
– If unit = `kg` → just copy.
– If unit = `st_lb` → `(stones * 6.35029) + (pounds * 0.453592)`.

3. Compute BMI = `kg / (m²)` where `m = cm / 100`.

4. Write the results into the hidden GF inputs (`.bmi-height-hidden`, `.bmi-weight-hidden`, `.bmi-bmi-hidden`).

### Example changes

#### PHP — define only 3 sub-inputs

public function __construct($data = array()) {
    parent::__construct($data);
    $this->inputs = array(
        array('id' => "{$this->id}.1", 'label' => __('Height (cm)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.2", 'label' => __('Weight (kg)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.3", 'label' => __('BMI', 'bmi-calculator-gf')),
    );
}

#### PHP — in your markup, add hidden fields

<input type="hidden" name="input_<?php echo esc_attr($field_id); ?>.1" class="bmi-height-hidden" value="">
<input type="hidden" name="input_<?php echo esc_attr($field_id); ?>.2" class="bmi-weight-hidden" value="">
<input type="hidden" name="input_<?php echo esc_attr($field_id); ?>.3" class="bmi-bmi-hidden" value="">

*(Keep all your visible radios and number fields the same — they’re just for user input, not saved directly.)*

#### JS — on input change, normalize and set hidden values

jQuery(document).on('input change', '.bmi-calc-wrap', function() {
  var wrap = jQuery(this);

  // Height
  var hUnit = wrap.find('input[name^="bmi_height_unit"]:checked').val();
  var cm = 0;
  if (hUnit === 'cm') {
    cm = parseFloat(wrap.find('.bmi-height-input').val()) || 0;
  } else {
    var ft = parseFloat(wrap.find('.bmi-height-feet').val()) || 0;
    var inch = parseFloat(wrap.find('.bmi-height-inches').val()) || 0;
    cm = (ft * 30.48) + (inch * 2.54);
  }
  wrap.find('.bmi-height-hidden').val(cm.toFixed(2));

  // Weight
  var wUnit = wrap.find('input[name^="bmi_weight_unit"]:checked').val();
  var kg = 0;
  if (wUnit === 'kg') {
    kg = parseFloat(wrap.find('.bmi-weight-input').val()) || 0;
  } else {
    var st = parseFloat(wrap.find('.bmi-weight-stones').val()) || 0;
    var lb = parseFloat(wrap.find('.bmi-weight-pounds').val()) || 0;
    kg = (st * 6.35029) + (lb * 0.453592);
  }
  wrap.find('.bmi-weight-hidden').val(kg.toFixed(2));

  // BMI
  var bmi = 0;
  if (cm > 0 && kg > 0) {
    bmi = kg / Math.pow(cm/100, 2);
  }
  wrap.find('.bmi-bmi-hidden').val(bmi.toFixed(1));
  wrap.find('.bmi-calc-value').text(bmi > 0 ? bmi.toFixed(1) : '--');
});

✅ Now GF will save just **Height (cm)**, **Weight (kg)**, and **BMI** into the entry.
The radio choices and raw ft/in or st/lb fields are only UI helpers, not stored.

Do you also want the **raw values** (like “5ft 7in” / “10st 5lb”) saved alongside the normalized cm/kg values, or just the normalized ones?

User
also ofcoure
Assistant
Exactly 👍 — Gravity Forms always expects a **root input** named
`input_` (no `.subId`) in addition to any sub-inputs.

– That root value is what shows up in merge tags like `{Field:1}`.
– Sub-inputs (`.1`, `.2`, `.3`) are stored separately in the entry detail.

So you should **keep your existing hidden**:

<input type="hidden"
       name="input_<?php echo esc_attr($this->id); ?>"
       class="bmi-calc-hidden"
       value="">

and also add the 3 sub-inputs for **height, weight, BMI**:

<input type="hidden" name="input_<?php echo esc_attr($this->id); ?>.1"
       class="bmi-height-hidden" value="">
<input type="hidden" name="input_<?php echo esc_attr($this->id); ?>.2"
       class="bmi-weight-hidden" value="">
<input type="hidden" name="input_<?php echo esc_attr($this->id); ?>.3"
       class="bmi-bmi-hidden" value="">

### How to use them together
– **Root hidden (`input_`)** → you can store a **summary string** (e.g. `"Height: 170 cm, Weight: 65 kg, BMI: 22.5"`).
– **Sub-inputs (`.1`, `.2`, `.3`)** → store **normalized numbers** (cm, kg, BMI).

Update your JS so both get filled:

// After calculating cm, kg, bmi…
wrap.find('.bmi-height-hidden').val(cm.toFixed(2));
wrap.find('.bmi-weight-hidden').val(kg.toFixed(2));
wrap.find('.bmi-bmi-hidden').val(bmi.toFixed(1));

// Root field: nice summary string
var summary = 'Height: ' + cm.toFixed(2) + ' cm, ' +
              'Weight: ' + kg.toFixed(2) + ' kg, ' +
              'BMI: ' + (bmi > 0 ? bmi.toFixed(1) : '--');
wrap.find('.bmi-calc-hidden').val(summary);

✅ Result:
– Entry has clean numeric data in sub-inputs (good for reports/exports).
– Entry also has a readable combined string in the main field (good for confirmations/notifications).

Do you want me to adjust the PHP `get_field_input()` code for you so it includes all 4 hidden fields correctly, or do you just need the JS update?

User
jQuery(function ($) {
let userEditedWeightStLb = false;
let userEditedHeightFtIn = false;

function kgToStLb(kg) {
const st = Math.floor(kg * 2.20462 / 14);
const lb = (kg * 2.20462) – (st * 14);
return { st: st, lb: Math.round(lb * 100) / 100 };
}

function stLbToKg(st, lb) {
return (parseFloat(st) * 6.35029) + (parseFloat(lb) * 0.453592);
}

function cmToFtIn(cm) {
const totalIn = cm / 2.54;
const ft = Math.floor(totalIn / 12);
const inch = totalIn – (ft * 12);
return { ft: ft, inch: Math.round(inch * 100) / 100 };
}

function ftInToCm(ft, inch) {
return (parseFloat(ft) * 30.48) + (parseFloat(inch) * 2.54);
}

function calcBMI(kg, cm) {
if (!kg || !cm) return '';
const m = cm / 100;
const bmi = kg / (m * m);
return (bmi > 0 && isFinite(bmi)) ? bmi.toFixed(1) : '';
}

function updateBMI($wrap) {
let kg, cm;

// === Weight ===
const weightUnit = $wrap.find('input[name^=bmi_weight_unit_]:checked').val();
if (weightUnit === 'kg') {
kg = parseFloat($wrap.find('.bmi-weight-input').val());
if (!isNaN(kg)) {
const stlb = kgToStLb(kg);
$wrap.find('.bmi-weight-stones').val(stlb.st);
$wrap.find('.bmi-weight-pounds').val(stlb.lb);
}
} else {
const st = parseFloat($wrap.find('.bmi-weight-stones').val()) || 0;
const lb = parseFloat($wrap.find('.bmi-weight-pounds').val()) || 0;
const convertedKg = stLbToKg(st, lb);

if (userEditedWeightStLb && !isNaN(convertedKg)) {
kg = convertedKg;
$wrap.find('.bmi-weight-input').val(kg.toFixed(2));
} else {
kg = parseFloat($wrap.find('.bmi-weight-input').val());
}
}

// === Height ===
const heightUnit = $wrap.find('input[name^=bmi_height_unit_]:checked').val();
if (heightUnit === 'cm') {
cm = parseFloat($wrap.find('.bmi-height-input').val());
if (!isNaN(cm)) {
const ftin = cmToFtIn(cm);
$wrap.find('.bmi-height-feet').val(ftin.ft);
$wrap.find('.bmi-height-inches').val(ftin.inch);
}
} else {
const ft = parseFloat($wrap.find('.bmi-height-feet').val()) || 0;
const inch = parseFloat($wrap.find('.bmi-height-inches').val()) || 0;
const convertedCm = ftInToCm(ft, inch);

if (userEditedHeightFtIn && !isNaN(convertedCm)) {
cm = convertedCm;
$wrap.find('.bmi-height-input').val(cm.toFixed(2));
} else {
cm = parseFloat($wrap.find('.bmi-height-input').val());
}
}

// === BMI ===
const bmi = calcBMI(kg, cm);
console.log('BMI input changed:', { kg, cm, bmi });
$wrap.find('.bmi-calc-value').text(bmi || '–');
$wrap.find('.bmi-calc-hidden').val(bmi);

// Reset user input flags
userEditedWeightStLb = false;
userEditedHeightFtIn = false;
}

// === Track manual edits ===
$(document).on('input', '.bmi-weight-stones, .bmi-weight-pounds', function () {
userEditedWeightStLb = true;
});
$(document).on('input', '.bmi-height-feet, .bmi-height-inches', function () {
userEditedHeightFtIn = true;
});

// === Trigger BMI update on input ===
$(document).on('input change', '.bmi-calc-wrap input', function () {
const $wrap = $(this).closest('.bmi-calc-wrap');
updateBMI($wrap);
});

// === Toggle weight units ===
$(document).on('change', 'input[name^=bmi_weight_unit_]', function () {
const $wrap = $(this).closest('.bmi-calc-wrap');
if ($(this).val() === 'st_lb') {
$wrap.find('.bmi-weight-stlb').show();
$wrap.find('.bmi-weight-kg').hide();
} else {
$wrap.find('.bmi-weight-stlb').hide();
$wrap.find('.bmi-weight-kg').show();
}
updateBMI($wrap);
});

// === Toggle height units ===
$(document).on('change', 'input[name^=bmi_height_unit_]', function () {
const $wrap = $(this).closest('.bmi-calc-wrap');
if ($(this).val() === 'ft_in') {
$wrap.find('.bmi-height-ftin').show();
$wrap.find('.bmi-height-cm').hide();
} else {
$wrap.find('.bmi-height-ftin').hide();
$wrap.find('.bmi-height-cm').show();
}
updateBMI($wrap);
});

// === Initial state setup ===
$('.bmi-calc-wrap').each(function () {
const $wrap = $(this);

// Show/hide fields based on checked units
if ($wrap.find('input[name^=bmi_weight_unit_]:checked').val() === 'st_lb') {
$wrap.find('.bmi-weight-stlb').show();
$wrap.find('.bmi-weight-kg').hide();
}

if ($wrap.find('input[name^=bmi_height_unit_]:checked').val() === 'ft_in') {
$wrap.find('.bmi-height-ftin').show();
$wrap.find('.bmi-height-cm').hide();
}

updateBMI($wrap);
});
});
this is the js update code here

Assistant
Nice—your JS is almost there. You’re calculating `kg`, `cm`, and `bmi`; you just need to **write them into the GF hidden sub-inputs** (and optionally a readable summary in the root hidden).

Add the 4 hidden inputs in your PHP markup (if you haven’t already):

<input type="hidden" name="input_<?php echo esc_attr($this->id); ?>" class="bmi-calc-hidden" value="">
<input type="hidden" name="input_<?php echo esc_attr($this->id); ?>.1" class="bmi-height-hidden" value="">
<input type="hidden" name="input_<?php echo esc_attr($this->id); ?>.2" class="bmi-weight-hidden" value="">
<input type="hidden" name="input_<?php echo esc_attr($this->id); ?>.3" class="bmi-bmi-hidden" value="">

Then, in your JS, **patch `updateBMI($wrap)`** (only additions shown):

function updateBMI($wrap) {
    let kg, cm;

    // ... your existing weight logic ...

    // ... your existing height logic ...

    // === BMI ===
    const bmi = calcBMI(kg, cm);
    $wrap.find('.bmi-calc-value').text(bmi || '--');

    // ---- NEW: write normalized values into GF hidden sub-inputs ----
    const hasKG = Number.isFinite(kg) && kg > 0;
    const hasCM = Number.isFinite(cm) && cm > 0;
    const hasBMI = !!bmi;

    $wrap.find('.bmi-height-hidden').val(hasCM ? cm.toFixed(2) : '');
    $wrap.find('.bmi-weight-hidden').val(hasKG ? kg.toFixed(2) : '');
    $wrap.find('.bmi-bmi-hidden').val(hasBMI ? bmi : '');

    // Root hidden: human-readable summary (optional)
    const summary = (hasCM || hasKG || hasBMI)
      ? `Height: ${hasCM ? cm.toFixed(2) + ' cm' : '--'}, `
        + `Weight: ${hasKG ? kg.toFixed(2) + ' kg' : '--'}, `
        + `BMI: ${hasBMI ? bmi : '--'}`
      : '';
    $wrap.find('.bmi-calc-hidden').val(summary);

    // Reset user input flags
    userEditedWeightStLb = false;
    userEditedHeightFtIn = false;
}

That’s it.
Gravity Forms will now save **only**:
– `.1` → Height (cm, normalized)
– `.2` → Weight (kg, normalized)
– `.3` → BMI
…and **not** the radio selections or raw ft/in or st/lb values.

User
give me updated updateBMI full code
Assistant
function updateBMI($wrap) {
    let kg, cm;

    // === Weight ===
    const weightUnit = $wrap.find('input[name^=bmi_weight_unit_]:checked').val();
    if (weightUnit === 'kg') {
        kg = parseFloat($wrap.find('.bmi-weight-input').val());
        if (!isNaN(kg)) {
            const stlb = kgToStLb(kg);
            $wrap.find('.bmi-weight-stones').val(stlb.st);
            $wrap.find('.bmi-weight-pounds').val(stlb.lb);
        }
    } else {
        const st = parseFloat($wrap.find('.bmi-weight-stones').val()) || 0;
        const lb = parseFloat($wrap.find('.bmi-weight-pounds').val()) || 0;
        const convertedKg = stLbToKg(st, lb);

        if (userEditedWeightStLb && Number.isFinite(convertedKg)) {
            kg = convertedKg;
            $wrap.find('.bmi-weight-input').val(kg.toFixed(2));
        } else {
            kg = parseFloat($wrap.find('.bmi-weight-input').val());
        }
    }

    // === Height ===
    const heightUnit = $wrap.find('input[name^=bmi_height_unit_]:checked').val();
    if (heightUnit === 'cm') {
        cm = parseFloat($wrap.find('.bmi-height-input').val());
        if (!isNaN(cm)) {
            const ftin = cmToFtIn(cm);
            $wrap.find('.bmi-height-feet').val(ftin.ft);
            $wrap.find('.bmi-height-inches').val(ftin.inch);
        }
    } else {
        const ft = parseFloat($wrap.find('.bmi-height-feet').val()) || 0;
        const inch = parseFloat($wrap.find('.bmi-height-inches').val()) || 0;
        const convertedCm = ftInToCm(ft, inch);

        if (userEditedHeightFtIn && Number.isFinite(convertedCm)) {
            cm = convertedCm;
            $wrap.find('.bmi-height-input').val(cm.toFixed(2));
        } else {
            cm = parseFloat($wrap.find('.bmi-height-input').val());
        }
    }

    // === BMI ===
    const bmi = calcBMI(kg, cm);
    $wrap.find('.bmi-calc-value').text(bmi || '--');

    // === Write normalized values to GF hidden sub-inputs ===
    const hasKG  = Number.isFinite(kg) && kg > 0;
    const hasCM  = Number.isFinite(cm) && cm > 0;
    const hasBMI = !!bmi;

    $wrap.find('.bmi-height-hidden').val(hasCM ? cm.toFixed(2) : '');
    $wrap.find('.bmi-weight-hidden').val(hasKG ? kg.toFixed(2) : '');
    $wrap.find('.bmi-bmi-hidden').val(hasBMI ? bmi : '');

    // Root hidden: human-readable summary (optional)
    const summary = (hasCM || hasKG || hasBMI)
        ? `Height: ${hasCM ? cm.toFixed(2) + ' cm' : '--'}, ` +
          `Weight: ${hasKG ? kg.toFixed(2) + ' kg' : '--'}, ` +
          `BMI: ${hasBMI ? bmi : '--'}`
        : '';
    $wrap.find('input[name^="input_"][class~="bmi-calc-hidden"]').val(summary);

    // Reset user input flags
    userEditedWeightStLb = false;
    userEditedHeightFtIn = false;
}
User
but now in view entry in bmi calcuaiton values shows Array we want to shwo height,weight and bmi value seaperatli in view entry page
Assistant
You’re seeing **Array** because Gravity Forms is treating your field as multi-input and the default renderer prints an array. Fix it by overriding the field’s display methods so the entry view (and exports/merge tags) return a readable string, while the three sub-inputs still save as separate values.

Add these methods **inside your `GF_Field_Bmi_Calculator` class** (keep your 3 sub-inputs `.1/.2/.3`):

// 1) Make sure you defined sub-inputs (height cm, weight kg, BMI)
public function __construct($data = array()) {
    parent::__construct($data);
    $this->inputs = array(
        array('id' => "{$this->id}.1", 'label' => __('Height (cm)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.2", 'label' => __('Weight (kg)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.3", 'label' => __('BMI', 'bmi-calculator-gf')),
    );
}
// 2) What shows on "View Entry" for the main field row
public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) {
    // $value may be an array for multi-input fields. Normalize it.
    $field_id = (string) $this->id;

    $h = '';
    $w = '';
    $b = '';

    if ( is_array( $value ) ) {
        // Try keys like "12.1", "12.2", "12.3"
        $h = rgar( $value, "{$field_id}.1" );
        $w = rgar( $value, "{$field_id}.2" );
        $b = rgar( $value, "{$field_id}.3" );

        // Fallback: some GF versions pass simple numeric indexes
        if ( $h === null && isset( $value[1] ) ) $h = $value[1];
        if ( $w === null && isset( $value[2] ) ) $w = $value[2];
        if ( $b === null && isset( $value[3] ) ) $b = $value[3];
    } else {
        // If a summary string was stored in the root input, just show it.
        return esc_html( (string) $value );
    }

    $parts = array();
    if ( $h !== '' && $h !== null ) $parts[] = sprintf( __( 'Height: %s cm', 'bmi-calculator-gf' ), esc_html( $h ) );
    if ( $w !== '' && $w !== null ) $parts[] = sprintf( __( 'Weight: %s kg', 'bmi-calculator-gf' ), esc_html( $w ) );
    if ( $b !== '' && $b !== null ) $parts[] = sprintf( __( 'BMI: %s', 'bmi-calculator-gf' ), esc_html( $b ) );

    $out = implode( $format === 'html' ? '<br/>' : ' | ', $parts );
    return $format === 'html' ? $out : wp_strip_all_tags( $out );
}
// 3) What shows in CSV/exports for the main field (one cell)
public function get_value_export( $entry, $input_id = '', $use_text = false, $delimiter = ',' ) {
    $h = rgar( $entry, "{$this->id}.1" );
    $w = rgar( $entry, "{$this->id}.2" );
    $bmi = rgar( $entry, "{$this->id}.3" );

    $parts = array();
    if ( $h !== '' && $h !== null ) $parts[] = "Height(cm): {$h}";
    if ( $w !== '' && $w !== null ) $parts[] = "Weight(kg): {$w}";
    if ( $bmi !== '' && $bmi !== null ) $parts[] = "BMI: {$bmi}";

    return implode( ' | ', $parts );
}
// 4) What shows for merge tags like {Field:ID}
public function get_value_merge_tag( $value, $input_id = '', $modifiers = null, $field = null, $raw_value = null, $url_encode = false, $esc_html = false, $format = 'text' ) {
    // Prefer root summary if you set it via JS; else build from sub-inputs
    if ( is_string( $value ) && $value !== '' ) {
        return $value;
    }

    $entry = GFFormsModel::get_current_lead();
    $h = is_array($entry) ? rgar( $entry, "{$this->id}.1" ) : '';
    $w = is_array($entry) ? rgar( $entry, "{$this->id}.2" ) : '';
    $b = is_array($entry) ? rgar( $entry, "{$this->id}.3" ) : '';

    $summary = trim( sprintf(
        'Height: %s cm, Weight: %s kg, BMI: %s',
        $h !== '' ? $h : '--',
        $w !== '' ? $w : '--',
        $b !== '' ? $b : '--'
    ), ', ' );

    if ( $esc_html )  $summary = esc_html( $summary );
    if ( $url_encode ) $summary = urlencode( $summary );

    return $summary;
}
// 5) (Nice to have) Entries list table cell (avoid "Array" there too)
add_filter( 'gform_entries_field_value', function( $value, $form_id, $field_id, $entry ) {
    // $field_id may be "12" (root). Detect our field by type.
    $form = GFAPI::get_form( $form_id );
    $field = GFFormsModel::get_field( $form, $field_id );
    if ( $field && $field->type === 'bmi_calculator' ) {
        $h = rgar( $entry, "{$field_id}.1" );
        $w = rgar( $entry, "{$field_id}.2" );
        $b = rgar( $entry, "{$field_id}.3" );
        $parts = array();
        if ( $h !== '' && $h !== null ) $parts[] = "H: {$h}cm";
        if ( $w !== '' && $w !== null ) $parts[] = "W: {$w}kg";
        if ( $b !== '' && $b !== null ) $parts[] = "BMI: {$b}";
        return implode( ' | ', $parts );
    }
    return $value;
}, 10, 4 );

### What this gives you
– **Single Entry view**: your main field line shows `Height: … / Weight: … / BMI: …` instead of `Array`.
– **Sub-inputs** still appear individually (Height (cm), Weight (kg), BMI) because `$this->inputs` is set.
– **Entries list table**: friendly one-line summary.
– **Exports**: one readable cell for the main field; plus individual columns for `.1/.2/.3` if you include them.

No database/schema change needed—just add these methods and you’re set.

User
and in Main entrie table its shoing Height I want only show BMi result on main entry table
Assistant
Perfect — Gravity Forms lets you control **what shows in the Entries List table** (the “main table”) separately from what’s stored.

Right now GF shows the **first sub-input** (`.1`, Height) in the entries list column.
To override this so only the **BMI result** shows in the main table column:

### ✅ Inside your `GF_Field_Bmi_Calculator` class add:

// Control what shows in the Entries List table (the main column)
public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) {
    // Always show just the BMI sub-input (.3)
    $bmi = rgar( $entry, "{$this->id}.3" );
    return $bmi !== '' && $bmi !== null ? esc_html( $bmi ) : '--';
}

### 🔎 What this does
– The **entry detail page** will still show Height, Weight, BMI separately (because of the `get_value_entry_detail()` override).
– The **Entries List (main table)** will show only the BMI result (e.g. `22.5`).
– Export still has `.1`, `.2`, `.3` individually.

👉 If you also want to label that column **“BMI”** instead of the field label “BMI Calculator”, you can just rename your field label in the form editor (e.g. `BMI`). The editor label becomes the table header.

Would you like me to also update the `get_value_entry_detail()` so the **main field line** in the single entry view shows *only BMI* too, or do you want Height/Weight/BMI there together?

User
coluln still says Height
Assistant
You’re seeing the header “Height” because Gravity Forms uses the **first sub-input** of a multi-input field for the Entries list column header. Two easy fixes—pick one.

### Option A (simplest): make BMI the first sub-input
Reorder `$this->inputs` in your field’s constructor so `.3` (BMI) comes first. The Entries list will then use **BMI** as the column header and value.

public function __construct($data = array()) {
    parent::__construct($data);

    // Put BMI first so the main entries table uses it
    $this->inputs = array(
        array('id' => "{$this->id}.3", 'label' => __('BMI', 'bmi-calculator-gf')),          // first -> used in entries list
        array('id' => "{$this->id}.1", 'label' => __('Height (cm)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.2", 'label' => __('Weight (kg)', 'bmi-calculator-gf')),
    );
}

*(Keep your existing save/display logic as is. No other changes needed.)*

### Option B (per-form control): force the entries list to show only BMI
If you prefer not to change the input order, target the Entries page columns via filters. Replace `123` with your form ID and `FIELD_ID` with this field’s ID.

// Show only the BMI sub-input (.3) for this field in the entries list, with header "BMI"
add_filter('gform_entry_list_columns', function($columns, $form_id) {
    if ((int)$form_id !== 123) return $columns;

    // Remove the field's default column if present (e.g. FIELD_ID or FIELD_ID.1)
    unset($columns['FIELD_ID']);
    unset($columns['FIELD_ID.1']);
    unset($columns['FIELD_ID.2']);

    // Add BMI as the column
    $columns["FIELD_ID.3"] = __('BMI', 'bmi-calculator-gf');
    return $columns;
}, 10, 2);

// (Optional) Ensure the BMI value renders cleanly (usually not needed)
add_filter('gform_entries_field_value', function($value, $form_id, $field_id, $entry) {
    if ((int)$form_id !== 123) return $value;
    // Only intercept our BMI sub-input column
    if ((string)$field_id === 'FIELD_ID.3') {
        $bmi = rgar($entry, 'FIELD_ID.3');
        return $bmi !== '' && $bmi !== null ? esc_html($bmi) : '--';
    }
    return $value;
}, 10, 4);

**Recommendation:** Option A is cleaner and global (no per-form filters). It will make the main entries table show **BMI** as the column header and value, while you still keep Height/Weight saved as other sub-inputs.

User
Assistant
You’re still seeing **“Height (cm)”** because in your constructor the **first sub-input is `.1` (Height)**. Gravity Forms uses the *first sub-input*’s label for the Entries table column header on multi-input fields.

### Fix (recommended): put BMI first
Change your constructor to put `.3` (BMI) first:

public function __construct($data = array()) {
    parent::__construct($data);

    // Put BMI first so the Entries list column header becomes "BMI"
    $this->inputs = array(
        array('id' => "{$this->id}.3", 'label' => __('BMI', 'bmi-calculator-gf')),          // first => used in entries list
        array('id' => "{$this->id}.1", 'label' => __('Height (cm)', 'bmi-calculator-gf')),
        array('id' => "{$this->id}.2", 'label' => __('Weight (kg)', 'bmi-calculator-gf')),
    );
}

Then **open the form in the editor and click Update** (this makes GF regenerate the columns). If you use any persistent/object cache, clear it.

Your existing `get_value_entry_list()` already returns only the BMI value, so after the reorder you’ll get:
– Header: **BMI**
– Cell value: BMI (e.g., `22.5`)

### If you can’t reorder inputs
Force the column to use `.3` and label it “BMI” via filters (replace `123` with your form ID and `FIELD_ID` with this field’s numeric ID):

add_filter('gform_entry_list_columns', function($columns, $form_id) {
    if ((int) $form_id !== 123) return $columns;

    // Remove default column variants Gravity Forms may add for this field
    unset($columns['FIELD_ID'], $columns['FIELD_ID.1'], $columns['FIELD_ID.2']);

    // Add BMI-only column
    $columns['FIELD_ID.3'] = __('BMI', 'bmi-calculator-gf');
    return $columns;
}, 10, 2);

add_filter('gform_entries_field_value', function($value, $form_id, $field_id, $entry) {
    if ((int) $form_id !== 123) return $value;
    if ((string) $field_id === 'FIELD_ID.3') {
        $bmi = rgar($entry, 'FIELD_ID.3');
        return $bmi !== '' && $bmi !== null ? esc_html($bmi) : '--';
    }
    return $value;
}, 10, 4);

But the clean, global solution is just **reorder `$this->inputs`** and re-save the form.

Leave a Comment

Your email address will not be published. Required fields are marked *