Forum: Ruby on Rails How to order/match text_field with values in edit mode for existing and new values ?

Posted by Serguei Cambour (javix)
on 2013-03-11 09:52
(Received via mailing list)
I'm using a nested form to enter worked time weekly. So when creating a 
new
week entry of 7 days I create a form as follows:

#timesheets/new.html.erb

 <%= form_for @timesheet do |f| %>
    <%= render 'fields', f: f %>
    <%= f.submit class: 'btn btn-large btn-primary'%>
  <% end %>

#timesheets/_fields.html.erb
..
<% @timesheet.date_range(@timesheet.start_date, 
@timesheet.end_date).each
do |week_date| %>
<th><%= week_date.to_formatted_s(:short) %></th>
<% end %>
<%= f.fields_for :activities do |builder|%>
<%= render "activity_fields", :f => builder %>
<% end %>
....

#timesheets/_activity_fields.html.erb
<tr class="fields">
<td><%= f.collection_select(:task_id, @tasks, :id, :name, :prompt => 
true)
%></td>
<%= f.fields_for :time_entries do |builder|%>
<%= render 'time_entries_fields', :f => builder %>
<% end %>
...

#timesheets/_activity_fields.html.erb

<td>
<%= f.hidden_field :workdate %>
<%= f.text_field :worktime, size: 3 %>
</td>

No problem for create a week entry and to display it in the 
show.html.erb
page:

#timesheets.show.html.erb

<% provide(:title, 'Timesheet details')%>
<p>
<strong>Start Date:</strong>
<%= @timesheet.start_date.to_formatted_s(:rfc822) %>
</p>
<p>
<strong>End Date:</strong>
<%= @timesheet.end_date.to_formatted_s(:rfc822) %>
</p>
<p>
<strong>Status:</strong>
<%= @timesheet.status %>
</p>

<% if @activities.empty? %>
  <h2>No activities found</h2>
<% else %>
  <h2>activities list</h2>
  <table class="table table-striped">
    <thead>
      <tr>
        <th>Activity</th>
        <% @timesheet.date_range(@timesheet.start_date,
@timesheet.end_date).each do |week_day| %>
<th><%= week_day.to_formatted_s(:short)%></th>
<%end%>
      </tr>
    </thead>
    <tbody>
      <% @activities.each do |activity| %>
<tr>
<td><%= activity.task.name %></td>
<% @timesheet.date_range(@timesheet.start_date, 
@timesheet.end_date).each
do |week_day| %>
<td><%= activity.daily_spent(week_day) %></td>
<% end %>
</tr>
<% end %>
    </tbody>
  </table>
    <%= will_paginate @activities %>
  <% end %>
<%=  link_to 'Back', timesheets_path %> | <%= link_to 'Edit',
edit_timesheet_path(@timesheet) %>

The problem is in the *edit page*: the existing day entries do not match
with the table column header displaying week day. I mean that the 
existing
worked time values are displayed first, and the empty text fields just
follow them:

So here is what was entered when creating a new week entry:

Task        |   11-03  |    12-03  |   13-03  |   14-03  |   15-03  |
16-03  |   17-03  |
 Task-1    |    0.5     |    1        |             |
 Task-2    |   0.5      |              |   1        |

So instead of displaying the table fields in the same way as it was 
during
the creation, the edit page is displayed as follows:

Task        |   11-03  |    12-03  |   13-03  |   14-03  |   15-03  |
16-03  |   17-03  |
 Task-1    |    0.5     |    1        |             |
 Task-2    |   0.5      |     1       |             |

Here is how it is defined in the controller:

#timesheets_controller.rb
...
def show
    @timesheet = current_user.timesheets.find(params[:id])
    @activities = @timesheet.activities.paginate(page: params[:page])
  end

  def new
    @timesheet = current_user.timesheets.new
    activity = @timesheet.activities.build
    @timesheet.create_activity_days(activity)
  end

  def create
    @timesheet = current_user.timesheets.new(params[:timesheet])
    @timesheet.status = Timesheet::SUBMITTED
    if @timesheet.save
      flash[:success] = 'Timesheet created sucessfully'
      redirect_to @timesheet
    else
      render 'new'
    end
  end

  def edit
    @timesheet = current_user.timesheets.find(params[:id])
    @timesheet.activities.each do |activity|
      @timesheet.date_range(@timesheet.start_date,
@timesheet.end_date).each do |week_day|
        unless activity.worked_days.include?(week_day)
          activity.time_entries.build(workdate: week_day)
        end
      end
    end
  end
...
Of course, the models have their definition as follows:

#timesheet.rb
class Timesheet < ActiveRecord::Base
  attr_accessible :status, :user_id, :start_date, :end_date,
:activities_attributes
  has_many :activities, dependent: :destroy
  has_many :time_entries, through: :activities
  accepts_nested_attributes_for :activities, :allow_destroy => true,
:reject_if => proc { |attributes| attributes['task_id'].blank? }
....
end

#activity.rb
class Activity < ActiveRecord::Base
  attr_accessible :task_id, :timesheet_id, :time_entries_attributes
  has_many :time_entries, order: :workdate, dependent: :destroy

  accepts_nested_attributes_for :time_entries, :allow_destroy => true,
:reject_if => proc { |attributes| attributes['worktime'].blank? }
...
end

