Search code examples
ruby-on-railsjquery-tokeninputvalidationerror

Ruby on Rails - JS Input token, an issue when validation fails


I have a company model which can have many tags. It works fine, but in one occasion it does not work. The occasion is when company model validation fails. After :render => 'edit' it does not show tags in the view. I suspect the data-pre is not taking the data correctly. I would also like for tags to be preserved when solving validations.

I got this idea from here: http://railscasts.com/episodes/167-more-on-virtual-attributes

I use Input token control: http://loopj.com/jquery-tokeninput/

This is what I have in Company model regarding the tag_tokens:

  before_save :save_tag_tokens
  attr_writer :tag_tokens
  attr_accessible :tag_tokens

  def tag_tokens
    @tag_tokens || tags.to_json(:only => [:id, :name])
  end

  def save_tag_tokens
    if @tag_tokens
      @tag_tokens.gsub!(/CREATE_(.+?)_END/) do
        Tag.create!(:name => $1.strip.downcase).id
      end
      self.tag_ids = @tag_tokens.split(",")
    end
  end

Here is the code from the view:

  <div class="input text no-border">
    <% Tag.include_root_in_json = false %>
    <%= company_form.label :tag_tokens, t('form.account.company.edit.company_tags_html')%>
    <%= company_form.text_field :tag_tokens, :id => 'company_tag_tokens', "data-pre" => @company.tag_tokens%>
    <p class="tip"><%= t('form.account.company.edit.tag_tip') %></p>
  </div>

EDIT:

OK, so I see what is the problem with the above code.

When i load edit page data-pre contains this: data-pre="[{&quot;id&quot;:1704,&quot;name&quot;:&quot;dump truck&quot;}]". when I submit the form with validation error the data-pre contains: data-pre="1704".

if i change the code to this:

def tag_tokens
    tags.to_json(:only => [:id, :name])
end

new tags that were not yet save to the company model are removed, because they are read from the DB everytime. How can I preserve the entered data between form transitions?


Solution

  • OK, I've written a solution, it might not be the nicest one, but it works to me! It parses the input token value to JSON format (when validation fails), which is used when loading the page. Under page load it just loads tags from DB.

     def tag_tokens
        if @tag_tokens
          #if there is user info, parse it to json format. create an array
          array = @tag_tokens.split(",")
          tokens_json = []
          #loop through each tag and check if it's new or existing
          array.each do |tag|
            if tag.to_s.match(/^CREATE_/)
              #if new generate json part like this:
              tag.gsub!(/CREATE_(.+?)_END/) do
                tokens_json << "{\"id\":\"CREATE_#{$1.strip.downcase}_END\",\"name\":\"Add: #{$1.strip.downcase}\"}"
              end
            else
              #if tag is already in db, generate json part like this:
              tokens_json << "{\"id\":#{tag},\"name\":\"#{Tag.find_by_id(tag).name}\"}"
            end
          end
          #encapsulate the value for token input with [] and add all tags from array
          "[#{tokens_json.to_sentence(:last_word_connector  => ',', :words_connector => ',', :two_words_connector => ',')}]"
        else
          #if there is no user input already load from DB
          tags.to_json(:only => [:id, :name])
        end
      end