Sunday, April 24, 2016

Editing Date in Rails MVC

Date_Select

I was working on a simple rails app to act as an administrative console for a larger project I am working on. The main application is a ReactJS based client so I haven't been doing much with Rails MVC, so my memory on using Rails' form helpers was a bit rusty.

I decided for the administrator's console, however I would use a standard Rails MVC design. Most everything came back quickly but one thing I had trouble with was how to edit dates using the Rails data_select tags. To get it to work end-to-end I had to piece several different pieces of information together.

So I thought I would write this short post to capture all that information in one place.

First, in the view for the form I configure the date_select like this:

<%= form_for goal, url:{action: 'goal_update'}, html: {class: 'form-horizontal'} do |f| %>
    <div class="form-group">
      <%= f.label(:start, 'Start:', class: 'col-sm-2 control-label') %>
      <div class="col-sm-10">
        <%= f.date_select(:start, class: 'form-control', disabled: disabled) %>
      </div>
    </div>
   <div class="form-group">
      <div class="col-sm-offset-2 col-sm-10">
        <%= f.submit('Save', class: 'btn btn-primary') %>
      </div>
    </div>
<% end %>

In this view code I have passed in a goal object and on line 1 I setup the form for it. Note: I am using Bootstrap here for most of the styling, with a little bit of my own CSS thrown in.

This goal has a start attribute which I setup the form element for on line 5.

The disabled attribute is passed into the view as well so I can use the same form code for when the administrator just wants to view the goal versus edit the goal.

Basically the view part of this is very simple and just requires following the syntax for the date_select tag.

It's a little more interesting we when get to the controller though.

Here is the relevant parts of goal_update from the controller. Note: goal_update is defined in my routes to point to a specific controller and action.

  def goal_update
    goal_id = params[:goal][:id].to_i
    @goal = Goal.where(id: goal_id).first
    ...

    @goal.start = date_from_params(params[:goal],'start')

    ... 
  end
  
  def date_from_params(hash, field_name)
    begin
      date_hash = %w(1 2 3).map { |e| hash["#{field_name}(#{e}i)"].to_i }
      return Date.civil(date_hash[0], date_hash[1], date_hash[2])
    rescue
      return nil
    end
  end

So you can see in line 2 all the information about the goal is passed in the params hash under the :goal key.

In line 2, I get the id of the goal to update and then load it up in line 3. Yes, I need more error checking here but thats not important for this post

Later in the method on line 6, I get the start date by calling a helper method I wrote to safely get the date from the params hash.

This helper method is shown starting at line 11.

Its important to note how the date values come in via the params object. The relevant portion of the goal hash will look like the following when it is submitted to the controller:

"start(1i)"=>"2016", "start(2i)"=>"4", "start(3i)"=>"27",

So the helper method takes the goal hash params[:goal] and the key for the date we want to construct 'start'. It uses the map function on line 13 to pull out the individual components and then on line 14 converts this to a Date which is returned. If anything goes wrong we catch any exceptions and return nil to indicate the date was not set.

And there you go.

One final note: It has been my intention to write a post once a week and as you can tell I haven't been able to keep that up over the last few months. I even thought I would just write a post once every two weeks, but that hasn't worked out very well either.

The problem is I need to balance project development with keeping this blog active. It's hard to do both. Right now project development is the higher priority, so for the foreseeable future I expect these posts to be a bit erratic. However, I am committed to keeping this blog active.

I do have some cool stuff I am doing in my currently active projects that I hope to write about in the near future.

So stay tuned.