3

Been doing well so far with MDC Web Components, but I've been hung up here for far too long. (Not strong in JS.)

mdc-select used to be non-native, then used native HTML select, and now once again it's non-native. For a while MDC Web supported a hidden input so that you could pass values to the server.

There's hardly any documentation - mostly just stuck users like me opening issues on GitHub:

Closed: MDC Select - no longer form input compatible #2221

Closed: [MDC Select] Example in README does send values to the web server #5295

Open: [MDCSelect] Add hidden input element to support HTML forms #5428

I need to set/update the value of a hidden input on MDCSelect change for multiple select boxes on the same page... I can get it to do it for ONE select box, but not multiple.

Here is the select box HTML:

<div class="mdc-select mdc-select--outlined region-select">
  <div class="mdc-select__anchor demo-width-class">
    <i class="mdc-select__dropdown-icon"></i>
    <div id="demo-selected-text" class="mdc-select__selected-text" tabindex="0" aria-disabled="false" aria-expanded="false"></div>
    <div class="mdc-notched-outline">
      <div class="mdc-notched-outline__leading"></div>
      <div class="mdc-notched-outline__notch" style="">
        <label id="outlined-label" class="mdc-floating-label" style="">Region</label>
      </div>
      <div class="mdc-notched-outline__trailing"></div>
    </div>
  </div>

  <div class="mdc-select__menu mdc-menu mdc-menu-surface demo-width-class">
    <ul class="mdc-list">
      <li data-value="" disabled="" aria-selected="false" role="option" class="mdc-list-item" tabindex="0"></li>
      <li data-value="north" aria-selected="false" role="option" class="mdc-list-item" tabindex="-1">North</li>
      <li data-value="east" aria-selected="false" role="option" class="mdc-list-item" tabindex="-1">East</li>
      <li data-value="south" aria-selected="false" role="option" class="mdc-list-item" tabindex="-1">South</li>
      <li data-value="west" aria-selected="false" role="option" class="mdc-list-item" tabindex="-1">West</li>
    </ul>
  </div>
<!-- THIS IS THE HIDDEN INPUT THANK YOU -->
<input type="hidden" id="name2" name="input_name2" value="" class="my_mdc-select__value" />
</div>

I've tried targeting the hidden input with id, name, and even class. I think I need some sort of integrated function, forEach, or loop - tried adding JS beneath each select with no avail. I've worked the examples (seen below) from other users and no success. JavaScript isn't my thing, I know what it supposed to be happening but don't know the function or loop syntax etc to make this work.

I need to make sure each set/update targets the correct hidden input associated with that particular select box.

Here is my JS that works for ONE select box but not multiple:

// Select Menu
import {MDCSelect} from '@material/select';

const selectElements = [].slice.call(document.querySelectorAll('.mdc-select'));

selectElements.forEach((selectEl) => {
  const select = new MDCSelect(selectEl);

 select.listen('MDCSelect:change', (el) => {
    const elText = el.target.querySelector(`[data-value="${select.value}"]`).innerText;

    console.log(`Selected option at index ${select.selectedIndex} with value "${select.value}" with a label of ${elText}`);

    // this works but only saves one
    document.querySelector('input.my_mdc-select__value').value = select.value;
});

}); 

Here is some code that others used that I haven't been able to modify/apply (taken from links above):

From nikolov-tmw:

document.querySelectorAll( '[data-mdc-auto-init="MDCSelect"]' ).forEach( function( sel ) {
    sel.My_MDCSelect__Value = sel.querySelector('input.my_mdc-select__value');
    if ( null !== sel.My_MDCSelect__Value ) {
        sel.addEventListener( 'MDCSelect:change', function( a ) {
            if ( sel.MDCSelect ) {
                sel.My_MDCSelect__Value.value = sel.MDCSelect.value;
            }
        } );
    }
} );

From daniel-dm:

<div class="mdc-select">
  ...
</div>
<input id="pet-select" type="hidden" name="pets">
<script>
  const input = document.querySelector('#pet-select');
  const select = document.querySelector('.mdc-select');
  select.addEventListener('MDCSelect:change', e => {
    input.value = e.detail.value;
  });
</script>

Please help! This particular issue has been open since January (people struggling long before) with no clear solution to help non-JS developers implement MDCSelect boxes. Thanks in advance!

JDizzle
  • 33
  • 4
  • 1
    can I ask why you are using `[].slice.call(document.querySelectorAll('.mdc-select')) `as opposed to just `document.querySelectorAll('.mdc-select')`? – I am a registered user Apr 16 '20 at 21:13
  • @I am a registered user For .mdc-button and .mdc-text-field, I had no problems with manual instantiation... but it didn't seem to work with .mdc-select. While troubleshooting I found `[].slice.call` here: [GitHub #3236](https://github.com/material-components/material-components-web/issues/3236) and here: [stackoverflow mdc-textfield-working-but...](https://stackoverflow.com/questions/57447776/mdc-textfield-working-but-switch-not-working) ...as for why? I cannot explain, glad to learn why I shouldn't be! I can try again with `document.querySelectorAll('.mdc-select')` – JDizzle Apr 20 '20 at 18:15
  • Used this: `const selectElements = document.querySelectorAll('.mdc-select'); selectElements.forEach(selectElement => MDCSelect.attachTo(selectElement));` Seems to be working. Working on Rustem Gareev's answer below. – JDizzle Apr 20 '20 at 19:39
  • For those interested: `[].slice.call` aka `Array.prototype.slice.call` is a way to convert a `NodeList` (or any iterable for that matter) to JS `Array` instance and make use of all the useful methods of the built-in. `querySelectorAll` returns a `NodeList`. – Oleg Valter is with Ukraine Sep 08 '20 at 07:09

1 Answers1

0

The problem is here:

document.querySelector('input.my_mdc-select__value').value = select.value;

Document.querySelector will find the first matching element in the whole document, so in your loop you're always accessing the same input element.

Instead, you should run querySelector method on the parent element of each hidden input, which in your loop will look like:

selectEl.querySelector('input.my_mdc-select__value').value = select.value;
Rustem Gareev
  • 804
  • 1
  • 7
  • 14
  • Thank you Rustem Gareev! For those following, this works w/ manual instantiation! `// Select Menu import {MDCSelect} from '@material/select'; const selectElements = document.querySelectorAll('.mdc-select'); selectElements.forEach((selectEl) => { const select = new MDCSelect(selectEl); select.listen('MDCSelect:change', () => { selectEl.querySelector('input.my_mdc-select__value').value = select.value; }); });` – JDizzle Apr 20 '20 at 21:31