Problem/Motivation

Building #options using YAML is not intuitive for most users.

Proposed resolution

Provide a simple UI for building #options for select menus, radios, and checkboxes.

Remaining tasks

  • Create a YamlFornOptions element
  • Add multiple options to be added
  • Add Source (YAML) tab to Edit YAML Form Options. See: yamlform_ui_entity_type_alter()
  • Add example to tests/modules/yamlform_test/config/install/yamlform.yamlform.test_element_extras.yml
  • Write test verify rendering and processing
  • Update YamlFormOption entity edit page
  • Update YamlFormElementOptions element using by #select, #radios, and #checkboxes,

Notes

  • Reuse D8 core table drag
  • Add more should use #ajax
  • Add more should include number of options.
  • Add optgroup should also be included
  • Must support optgroup
  • Keep it simple.
  • Hopefully code can be reused to create conditional states UI

Code Example

$form['options] = [
  '#type' => 'yamlform_options',
  '#title' => 'Options',
  '#default_value' => [
    1 => 'Option 1',
    2 => 'Option 2',
    'OptGroup' => [
       3 => 'Option 3',
    ],
    '#optgroup' => TRUE,
    '#required' => TRUE,
  ],
];

References

Development Notes

  • Branch Name: 2759527-create-options-element
  • Commit message: Issue #2759527: Create a UI for editing #options

Comments

jrockowitz created an issue. See original summary.

jrockowitz’s picture

Issue summary: View changes

  • jrockowitz committed 19a612e on 8.x-1.x
    Issue #2759527: Add 'Submitted to' to YAML form download settings
    

  • jrockowitz committed 776ef45 on 8.x-1.x
    Issue #2759527: Add 'Submitted to' to YAML form download settings
    
scott_euser’s picture

Hi Jacob,

Simplest is probably following this example but a much nicer interface would be ajaxified, perhaps with an interface similar to the inline entity form widget like this sceenshot. That uses the table drag and the code from it could be used as jump start perhaps.

If built in the latter way as a widget of sorts, it would probably be more reuseable for states, etc.

Would you like my help with it? I could work on it later in the week (Thurs/Fri). Perhaps before that we can arrange to meet on Drupal's IRC, Skype, Hangouts, or whatever your preferred medium is. Also completely understand if you prefer to do it yourself (ex, if you have a vision for it already and how you'd like it to work for states, etc)

Thanks,
Scott

jrockowitz’s picture

Issue summary: View changes
StatusFileSize
new13.42 KB
jrockowitz’s picture

Issue summary: View changes
jrockowitz’s picture

Issue summary: View changes
jrockowitz’s picture

@scott_euser Help is always going to appreciated.

I think the best approach is to do this in small steps, which could include...

  • Setting up new YamlFormOptions element
  • Setup tabledrag with optgroup support
  • Add AJAX add more and add opgroup.
  • Integrate YamlFormOptions element in YamlFormOptionsForm.
  • Write tests

Below is the only immediate reference I found for creating render elements. There definitely needs to be more documentation on how to create custom form elements for Drupal and the YAML form module.

Some more notes

  • I usually create a test YAML form and use it to build out new form elements.
  • Once the element is close to done I add a basic and advanced example to yamlform.yamlform.test_element_extras

I am on vacation this week but next week I can do a Google Hangout.

jrockowitz’s picture

Issue summary: View changes

Adding AJAX example to above references.

scott_euser’s picture

I don't think I've managed to get my head around your intentions on 'Create a YamlFormOptions element yet to be able to make any progress on this.

I've been tinkering within \Drupal\yamlform\Element\YamlFormElementOptions and it seems easy enough to add a draggable table of fields and eventually ajaxify it to dynamically add in rows (when Custom type is selected for the 'Options' within the Add Element form). I think that is not what you are looking for though.

Are you intending to create a new field type like you've done with yamlform_codemirror? So something like:

$element['custom'] = [
  '#type' => 'yamlform_options',
  ...
];

Then have a new field widget handle the display (ie, creating the renderable array for the table with tabledrag, etc).

Perhaps I'm way off the mark!

jrockowitz’s picture

You are not far off.

There needs to be new YamlFormOptions element which can used by the YamlFormElementOptions element and the YamlFormOptionsForm (/admin/structure/yamlform/settings/options/manage/add)

Also creating a new dedicated YamlFormOptions element will be easier to test.

jrockowitz’s picture

@scott_euser I am back from vacation. If you want to do a Google Hangout, just email me.

jrockowitz’s picture

Assigned: jrockowitz » scott_euser
scott_euser’s picture

StatusFileSize
new13.67 KB

