简体   繁体   中英

Traversing through records by month/year with last and next button Rails 6

I am creating a two button functionality that allows me to pass parameters to a button by either subtracting the from the current month or moving to the next month. Its simple in theory, you click next and you'll see all records that were created for the month, or you'll click last month and you'll see the records that were created last month. The issue I am having is that the parameters will be one off when I click the next button. How can I navigate through months/years with two buttons? It doesn't seem to be remembering the parameter for the last button click event.

Rails Logs

Started GET "/facility_directory/lnpp?previous_month=5&commit=Next&next_month=7" for 127.0.0.1

facilities_controller.rb

def facility_directory

if params.has_key?(:select) # select month/year form
  month = Date.new(params[:select][:year].to_i, params[:select][:month].to_i)
   @facility_records = @facility.facility_records.where(created_at: month.all_month).order(created_at: :desc)
 @year = params[:select][:year]
 @month = params[:select][:month]
 elsif params.has_key?(:previous_month) # last month navigation button
   previous_month = Date.new(Date.today.year, params[:previous_month].to_i)
   @facility_records = @facility.facility_records.where(created_at: previous_month.all_month).order(created_at: :desc)
elsif params.has_key?(:next_month)
next_month = Date.new(Date.today.year, params[:next_month].to_i)
@facility_records = @facility.facility_records.where(created_at: next_month.all_month).order(created_at: :asc)
else
  date = Date.today
  start_date = date.at_beginning_of_month
  end_date = date.at_end_of_month
 @facility_records = @facility.chart_records.order(created_at: :desc).where(created_at: start_date..end_date)
end

end

facility_directory.html.erb

<%= form_tag facility_directory_path(@facility), :method => :get, :class => "form" do %>

 <div>
  <%= select_month(Date.today, {:include_blank => true.....%>
  <%= select_year(Date.today, {:include_blank => true...%>
</div>
<%= submit_tag('Search') %>
<% end %>

Navigation buttons last month and next month.

<%= form_tag facility_directory_path(@facility), method: :get, class: '' do %>
  <%= submit_tag 'Previous Month', class: 'button' %>
  <%= hidden_field_tag 'previous_month', @month.present? ? (@month.to_i - 1) : (Time.now.month - 1) %>
<%= submit_tag 'Next', class: 'button ui basic blue small' %>
<%= hidden_field_tag 'next_month', @month.present? ? (@month.to_i + 1) : (Time.now.month + 1) %>
<% end %>

Looks like you're also going to run into a problem where your view will render out a form where the hidden value of next_month will be 13, or the previous will be 0 - both of which are going to be invalid.

You're also setting default year and month logic in the view. You'll generally be better served if the setting of month and year is done in one place - the controller.

Shift all of the logic, including how to work out what next/previous is, into the controller and keep the view simple:

<%= form_tag facility_directory_path(@facility), method: :get, class: '' do %>
    <%= hidden_field_tag 'current_month', @month %>
    <%= hidden_field_tag 'current_year', @year %>

    <%= submit_tag 'Previous Month', class: 'button' %>
    <%= submit_tag 'Next', class: 'button ui basic blue small' %>
<% end %>

This form in the view now just holds the current state (month and year) and has a previous and next. There is no month calculation or default setting.

Controller

Then, in your controller, you can detect if:

  • There was no form input (default to use the current month)
  • The search button was pressed (the year and month drop down were used)
  • The next or previous buttons are pressed.

The value of the button pressed is passed to the controller, so if the next or previous button is pressed you'll see params[:commit] will be equal to either 'Previous Month' or 'Next'. And, you'll also have params[:current_year] and params[:current_month].

Make sure that your controller creates a default @year and @month as the current one.

The below should cover all of these cases.

if params[:commit] == "Previous Month" || params[:commit] == "Next"
    # Handle preview/next buttons
    now = Date.new(params[:current_year].to_i, params[:current_month].to_i, 1)
    if params[:commit] == "Previous Month"
        target_date = now - 1.month
    else
        target_date = now + 1.month
    end
elsif params.has_key?(:select)
    # Handle the select drop down
    target_date = Date.new(params[:select][:year].to_i, params[:select][:month].to_i)
else
    # Handle the default case - the first day of the current month
    target_date = Date.new(Date.today.year, Date.today.month, 1)
end

@year = target_date.year.to_i
@month = target_date.month.to_i

# You can now consolidate the Active Record query into one query which handles all cases
@facility_records = @facility.chart_records.order(created_at: :desc).where(created_at: target_date..target_date+1.month)

Using this approach, you can have one Active Record query which works in all instances.

The best thing to avoid this in future is to:

  1. Keep as much logic as possible out of the view
  2. Setup default values for the action in the controller (or model)
  3. Design your controller by thinking through the cases
  4. Know that you can read the value of the button pressed.

Hope this helps currently and in future.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM