Посмотрите видео к этому посту https://www.youtube.com/watch?v=gAXs3xhfHVg. Спасибо за подписку!

При создании платформы электронной коммерции Thoughts & Fitness я столкнулся с очень интересной проблемой. Мой клиент хотел иметь возможность создавать ежедневные тренировки на своем сайте. Каждая тренировка будет иметь определенное количество подъемов, и количество подъемов для каждой тренировки будет варьироваться.

Обычно при работе со связанными моделями мы склонны использовать вложенные ресурсы, но в этом сценарии вложенные ресурсы сделают добавление упражнений к каждой тренировке очень громоздким. Мне нужна была динамическая форма, которая позволила бы мне легко добавлять новые формы для каждого упражнения в ежедневных тренировках.

Если вам легче учиться с помощью видео, посмотрите версию этого поста на YouTube https://www.youtube.com/watch?v=gAXs3xhfHVg.

Конфигурация файла Gem

В этом уроке мы будем использовать гемы Simple Form и tailwindcss-rails. Simple Form делает написание форм Rails очень простым. Я считаю, что TailwindCSS намного лучше, чем Bootstrap, потому что он дает мне больше контроля, позволяя применять отдельные точки останова к каждому из моих классов, а также содержит больше компонентов, чем Bootstrap. Кроме того, мы собираемся использовать гем simple_form_tailwinded, который стилизует все наши формы.

Давайте приступим к настройке нашего файла gem.

gem "tailwindcss-rails", "~> 0.3.3"
gem 'simple_form'
gem 'simple_form-tailwind'

Для краткости я не буду описывать команды для установки всех этих драгоценных камней, но все их git-страницы указаны выше.

Создание наших моделей

У нас будет две модели DailyWorkouts и Lift. Каждый DailyWorkout будет иметь фокус, т.е. над какой частью тела мы работаем сегодня и будет много подъемов. Каждое упражнение будет иметь название, количество подходов, количество повторений и будет принадлежать ежедневной тренировке.

rails g model DailyWorkout focus 
rails g model Lift name sets:integer reps:integer daily_workout:references
rails db:migrate

Ссылки — это способ добавления внешнего ключа к нашей модели лифта. Этот ключ будет равен идентификатору DailyWorkout, которому принадлежит наш лифт.

Наш файл модели лифта должен выглядеть так:

class Lift < ApplicationRecord
  belongs_to :daily_workout
end

И наша Ежедневная тренировка должна выглядеть так:

class DailyWorkout < ApplicationRecord
  has_many :lifts
  accepts_nested_attributes_for :lifts
end

Согласно документации Rails, помощник accepts_nested_attributes_for позволяет нам сохранять связанные записи через его родителя. В основном это означает, что мы собираемся создавать все наши упражнения с помощью нашей модели DailyWorkout.

Контроллер

Давайте создадим наш ежедневный контроллер тренировок, не забудьте настроить маршруты!.

rails g controller DailyWorkouts new create

Теперь наш контроллер должен выглядеть так:

class DailyWorkoutsController < ApplicationController
 
  def new
    @daily_workout = DailyWorkout.new
    @daily_workout.lifts.build
  end

  def create
    @daily_workout = DailyWorkout.create(daily_workout_params)
    if @daily_workout.save
      flash[:success] = "A daily Workout Was Created !"
      redirect_to root_path
    else
       render :new
    end
  end

  private
  def daily_workout_params
    params.require(:daily_workout).permit(:focus, 
       lifts_attributes: %i[name sets reps])
  end

end

Внутри нашего нового действия мы создаем DailyWorkout и используем помощник сборки active_record, чтобы добавить набор упражнений в нашу ежедневную тренировку. Действие создания ничем не отличается от действия стандартной модели.

Наш метод daily_workout_params допускает значения: фокус нашей тренировки и атрибуты для каждого упражнения. Давайте начнем стилизовать наши представления.

Вид

Прежде чем мы сделаем нашу форму динамической, мы собираемся запрограммировать ее так, чтобы мы строили только одно упражнение для нашей ежедневной тренировки. Поскольку эта форма уже очень сложна, важно, чтобы мы максимально упростили ее, прежде чем переходить к более сложным задачам.

# new.html.erb
<%= simple_form_for @daily_workout do |f| %>
          <%= f.input :focus %>
          <%= f.simple_fields_for :lifts do |w| %>
            <div id='fieldsetContainer'>
              <fieldset id="0" class="text-indigo-500">
                <%= w.input :name %>
                <%= w.input :sets %>
                <%= w.input :reps %>
          <% end %>
          </fieldset>
          </div>
          <%= f.button :submit %>
<% end %>
<br/>
<button id=”addLift” onclick="addLift()">
       Add another lift
</button>

Метод simple_fields_for используется для создания обёртки для наших лифтов, он почти такой же, как и хелпер fields_for по умолчанию в Rails. После нашей формы мы создаем кнопку, которая будет вызывать функцию JavaScript при нажатии. Эта функция будет отвечать за добавление новой формы Лифта, что даст нам возможность добавлять больше Лифтов в нашу коллекцию.

Я поместил тег script непосредственно под HTML и включил ведение журнала консоли для отладки, посмотрите код ниже:

<script>
    const addWorkout = ()=> {
        const lastId =   document.querySelector('#fieldsetContainer').lastElementChild.id;
        console.log(lastId)

        const newId = parseInt(lastId,10) + 1
        console.log(newId)

        const newFieldset = document.querySelector('[id="0"]').outerHTML.replace(/0/g, newId)
        console.log(newFieldset)
        document.querySelector("#fieldsetContainer").insertAdjacentHTML("beforeend", newFieldset)
    }
</script>

По сути, все, что мы делаем, это находим идентификатор последней созданной формы лифта, создаем новую форму с идентификатором N + 1 и добавляем эту форму в наш HTML.

Вот и все для этого урока, надеюсь, вы чему-то научились!

Обязательно подпишитесь!

Ютуб: https://www.youtube.com/channel/UCfd8A1xfzqk7veapUhe8hLQ

Подкаст Кори Корнер: https://anchor.fm/coreys-corner