@jrockowitz I am making progress (attached) but am struggling with a couple things and hoping to get your advice (perhaps you've addressed it elsewhere in YAML Form already?):

  1. This issue seems to be a difficult one to overcome until it's fixed. It wraps all AjaxResponse objects in a div but in our case the Response should be rows within the table. At the moment I am rebuilding the entire table including what is already added to get around it.
  2. (1) leads to a new issue; I have not managed to figure out how to make FormState and AjaxResponse play nicely together. #default_value is being overridden by the FormState and I've had no joy yet keeping the values besides fixing them with #value instead of #default_value, but then they aren't editable.
  3. Do you have an example of an element where you created a custom template for it? At the moment I've just dropped the add more stuff into a fieldset while testing but would like to get it looking like your screenshot, but with the addition to choose whether to add options or option groups perhaps?

Even if (1) is solved, I am not sure if we'll be able to avoid rebuilding the entire form as there doesn't seem to be a way to retrigger #tabledrag if you add in rows to an already rendered table.

At the moment to test, you'd need a form that already has options (and potentially option groups) as I haven't yet started the saving of values. The adding of new options and option group inputs works + handling of existing ones and allowing you to drag them around. It supports options within option groups, options on their own, and mixed.

Hope you can help point me in the right direction for the above bits!

Thanks,
Scott

jrockowitz’s picture

Issue summary: View changes
StatusFileSize
new7.19 KB

@scott_euser

  1. Yes, we are going to most likely have to replace the whole table. We need to look at the WidgetBase class' ajax implementation and copy some of it code/patterns. For example, every options element must have a unique ID.
  2. You have to use \Drupal\yamlform\Element\YamlFormOptions::valueCallback to parse and set the #default_value.
  3. You probably don't need any custom templates or additional css for this widget. You just need to use core's .container-inline class.

The below code

    $element['actions'] = [
      '#prefix' => '<div class="container-inline">',
      '#suffix' => '</div>',
    ];
    $element['actions']['add_options'] = [
      '#type' => 'submit',
      '#value' => t('Add'),
      '#ajax' => [
        'callback' => '\Drupal\yamlform\Element\YamlFormOptions::addOptionsCallback',
        'wrapper' => 'edit-properties-options-custom-options-container-options-table',
        'event' => 'click',
        'method' => 'append',
        'effect' => 'slide',
      ],
    ];
    $element['actions']['add_options_quantity'] = [
      '#type' => 'number',
      '#min' => 1,
      '#max' => 1000,
      '#default_value' => 10,
      '#field_suffix' => t('more options'),
    ];

Generates the needed widget which would need an 'Add Option Group' Button added below it.

Also, you don't need to the fieldset container

The below code can be changed from

$element['options_container'] = [
  '#type' => 'fieldset',
  '#title' => t('Options'),
];

to

$element = [
  '#prefix' => '<div id="{Unique ID}">',
  '#suffix' => '</div>',
];

Some more notes

  • Only focus on getting the options to work and then we can add in optgroup support.
  • There might need to be two different tabledrags for the element, one for optgroup support and the other without optgroups but I am really not sure.
  • If the element has no default values it should start with 5 empty options
  • Each options #titles should be hidden and the table headers should be 'Option value' and 'Option title'. Each option row can have placeholder text that say 'Enter value' and 'Enter label'. This will help compress the element's overall height.
scott_euser’s picture

@jrockowitz Thanks for the pointers! One further question; we are still ultimately storing it in YAML right? In the current structure is just value: label so would the unique ID just be a temporary ID until saved?

jrockowitz’s picture

@scott_euser We should be returning (and storing) an associative array of options. We are going to have to convert the existing YamlFormOptions from storing YAML to an associative array. This can be done after the widget is setup and working.

scott_euser’s picture

StatusFileSize
new14.16 KB

@jrockowitz Thanks for the info, I think I'm slowly getting there but running into a fair few hiccups connecting the YAML Form Element with the Ajax API.

I haven't been able to get valueCallback to populate already saved data via the new table loaded with the Ajax API unless I do like the attached patch and call buildForm. The attached patch works but feels clunky / hacky.

Note that problem doesn't happen if you simply create a new select element, add some values, then click Add 10 more options - it only happens if you already have saved values and you go in to edit them.

jrockowitz’s picture

@scott_euser Thanks, I will look at the code and see what I can do to help.

  • jrockowitz committed 19a612e on 2759527-ui-options
    Issue #2759527: Add 'Submitted to' to YAML form download settings
    
  • jrockowitz committed 776ef45 on 2759527-ui-options
    Issue #2759527: Add 'Submitted to' to YAML form download settings
    

  • jrockowitz committed 90105c3 on 2759527-ui-options
    Issue #2759527 by scott_euser, jrockowitz: Create a UI for editing #...
jrockowitz’s picture

@scott_user I can now see how difficult and frustrating this is going to be to implement. I think the only way we are going to be able to do this is to completely reverse engineer https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Field!WidgetBase.... and start with just getting basic options with no optgroups working with no AJAX.

By removing all the AJAX and table drag code I was able to get the population and validation of options working in a '2759527-ui-options' branch. I still need to figure out how to get the add button work.

I think once we get the non AJAX working we could gradually layer in the AJAX.

I need a few more hours to get the non AJAX version working.

  • jrockowitz committed 322dc6a on 2759527-ui-options
    Issue #2759527 by scott_euser, jrockowitz: Create a UI for editing #...

  • jrockowitz committed 771e61b on 2759527-ui-options
    Issue #2759527 by scott_euser, jrockowitz: Create a UI for editing #...

  • jrockowitz committed 8fd2383 on 2759527-ui-options
    Issue #2759527 by scott_euser, jrockowitz: Create a UI for editing #...

  • jrockowitz committed 8be78cb on 2759527-ui-options
    Issue #2759527 by scott_euser, jrockowitz: Create a UI for editing #...
jrockowitz’s picture

@scott_euser

Wow, this is insanely difficult to sort out. I really appreciate you starting this and feel bad that I had to reapproach the code from a different angle. The good news is if we can get this working correctly it should be very possible to add a UI to the #states API.

I had to go back to looking at Port ajax example add more and remove example from drupal 7 to drupal 8 to figure out how to properly setup the add more button.

Then, I still had to look at the WidgetBase class to figure out that the form state for the number of options needs to be stored via a unique key.

Then, I got completely burned by #1342066: Document that buttons with the same #value need a unique #name for the form API to distinguish them, or change the form API to assign unique #names automatically

Finally, I was able to get the add more working correctly. You can test the add more behavior using /yamlform/test_element_options in the test branch.

Now, I am a little stuck on how to remove an existing option. I can't find any examples of an Element that supports removing values in core or contrib.

A few take aways....

  • Getting the behaviors to work without AJAX is key.
  • AJAX is literally just returning (and replacing) the entire updated element.
  • There is a not chance in hell that we can initial support Optgroups. If a developer wants Optgroups, they will have to write some YAML.
  • more to come...
scott_euser’s picture

No problem at all, glad it wasn't just me struggling with it! Apologies I've had a busy week, I'll try to take a look asap at your progress and see if I can find anything around the remove. Perhaps looking at how Paragraphs does it could point us in the right direction.

  • jrockowitz committed debd172 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Get remove working.
    
jrockowitz’s picture

@scott_euser I managed to get a buggy but correctly coded 'Remove' button working by looking at \file_managed_file_submit() and using the below example

$form_state->setValueForElement($element['fids'], implode(' ', $fids));
NestedArray::setValue($form_state->getUserInput(), $element['fids']['#parents'], implode(' ', $fids));

There is still a bug where it is adding the incorrect number of rows after removing an option but this should be easy to fix.

  • jrockowitz committed fa5bb80 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Cleanup and handler...

  • jrockowitz committed 6791d65 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Fix...

  • jrockowitz committed 02f946a on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Minor bug fixies. Get...
scott_euser’s picture

@jrockowitz Looks good! I couldn't reproduce the incorrect number of rows being added bug you described, but I did have issues with the multiple select elements in one form with default values prepopulated (options_default_value for instance if you do a clean install of yamlform_test). The issue happens when you hit remove when you have multiple select elements on the page.

Looks like ajaxCallback and removeOptionSubmit just aren't being called at all when the select element is not the first one on the page, so an empty table is returned and all rows are removed.

I couldn't see anything obvious as the callback on the remove button appears identical.

  • jrockowitz committed 4ae9a53 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Fix buggy add and...

  • jrockowitz committed b131d06 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Fix...
jrockowitz’s picture

Issue summary: View changes
Status: Active » Needs review
StatusFileSize
new297.42 KB
new34.79 KB

As of right now, I am not seeing any bugs.

I am using /form/test-element-options for testing.

Here is a screenshot of the form.

I am also attaching a patch with no new tests but I just want to confirm that nothing is broken.

Next I have to write a dedicated test for just the YamlFormOptions element.

Status: Needs review » Needs work

The last submitted patch, 38: create_a_ui_for_editing-2759527-38.patch, failed testing.

  • jrockowitz committed be4dfb0 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Add...

  • jrockowitz committed 877b616 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Write tests.
    
jrockowitz’s picture

Status: Needs work » Needs review
StatusFileSize
new128.57 KB

@scott_euser This might done or at least committable. I am expecting that the element and code will need some more tweaks/improvements in the near future.

jrockowitz’s picture

jrockowitz’s picture

StatusFileSize
new50.79 KB

Here is the actual patch and not a screenshot of some random error from my local machine.

Status: Needs review » Needs work

The last submitted patch, 44: create_a_ui_for_editing-2759527-41.patch, failed testing.

  • jrockowitz committed 41ee139 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Fix broken test.
    
jrockowitz’s picture

Status: Needs work » Needs review
StatusFileSize
new49.7 KB

  • jrockowitz committed 58659a6 on 2759527-ui-options
    Issue #2759527: Create a UI for editing #options. Missing test.
    
scott_euser’s picture

That last issue I had with multiple select elements on the same page appears solved.
All seems to work fine for me!

Okay if I revisit optgroup based on this in a simple form? If mixed optgroup and non-optgroup (which appears to be a valid case) then still yaml only perhaps.

jrockowitz’s picture

@scott_euser I am going merge what is done here.

You should create a new ticket for adding optgroup support to YamlFormOptions element.

Right now, optgroups can only be created via editing the YAML source code, which I am okay with.

In the next week or two, I am going to start working on the UI for #states.

  • jrockowitz committed baacd6f on 8.x-1.x
    Issue #2759527: Create a UI for editing #options
    
jrockowitz’s picture

Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.