What library parses form params when submitting or changing form in liveview

How does phoenix liveview parses form params? If for example I have a FormData right here:

{_method: "put", _csrf_token: "AEY...", invoice[expenses][0][id]: "185..", invoice[expenses][0][delete]: "false", invoice[expenses][0][product_type_id]: "98e...", …}

which is logged from:

const formData = new FormData(form);

      var object: any = {};
      formData.forEach(function(value: any, key: any){
          object[key] = value;

how come in the handle event params it is already formatted and nested into something like this

%{"_method" => "put", "_csrf_token" => "AEY...", "invoice" => %{"expenses" => %{"0" => %{"id" => "185...", "delete" => "false", "product_type_id" => "98e...", ...}}}}

The reason I asked is I might need to use whatever used as parser here to manually pass params to a custom event in my liveview. For example, when select2 changed, I want to pass the full params to validate event just like any other inputs.

Thanks in advance.

Answering my own question :slight_smile:

Not totally sure about the flow, I may skip some necessary steps but hopefully, you get the idea.

Form submission or change is handled first by a javascript event handler. During which the FormData is serialize into a URL encoded string via this code right here:

let serializeForm = (form, meta = {}) => {
  let formData = new FormData(form)
  let toRemove = []

  formData.forEach((val, key, index) => {
    if(val instanceof File){ toRemove.push(key) }

  // Cleanup after building fileData
  toRemove.forEach(key => formData.delete(key))

  let params = new URLSearchParams()
  for(let [key, val] of formData.entries()){ params.append(key, val) }
  for(let metaKey in meta){ params.append(metaKey, meta[metaKey]) }

  return params.toString()

The above code is also where _target is being inserted via meta argument.

The URL encoded string is sent to the LiveView channel and received via the handle_info/2 callback wherein it decodes the URL encoded string to a map using Plug.Conn.Query.decode/1 then eventually send it to the handle_event/3 result handler.