#time_entry.rb
class TimeEntry < ActiveRecord::Base
  attr_accessible :activity_id, :workdate, :worktime

  belongs_to :activity
....
end

Any idea how to solve that? Thank you.
Posted by Colin Law (Guest)
on 2013-03-11 11:05
(Received via mailing list)
On 11 March 2013 08:50, Javix <s.cambour@gmail.com> wrote:
> I'm using a nested form to enter worked time weekly. So when creating a new
> week entry of 7 days I create a form as follows:

The first thing you need to work out is where the code is failing.
For example you say that it is not displaying correctly.  Is the
problem that the data in the db is wrong or the controller is not
fetching the data correctly or the view is not displaying it
correctly?

Have a look at the Rails Guide on debugging.  That will show you
techniques that you can use to debug your code.  Once you have worked
out which section of code is not working correctly then you will
probably understand what is wrong.  If not then come back showing us
just the failing code.

Colin
Posted by Serguei Cambour (javix)
on 2013-03-11 15:33
(Received via mailing list)
On Monday, March 11, 2013 11:03:53 AM UTC+1, Colin Law wrote:
> correctly?
>
> I displayed the generated values after modifying the code as follows:
#timesheets_controller.rb
def edit
    @timesheet = current_user.timesheets.find(params[:id])
    @entries = []
    @timesheet.activities.each do |activity|
      @entries = activity.time_entries
      puts "+++++++ @entries: #{@entries.inspect}"
      @timesheet.date_range.each do |week_day|
        puts "++++++++++++++++++++ Looping : week_day: #{week_day}"
        puts "++++++++++++++++++++ worked_days:
#{activity.worked_days.inspect}"
        puts "++++++++++++++++++++ entries size: #{@entries.size}"
        unless activity.worked_days.include?(week_day)
          puts "!!!!!!!! BANG=> adding day: #{week_day}"
          entry = activity.time_entries.build(workdate: week_day)
          puts "+++++++++++ new entry to be added: #{entry.inspect}"
            @entries.push(entry)
            puts "++++++++++++++ Entries after adding: size:
#{@entries.size}, inspect: #{@entries.inspect}"
        end
      end
      @entries.sort! { |a, b| a.workdate <=> b.workdate }
      @entries.each do |e|
        puts "++++++++++++++++++++++++++++++++++++++++++++++++"
        puts "final entries size: #{@entries.size}"
        puts "id: #{e.id}, workdate: #{e.workdate}, worktime:
#{e.worktime.to_s}"
        puts "++++++++++++++++++++++++++++++++++++++++++++++++"
      end

    end
  end

The problem is that after calling 'push method, I have double time_entry
created for every date that does not have work time entered:

++++++++++++++++++++ Looping : week_day: 2013-03-11
++++++++++++++++++++ worked_days: [Mon, 11 Mar 2013, Tue, 12 Mar 2013]
++++++++++++++++++++ entries size: 2
++++++++++++++++++++ Looping : week_day: 2013-03-12
++++++++++++++++++++ worked_days: [Mon, 11 Mar 2013, Tue, 12 Mar 2013]
++++++++++++++++++++ entries size: 2
++++++++++++++++++++ Looping : week_day: 2013-03-13
++++++++++++++++++++ worked_days: [Mon, 11 Mar 2013, Tue, 12 Mar 2013]
++++++++++++++++++++ entries size: 2
!!!!!!!! BANG=> adding day: 2013-03-13
+++++++++++ new entry to be added: #<TimeEntry id: nil, activity_id: 33,
workdate: "2013-03-13", worktime: nil, created_at: nil, updated_at: nil>
++++++++++++++ Entries after adding: size: 4, inspect: [#<TimeEntry id:
197, activity_id: 33, workdate: "2013-03-11", worktime:
#<BigDecimal:7fe81a6eb2c0,'0.1E1',9(36)>, created_at: "2013-03-11
14:23:04", updated_at: "2013-03-11 14:23:04">, #<TimeEntry id: 198,
activity_id: 33, workdate: "2013-03-12", worktime:
#<BigDecimal:7fe81a6ea8e8,'0.5E0',9(45)>, created_at: "2013-03-11
14:23:04", updated_at: "2013-03-11 14:23:04">, #<TimeEntry id: 199,
activity_id: 33, workdate: "2013-03-13", worktime: nil, created_at:
"2013-03-11 14:23:07", updated_at: "2013-03-11 14:23:07">, #<TimeEntry 
id:
199, activity_id: 33, workdate: "2013-03-13", worktime: nil, created_at:
"2013-03-11 14:23:07", updated_at: "2013-03-11 14:23:07">]

In the very and I have a collection of 12 time entries instead of 7 to
display in the partial:

#timesheets/_activity_fields.html.erb

<tr class="fields">
<td><%= f.collection_select(:task_id, @tasks, :id, :name, :prompt => 
true)
%></td>
<%= f.fields_for :time_entries do |builder|%>
<%= render 'time_entries_fields', :f => builder, collection: @entries %>
<% end %>
 <% unless f.object.nil? || f.object.new_record? %>
<td><%= link_to_remove_fields "remove", f %></td>
<% end %>
</tr>

I have no idea why 2 objects are saved every time when calling Array 
push
method.

Regards
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.