camstuart

camstuart

Best practice to enrich a form with data received from a javascript call back

Synopsis

I am trying to understand the correct approach for capturing data returned from a JS call to a third party, and ensuring it finds it’s way to my database. The page and form is in live view, and I would like to avoid breaking form validation or upsetting the live view life cycle.

NB: I played around with Phoenix ~4 years ago, and enjoyed it, but had to work on other technologies since.
I am back now, and intend to work with Elixir and Phoenix more seriously for a large side project.

Requirement I am implementing

I have a live view page that lists ‘locations’. To the UI, these are names, (EG: Home) and addresses.

To the backend, locations are all of the address constituents plus geometry (lat/lon). I have already setup PostGIS and geometry fields to capture this data enrichment.

At the bottom of the list of locations is a form to add new locations.

I have opted to obtain the geocoded address data from the front end in this case, simply because the library that gives me address autocomplete, returns the geocoded data in it’s response which is very convenient.

Running some sort of background job to geocode on the backend is less cost efficient in terms of the geocoding service, and would be wasted effort.

I have added javascript to app.js to perform ‘address autocomplete’ with the placekit.io service

As you can see from this javascript, I am using a very old (like me) web development trick where I set these values returned in the placekit api callback, into hidden field values.

function initPlaceKitAutocomplete() {
  const input = document.getElementById('lookup-address-input');
  if (input) {
    const pka = placekitAutocomplete('apikey-nothing-to-see-here', {
      target: '#lookup-address-input', // the 'id' in the HEEx <.input>
    });

    pka.on('pick', (value, item, index) => {
      console.log('pick', value, item, index);
      console.log('full address data', item);
      document.getElementById('latitude').value = item.lat;
      document.getElementById('longitude').value = item.lng;
    });
  }
}

document.addEventListener('DOMContentLoaded', function() {
  loadPlaceKitAPI(initPlaceKitAutocomplete);
});

The idea being that the data will then be available in the form changeset on submission (I hope)

Question

Is this a reasonable / best practice approach? It feels hacky.

I have a sense that it would be possible (and perhaps cleaner, although more work) to have the pka.on event update the form changeset by speaking directly to the live view component somehow (and perhaps not require hidden input fields?)

So the UI would send the geocoded data to the backend, which would in turn cause live view to update the UI. :thinking:

I would be very grateful for any advice on design and or implementation.

Thanks for reading :slight_smile:

PS: Happy to add template and elixir for context if that helps

Most Liked

LostKobrakai

LostKobrakai

I‘d just use the hidden inputs and leave the frontend decoupled from the backend. No need to change anything on the backend if you decide down the line that you want to use some other method of populating the form.

I‘d suggest to question what makes the location selection different from any other form input, so that it should get special treatment.

Edit: You want to create a change event on those inputs though to make phx-change pick up on the updated values. Setting just the values on the DOM nodes from js doesn‘t do that automatically as you might want to change the value without triggering a change event.

03juan

03juan

Since you want to integrate as much as possible with the LiveView lifecycle, this sounds like a good use-case for a Client Hook

  • No need for the DOMContentLoaded listener
  • attach hook directly on the input <input type="search" name="place" placeholder="Search place..." class="input w-full" id="placekit-input" phx-hook="PlaceKit">
  • all logic can go in mounted() callback
    • use {target: this.el} in options
    • save the client in the hook object this.pka = placekit kautocomplete(...)
    • send data to the server in the callback this.pka.on("pick", (_, item, _) => this.pushEvent("picked", {item})
  • update the UI in server assigns after receiving the event
  • call this.pka.destroy() in the hook’s destroyed() callback

Bonus if you want to vendor/code-split the autocomplete-js library, you can dynamically import the library in your mounted() cb then use it to instantiate the client object. Keep app.js lean for the pages that don’t need this feature.

Where Next?

Popular in Questions Top

chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
dotdotdotPaul
Okay, I’m having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I’m sure I’...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New

We're in Beta

About us Mission Statement