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

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Similar Content

    • By Shohan Arafat
      I was looking for code sample where I can attach a textarea for setting up a content in my page. And I wanted to do it programatically. But I can't find a way to do that. Is it possible to add a content textarea with program?
    • By Robin S
      Select Images
      An inputfield that allows the visual selection and sorting of images, intended for use with the FieldtypeDynamicOptions module. Together these modules can be used to create a kind of "image reference" field.

      Integration with FieldtypeDynamicOptions
      InputfieldSelectImages was developed to be used together with FieldtypeDynamicOptions (v0.1.3 or newer):
      Create a Dynamic Options field. Choose "Select Images" as the "Inputfield type". Select Images appears in the "Multiple item selection" category but you can set "Maximum number of items" to 1 if you want to use Select Images for single image selections. Define selectable options for the field via a FieldtypeDynamicOptions::getSelectableOptions hook. See some examples below. FieldtypeDynamicOptions is recommended but is not a strict requirement for installing InputfieldSelectImages in case you want to use an alternative way to store the field data.
      Selection of Pageimages
      In this example the field allows selection of Pageimages that are in the "images" field of the home page.
      The field will store URLs to the Pageimages so it works as a kind of "image reference" field. You can use the "Format as Pagefile/Pageimage object(s)" option for the Dynamic Options field to have the formatted value of the field be automatically converted from the stored Pageimage URLs to Pageimage objects.
      $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // Get Pageimages within the "images" field on the home page foreach($event->wire()->pages(1)->images as $image) { // Add an option for each Pageimage // When the key is a Pageimage URL the inputfield will automatically create a thumbnail // In this example the label includes the basename and the filesize /** @var Pageimage $image */ $options[$image->url] = "{$image->basename}<br>{$image->filesizeStr}"; } $event->return = $options; } }); Selection of image files not associated with a Page
      When not working with Pageimages you must add a "data-thumb" attribute for each selectable option which contains a URL to a thumbnail/image.
      In this example the field allows selection of image files in a "/pics/" folder which is in the site root.
      $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // Get files that are in the /pics/ folder $root = $event->wire()->config->paths->root; $path = $root . 'pics/'; $files = $event->wire()->files->find($path); // Add an option for each file foreach($files as $file) { $basename = str_replace($path, '', $file); $url = str_replace($root, '/', $file); // The value must be an array with the following structure... $options[$url] = [ // The label for the image 'label' => $basename, 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => $url, ], ]; } $event->return = $options; } }); The field values don't have to be image URLs
      The values stored by the Dynamic Options field don't have to be image URLs. For example, you could use the images to represent different layout options for a page, or to represent widgets that will be inserted on the page.
      Also, you can use external URLs for the thumbnails. In the example below the options "calm" and "crazy" are represented by thumbnails from placecage.com.
      $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "calm_or_crazy" if($field->name === 'calm_or_crazy') { $options = []; // Add options that are illustrated with thumbnails from placecage.com $options['calm'] = [ // The label for the option 'label' => 'Nicolas Cage is a calm man', 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => 'https://www.placecage.com/260/260', ] ]; $options['crazy'] = [ // The label for the option 'label' => 'Nicolas Cage is a crazy man', 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => 'https://www.placecage.com/c/260/260', ] ]; $event->return = $options; } }); Field configuration
      You can define labels for the button, notices, etc, that are used within the inputfield if the defaults don't suit.

       
      https://github.com/Toutouwai/InputfieldSelectImages
      https://processwire.com/modules/inputfield-select-images/
    • By Neue Rituale
      https://github.com/neuerituale/FieldtypeOembed
      or in the modules directory: https://processwire.com/modules/fieldtype-oembed/
    • By carlitoselmago
      It's been twice this year as a PW developer where clients have asqued me to have a way to put content on specific coordinates of a picture. Something like what image maps are made of. But not necesarly on links, sometimes text, sometimes images and links. 
      So I've been thinking to build an inputfield for that purpose. But I'm not sure how could it be the most flexible so I can share it in github/modules page.
      Ideally it would need to work like a repeater field where you start uploading an image and then adding any custom field like title, url etc. So the user could add multiple elements floating over that image.
      Does anybody here know the ideal way to start the arquitecture of something like that? I could just hardcode the fields I need for the website I have to code this month but it would be nice to be something more flexible.
    • By theoretic
      Hi there! And thanks for Processwire!
      It appears there's a possible bug in Processwire 3.0.170 concerning file and/or image inputfield. Creating such a field results in the following error:

      Fatal Error: Uncaught Error: Call to a member function get() on null
      The inputfield is created however. The closer look reveals a problem at line 60 in wire\modules\Fieldtype\FieldtypeFile\config.php:
      if(!$value) $value = $fieldtype->get('defaultFileExtensions'); Commenting this line removes the problem, but the newly created inputfield requires 'Allowed file extensions' config option to be set (which is rather expectable since i commented the above-cited line of code). Never faced this problem before, hope it can be resolved.
×
×
  • Create New...