Search code examples
javascriptgetter-setterform-data

New FormData(SomeForm) Not Calling Custom Getter in JavaScript


I have a custom dropdown componant. Within this component, I set a custom getter and setter shown below:

Object.defineProperty(this.DropDownInputTarget, 'value', {
    get: function() {
        return this.dataset.inputValue ?? nativeInputGetter.call(this);
    },
    set: function(value: string){
        nativeInputSetter.call(this, value);
    }
});

When using this in code or via the inspector console, I get the expected result. But when I am getting the form data via new FormData(SomeForm), the value is always null. Is there something I need to do for this to work as expected? Example from inspector:

code: document.querySelector('#Item1_ShippingInfo_StoreOptions_OtherLocationId').value
response: "BRXSL" 

How I get my form data:

const formData = new FormData(shippingForm);

All other values from the other inputs are retrieved correctly.

As far as why I am using a custom getter, this is for a custom drop down component. The actual value of the selected item is different from the displayed value. I keep the actual value in a data attribute. This is what the custom getter retrieves.

Here is a picture of what the values look like in the console. Note that I am getting null in the FormData object, but a value when I get the input.value. enter image description here And this is what it looks like if I don't use Ajax to submit the form: enter image description here

I do have a work-around, but it is less than ideal. It requires me to set the value I am missing manually after I create the formData object.

const formData = new FormData(shippingForm);
formData.set('Item1.ShippingInfo.StoreOptions.OtherLocationId', 
shippingForm.querySelector<HTMLInputElement>('#Item1_ShippingInfo_StoreOptions_OtherLocationId').value);

Solution

  • While using internals.setFormValue() can work, this requires the input to be a custom input. This way you can call HTMLElement.attachInternals().

    After that, you need to make sure the custom element class has this variable defined at the class level: static formAssociated = true; so that it will be picked up when creating a new FormData.

    If your input is not a custom element, this approach will not work as HTMLElement.attachInternals() will not work in built in types.

    I chose to not go this route because:

    1. Typescript does not have the best support for custom elements.
    2. Making a custom element would have required a lot of changes to the existing code.

    Solution: Add a hidden input element. This is the input element I will bind to in c# as well as update the value for when I select an item.

    <div class="select-container">
      <input type="text" class="form-control" placeholder="Other Location" data-custom-validation="dropdownValidation" data-dropdown-action="search" data-dropdown-input/>
      <input type="hidden" asp-for="@Model.Location" />
      <!-- Additional HTML for the select componant -->
    <div>

    In the above example, the hidden input is after the visible one. The visible input will still show what is "selected" while the hidden input gets updated to the current value I want to be submitted. Because the visible input has no name or value, it will not be captured when creating the form data.