a-ok Posted November 15, 2018 Share Posted November 15, 2018 As the title suggests I am wanting to add custom options to InputfieldSelect via a hook. I have this in my ready.php $wire->addHookBefore('InputfieldSelect::render', function($event) { if ($event->object->name != 'work_detail_videos_image') return; $page = $event->arguments('page'); $items = array(); foreach($page->work_detail_images as $image) { $items[] = $image->name; } $event->object->addOptions($items); }); I think it's wrong, however. Mainly because the field is within a repeater? The repeater is set to non-AJAX. Any thoughts? Link to comment Share on other sites More sharing options...
BitPoet Posted November 15, 2018 Share Posted November 15, 2018 There's no "page" argument to Inputfield::render, but you can use the property $inputfield->hasPage to retrieve the page associated with the Inputfield. The returned page is the individual repeater item page, not the page being edited. If work_detail_images is in the same repeater, the following line should be enough to get things working: $page = $event->object->hasPage; 1 Link to comment Share on other sites More sharing options...
a-ok Posted November 15, 2018 Author Share Posted November 15, 2018 1 hour ago, BitPoet said: There's no "page" argument to Inputfield::render, but you can use the property $inputfield->hasPage to retrieve the page associated with the Inputfield. The returned page is the individual repeater item page, not the page being edited. If work_detail_images is in the same repeater, the following line should be enough to get things working: $page = $event->object->hasPage; Thanks so much. Okay... this makes sense. Unfortunately 'work_detail_images' isn't in the repeater. I guess I'll need to hook into the repeater too? Link to comment Share on other sites More sharing options...
BitPoet Posted November 15, 2018 Share Posted November 15, 2018 38 minutes ago, oma said: Unfortunately 'work_detail_images' isn't in the repeater. I guess I'll need to hook into the repeater too? Probably nothing that complicatied. You can use getForPage() on the repater item to retrieve the page it belongs to. $repeaterPage = $event->object->hasPage; $page = $repeaterPage->getForPage(); 1 Link to comment Share on other sites More sharing options...
a-ok Posted November 15, 2018 Author Share Posted November 15, 2018 21 minutes ago, BitPoet said: Probably nothing that complicatied. You can use getForPage() on the repater item to retrieve the page it belongs to. $repeaterPage = $event->object->hasPage; $page = $repeaterPage->getForPage(); Thanks! Got this working with your help. Only issue now is that it doesn't seem to 'save' the selections by the user when saved. Is that because it's 're-adding' them each time? Link to comment Share on other sites More sharing options...
a-ok Posted November 15, 2018 Author Share Posted November 15, 2018 I also tried the following from this topic https://processwire.com/talk/topic/14463-fieldtypeoptions-set-selectable-options-through-api/ $wire->addHookBefore('InputfieldSelect::render', function($event) { if ($event->object->hasField == 'work_detail_videos_image') { $repeaterPage = $event->object->hasPage; $page = $repeaterPage->getForPage(); $options = $event->object->getOptions(); foreach($page->work_detail_images as $image) { if ($image->ext == 'GIF' || $image->ext == 'gif') { $option = new SelectableOption(); $option->title = $image->name; $options->add($option); } } $event->object->setOptions($options); } }); But it errors out with "Call to a member function add() on array" Link to comment Share on other sites More sharing options...
BitPoet Posted November 15, 2018 Share Posted November 15, 2018 The not saving thing is actually something I encountered too a few days ago when populating select options in a hook. Unfortunately, I didn't find the time to dig deeper (and it isn't an urgent case). Come to think of it, it might be a validation issue, as when saving in ProcessPageEdit, the render hook isn't called before processInput is triggered. So running the same hook code before InputfieldSelect::processInput might solve it. 1 Link to comment Share on other sites More sharing options...
a-ok Posted November 16, 2018 Author Share Posted November 16, 2018 18 hours ago, BitPoet said: The not saving thing is actually something I encountered too a few days ago when populating select options in a hook. Unfortunately, I didn't find the time to dig deeper (and it isn't an urgent case). Come to think of it, it might be a validation issue, as when saving in ProcessPageEdit, the render hook isn't called before processInput is triggered. So running the same hook code before InputfieldSelect::processInput might solve it. Thanks for your help, @bitpoet. It seems like adding 'InputfieldSelect::processInput' breaks it (doesn't return any options). Link to comment Share on other sites More sharing options...
a-ok Posted November 16, 2018 Author Share Posted November 16, 2018 Dumping $event doesn't seem to do anything so perhaps InputfieldSelect::processInput isn't right? Should it be InputfieldWrapper::processInput? Link to comment Share on other sites More sharing options...
Pete Posted July 28, 2022 Share Posted July 28, 2022 (edited) If anyone's wondering how to get the data to save, you need to set the options again prior to processing the input. So here's my example - first hook adds my options to the field so they render, second hook makes the options available when processing the input: wire->addHookBefore('InputfieldSelect::render', function($event) { if($event->object->name == 'my_select_field_name') { $countries = [1 => 'UK', 2 => 'USA', 3 => 'Etc etc']; $event->object->setOptions($countries); } }); $wire->addHookBefore('InputfieldSelect::processInput', function(HookEvent $event) { // Get the object the event occurred on, if needed $InputfieldSelect = $event->object; if ($InputfieldSelect->name == 'my_select_field_name') { $countries = [1 => 'UK', 2 => 'USA', 3 => 'Etc etc']; $event->object->setOptions($countries); } }); Using this in a complicated project where I've got custom tables for some data and it works nicely. Possibly also worth noting this works for checkboxes and radios I think as they are all extended from InputfieldSelect Edited February 2, 2023 by Pete Typo - $InputfieldSelet->id should have been ->name Link to comment Share on other sites More sharing options...
bernhard Posted July 28, 2022 Share Posted July 28, 2022 I think you could also hook Field::getInputfield() instead of Inputfield::render() and set your options there. They should then also be available in processInput, but I have not tested what I just said so I might be wrong ? Link to comment Share on other sites More sharing options...
Pete Posted July 28, 2022 Share Posted July 28, 2022 Ah thanks @bernhard that does make sense, I'll check it out! 1 Link to comment Share on other sites More sharing options...
Pete Posted August 1, 2022 Share Posted August 1, 2022 @bernhard I can't quite work out what the code would look like there, because if I bd() the inputfields on the page, it only shows the "outer" combo field name, not the subfields. Something else to note using my code is that if you output the field DATA (not the inputfield markup) on the frontend (which in my case is a country) using code like this: echo $user->billing_details->country; it won't show the data as it doesn't know what the key/value pairs are supposed to be when formatting the data. So I got around that by doing: echo $user->getUnformatted('billing_details')->country; Which simply outputs whatever you had stored in the DB. I'm sure there's some way of covering all this in 2 hooks or less using your method bernhard but not sure what it would look like for a combo field. For now my 2 hooks and outputting the unformatted value gets me where I need to be. I just wish I'd remembered about getUnformatted 90 minutes ago ? 1 Link to comment Share on other sites More sharing options...
bernhard Posted January 1 Share Posted January 1 On 7/28/2022 at 11:32 AM, Pete said: wire->addHookBefore('InputfieldSelect::render', function($event) { if($event->object->name == 'my_select_field_name') { $countries = [1 => 'UK', 2 => 'USA', 3 => 'Etc etc']; $event->object->setOptions($countries); } }); $wire->addHookBefore('InputfieldSelect::processInput', function(HookEvent $event) { // Get the object the event occurred on, if needed $InputfieldSelect = $event->object; if ($InputfieldSelect->name == 'my_select_field_name') { $countries = [1 => 'UK', 2 => 'USA', 3 => 'Etc etc']; $event->object->setOptions($countries); } }); Had the same need today and was not able to get it working properly. The quoted version did only work if I had some options set in the inputfield's config, which is not good. I came up with this solution (that only works for single selects): <?php $wire->addHookBefore('InputfieldInteger::render', function ($event) { $inputfield = $event->object; if ($inputfield->name != "your_fieldname") return; $f = new InputfieldSelect(); $f->name = $inputfield->name; $f->value = $inputfield->hasPage->{$inputfield->hasField}; $f->setOptions([1 => 'UK', 2 => 'USA', 3 => 'Etc etc']); $event->return = $f->render(); $event->replace = true; }); This works because it simply stores the option's id in the integer field so it does not try to sanitize for options that it does not have. Link to comment Share on other sites More sharing options...
MarkE Posted January 1 Share Posted January 1 Not sure if this is of any help, @bernhard, but I had a similar sort of need in my home-built pagebuilder module. The following code is called from the module init() protected function allowableLayouts($allowedLayoutFieldName) { // Create the options for motif_allowable_children/sublayouts being all the pro-forma layouts $layouts = $this->pages->find("template=MotifLayout"); //bd($layouts, '$layouts'); if($layouts && $layouts->count() > 0) {// Prevent deletion of options if pages not yet loaded $options = new selectableOptionArray(); $i = 1; foreach($layouts as $layout) { $option = new SelectableOption(); $option->set('id', $layout->id); // use page id as this should not change as components are added, changed and deleted $option->set('sort', $i); // this assigns the correct value but seems to have no effect on the display order $option->set('title', $layout->title); $option->set('value', $layout->path); //bd($option, 'allowable child'); $options->add($option); $i++; } $allowedLayoutField = $this->fields->get($allowedLayoutFieldName); if($allowedLayoutField && $allowedLayoutField->id > 0) { //bd($options, 'Allowable options for ' . $layoutField); // ToDo - Make this conditional on there being a change (better efficiency) after being satisfied that it works OK $allowedLayoutField->type->setOptions($allowedLayoutField, $options); } } } The above is presented "as-is" but, if it is relevant, I'm sure you can adapt as required. Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now