Jump to content

Custom Inputfield with multiple inputs


Soma
 Share

Recommended Posts

I'm in need of a special inputfield with multiple input form fields. I created a little Inputfield extending InputfieldTextarea and using it for storing the values as json encoded string. Seeing an example of Ryan recently using this technique I went and tried to do a little custom Inputfield. I got it working so far with a little try and error, but wanted to have feedback, if there's anything done wrong or could be done better.

I need this to store numeric values for the 12 months. These will be used to render a chart on page. So I first did a simple textarea and having each values on a new line, but wanted something more intuitive and convienient for the client to enter the values.

(I know it would also be possible (and maybe better solution) to write a complete new Fieldtype/Inputfield, but I'm not really into it yet and would need some help. But this was kinda simple and does the job, only drawback is that it wouldn't work with selectors as it's stored as json in a text field in db.)

Here's my code:

<?php

/**
* ProcessWire Custom InputfieldMonths
*
* Inputfield that stores numeric values for the 12 months of a year.
*
*/

class InputfieldMonths extends InputfieldTextarea {

protected $months = array(
		"January" => "jan",
		"February" => "feb",
		"March" => "mar",
		"April" => "apr",
		"May" => "may",
		"June" => "jun",
		"July" => "jul",
		"August" => "aug",
		"September" => "sep",
		"October" => "oct",
		"November" => "nov",
		"December" => "dec"
	);

public static function getModuleInfo() {
	return array(
		'title' => 'InputfieldMonths',
		'version' => 100,
		'summary' => 'Stores 12 integer values for months of a year',
		'permanent' => false,
		);
}

public function init() {
	parent::init();
}

public function ___render() {

	$values = json_decode($this->value,true);
	$out = '';
	foreach($this->months as $label => $name) {
		$out .= <<< _OUT
		<p>
			<label for='$name'>$label</label>
			<input id='$name' name='$name' value='$values[$name]'/>
		</p>
_OUT;
	}
	return $out;
}

public function ___processInput(WireInputData $input) {

	foreach($input as $key => $val) {
		if(!in_array($key, $this->months) or $val === '') continue;
		if(!is_numeric($val)) return $this->error("Wrong format. Value '$val' is not numeric!");
	}

	$months_values = array();

	foreach($this->months as $month) {
		$months_values[$month] = $input[$month];
	}

	$data = json_encode($months_values);

	if($this->value != $data) {
		parent::trackChange('value');
		$this->value = $data;
	}

	return $this;
}

}
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

I think for what you are trying to do: provide a simple solution to this particular need, it seems like a good solution. I like it. From a storage perspective, you could make it a little more efficient by using the month numbers (integers) in your encoded JSON rather than the month names, and then unset any blank values from the array before encoding it. This would ensure it's reduced to the smallest encoded format possible. Also, December is spelled wrong in your $months array at the top. :)

Link to comment
Share on other sites

From a storage perspective, you could make it a little more efficient by using the month numbers (integers) in your encoded JSON rather than the month names, and then unset any blank values from the array before encoding it. This would ensure it's reduced to the smallest encoded format possible.

You're right, thanks for the suggestions. But I need the blank values to be 0 for the chart, so I implemented a check that sets not filled in fields to 0. I know it would be possible to check for them in the output, but wanted to make it simple as possible.

Thanks for pointing out the typo! Must have been sleeping...

Link to comment
Share on other sites

  • 6 years later...

Humm, it does not work in a repeater.

It saves the same value for all items. What am I missing?

Thank you.

<?php

/**
 * ProcessWire Custom InputfieldBSCol
 */
class InputfieldBSCol extends InputfieldTextarea {

    protected $cols = array("col" => "", "col-sm" => "sm", "col-md" => "md", "col-lg" => "lg", "col-xl" => "xl");
    protected $sizes = array("", "auto", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12");

    public static function getModuleInfo() {
        return array(
            'title' => 'InputfieldBootstrapCol',
            'version' => 100,
            'summary' => 'Stores Bootstrap 4 Columns Settings',
            'permanent' => false,
            'autoload' => false,
            'singular' => false
        );
    }

    public function init() {
        parent::init();
    }

    public function ___render() {
        $values = json_decode($this->value, true);
        $brpval = $values['brp'];
        $bsizval = $values['bsiz'];
        $out = '';
        $out .= '<select id="brp" name="brp">';
        foreach ($this->cols as $key => $value) {
            $out .= "<option" . ($brpval == $value ? ' selected' : '') . " value=\"$value\">$key</option>";
        }
        $out .= '</select>-';

        $out .= '<select id="bsiz" name="bsiz">';
        foreach ($this->sizes as $value) {
            $out .= "<option" . ($bsizval == $value ? ' selected' : '') . " value=\"$value\">$value</option>";
        }
        $out .= '</select>';
        return $out;
    }

    public function ___processInput(WireInputData $input) {
        $bs_col_options = array();

        $bs_col_options['brp'] = $input['brp'];
        $bs_col_options['bsiz'] = $input['bsiz'];

        $colstr = 'col';
        if ($input['brp'] != '') {
            $colstr .= '-' . $input['brp'];
            if ($input['bsiz'] != '')
                $colstr .= '-' . $input['bsiz'];
        }

        $bs_col_options['bcolstr'] = $colstr;

        $data = json_encode($bs_col_options);

        if ($this->value != $data) {
            parent::trackChange('value');
            $this->value = $data;
        }

        return $this;
    }

}

 

Link to comment
Share on other sites

Now I've got it. I feel like an idiot. :huh:

I was thinking in terms of separate instances and separate pages, but forgot that all the input fields are in the same DOM tree.
So they need distinct names .
Actually it is all there with

$attrs = $this->getAttributes();
$idstring=$attrs['id'];  

I just didn't know. :undecided:

Link to comment
Share on other sites

  • 1 month later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...