fukasawamoe / yoriyoi

1 stars 0 forks source link

達成状況を一目でわかるようにグラフ化 #77

Closed fukasawamoe closed 9 months ago

fukasawamoe commented 9 months ago
fukasawamoe commented 9 months ago

フォームオブジェクト使えるのかよくわからなかった

fukasawamoe commented 9 months ago

進捗ダメそうです

fukasawamoe commented 9 months ago

fields_forでゴリ押しでなんとかなりました edit_multipleってeditじゃダメなんでしょうか...?(GPTに聞いたら役割が違うよ、とのことでしたが)

「form_withで@.stepを扱っていいの?その場合はfields_forは不要なのでは?」というのはとても良い質問です。 まず、 form_with model: @.stepの部分は、formがどのモデルに対して動作すべきかをRailsに伝えます。しかし、現在はそれを一つのモデルのための単一フォームとして扱うのではなく、複数のStepインスタンスを更新するフォームとして使用しています。 一方、fields_forはフォーム内の特定の部分が特定のモデルオブジェクトに属することを示します。つまり、form_with model: @.stepという行は無視され、 fields_for "steps[#{step.id}]", stepが各ステップを適切に処理します。 したがって、form_withで@.stepというモデルを指定する代わりに、単にform_with url: update_multiple_step_path, method: :put,local: true と書くことができます。これにより、formは任意のURLに対してPUTメソッドのリクエストを送信します。そして、 fields_forは各個別のStepオブジェクトに対して独立したパラメータセットを作成します。

とのこと、よってform_withの@.stepは削除。

これでstep_idでデータを引っ張って来れるようになったのでやっとachievementに取り掛かります

fukasawamoe commented 9 months ago

やっとachievementモデルに行きつきました。Stepモデル作成時と登録skip時の両方でAchievementモデルを作成する挙動は実装できました。 その中でのコードでhas_oneのアソシエーション時に使えるメソッドを初めて知りました

@.step.create_achievement! は Rails の has_one アソシエーションに対して提供されるメソッドの一部です。アソシエーションとは、モデル間のリレーションシップを定義するもので、この場合は Step モデルと Achievement モデルの間に has_one リレーションシップを定義しています。 これにより、ある Step インスタンスに対して、.create_achievement または .create_achievement! といったメソッドを用いて関連する Achievement インスタンスを作成することができます。これは Achievement.new を使ってインスタンスを作成し、それを保存するよりも簡潔に行うことができます。 create_achievement と createachievement! の違いは、前者は作成に失敗したときに false を返し、後者は例外 ActiveRecord::RecordInvalid をスローするところです。 このメソッドの名前は自動的に生成され、基本的にはモデル名の create というプレフィックスから成ります。したがって、 @.step.create_achievement! は @.step に関連する Achievement を作成し、それがうまくいかない場合は例外をスローするようにします。 このように、Rails ではアソシエーションを使ってモデル間の関係を効率的に管理することができ、コードも読みやすく保つことができます。素晴らしい点ですね!

引き続きボタンの実装からやっていきます

fukasawamoe commented 9 months ago

home/index.html.erb

<%= render template: "goals/show", locals: { goal: @goal, steps: @steps } %>

goals/show.html.erb

<% steps.each do |step| %>
          <% action = step.action %>
          <% times_set = step.times_set %>
          <% unless action.blank? && times_set.blank? %>
            <li><i class="fa-solid fa-fire" style="color: #f26845;"></i>  <%= action %>を週<%= times_set %>回行う</li>
            <div class='flex flex-wrap'>
              <% (0..6).each do |day| %>
                <% if step.achievement.day_check.include?(day) %>
                  <%= render "achievements/on_button", day: day, step: step %>
                <% else %>
                  <%= render "achievements/off_button", day: day, step: step %>
                <% end %>
              <% end %>
            </div>
            <% all_blank = false %>
          <% end %>
        <% end %>

achievement/_off_button.html.erb

<div>
  <%= button_to t('date.abbr_day_names')[day], add_day_check_step_path(step.achievement, day: day), method: :post, class:"mx-auto m-3 bg-white border-2 py-2 px-6 rounded-full text-lg" %>
</div>

のようになっている場合はstepのスコープ内なのでsteps_controllerにロジックを書く。 そしてボタンの送信先もstepのpath宛てにする。

fukasawamoe commented 9 months ago

達成ボタンの実装完了

JSでちまちま書こうと考えていたが、HotwireのTurboをうまく使えないかと考えたところ、<%= turbo_frame_tag "day_check" do %>の記載のみでajax化できるようになり、感動。

<追記> turboは動きが速いが正確じゃない Image from Gyazo

↓<%= turbo_frame_tag step do %>の記述し直しで治った? Image from Gyazo

fukasawamoe commented 9 months ago