Lazy Load Form Fields In Rails With HTMX
Tags: ruby, rails, htmx, javascript
Published: 2021-03-20 16:42:29

[HTMX](https://htmx.org/) is small powerful library that lets you trigger HTTP AJAX requests simply by adding attributes to your HTML tags without having to write any Javascript.

![htmx_logo_800](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e3gubqg49v797og4w57c.png) 

In a previous post I wrote how to [Lazy Load Form Fields In Rails Using StimulusReflex](https://dev.to/dalezak/lazy-load-form-fields-in-rails-using-stimulusreflex-36oc). I like [Stimulus](https://stimulus.hotwire.dev/) and [StimulusReflex](https://docs.stimulusreflex.com/) both a lot, paired together they are incredibly powerful set of libraries for Rails.

However recently [HTMX](https://htmx.org/) caught my attention, _“a library that allows you to access modern browser features directly from HTML, rather than using Javascript”_. So I thought I would try implementing the same lazy loading of form fields using HTMX for comparison.

The _/app/views/users/_form.html.erb_ looks the same as before.

```erb
<%= form_with(model: user, url: [user], local: true) do |form| -%>
<div class="card">
  <ul class="list-group list-group-flush">
    <li class="list-group-item">
      <%= form.text_field :name, label: "Name", placeholder: "Enter name", required: true %>
    </li>
    <li class="list-group-item">
      <%= form.email_field :email, label: "Email", placeholder: "Enter email", required: true %>
    </li>
  </ul>
  <div class="card-footer">
    <%= form.submit "Save", class: "btn btn-secondary float-right" %>
  </div>
</div>
<% end %>
```

Now I include the placeholder with [HTMX triggers](https://htmx.org/docs/#triggers).

```erb
<li class="list-group-item">
  <label class="required">Repositories</label>
  <div hx-get="/users/repositories" hx-trigger="load delay:1s" hx-swap="innerHTML" hx-headers='{"X-Requested-With": "XMLHttpRequest"}'>
    <i>Loading...</i>
  </div>
</li>
```
Some important HTMX attributes here:

- hx-get makes GET request to /users/repositories endpoint
- hx-trigger triggered upon the element loading with 1 second delay
- hx-swap replacements the inner html of this element
- hx-headers pass some additional headers so Rails knows request.xhr? is true

In your _routes.rb_, add the new endpoint.

```ruby
resources :users do
  collection do
    get "repositories", as: :repositories
  end
end
```

Then add the repositories action to the _UsersController_.

```ruby
def repositories
  user = User.new
  user.repositories.build
  @form = ActionView::Helpers::FormBuilder.new(:user, user, view_context, {})
  github = Octokit::Client.new(access_token: current_user.token)
  github.auto_paginate = true
  @repositories = github.repos({}, query: { type: "any", sort: "asc" })
  respond_to do |format|
    format.html { render layout: request.xhr? == false }
  end
end
```
Now add the _/app/views/users/repositories.html.erb_.

```erb
<% @repositories.each do |repository| %>
  <%= @form.fields_for :repositories, repository do |ff| %>
    <div class="form-check form-check-inline">
      <%= ff.check_box :checked, class: "form-check-input" %>
      <%= ff.label :checked, repository.full_name.downcase, class: "form-check-label mr-2 mb-1" %>
    </div>
    <%= ff.hidden_field :uid, value: repository.id %>
    <%= ff.hidden_field :name, value: repository.name %>
    <%= ff.hidden_field :path, value: repository.full_name %>
    <%= ff.hidden_field :url, value: repository.html_url %>
  <% end %>
<% end %>
```
_That’s it!_ Similar to the StimulusReflex version it asynchronously loads the repositories.

Two important notes. 
1. [HTMX](https://htmx.org/) does not set the X-Requested-With header by default, so Rails doesn’t know it’s xhr request. So setting this header lets you use request.xhr? in your controller which is helpful to know when to render layouts.
2. If you don’t want to include hx-headers in every request, you can instead set it on a parent element (even the body tag) which will cause the header to be inherited.

```erb
<body hx-headers='{"X-Requested-With": "XMLHttpRequest"}'>
```

[HTMX](https://htmx.org/) is a [very small](https://htmx.org/docs/) yet incredibly [powerful library](https://htmx.org/examples/)! It has a lot of potential to creating reactive applications without writing any Javascript while still leveraging your existing Rails partials on the backend. 🚀