How to pre-populate collection_check_boxes in an edit form?

GitHub repo: https://github.com/Yorkshireman/mywordlist

I got out of this trait. I'm sure there is a way, possibly requiring some kind of code inside the html hash parameters, but I can't handle it. Any ideas?

When you visit the _edit_word_form.html.erb partial for Word that has one or more categories, the Categories checkboxes are all unchecked, requiring the user to select them again, even if they do not want to change the categories.

Text fields for: title and: description are PRELIMINARY filled (fortunately).

_edit_word_form.html.erb:

<%= form_for(@word) do %> <%= fields_for :word, @word do |word_form| %> <div class="field form-group"> <%= word_form.label(:title, "Word:") %><br> <%= word_form.text_field(:title, id: "new_word", required: true, autofocus: true, class: "form-control") %> </div> <div class="field form-group"> <%= word_form.label(:description, "Definition:") %><br> <%= word_form.text_area(:description, class: "form-control") %> </div> <% end %> <%= fields_for :category, @category do |category_form| %> <% if current_user.word_list.categories.count > 0 %> <div class="field form-group"> <%= category_form.label(:title, "Choose from existing Categories:") %><br> <%= category_form.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) do |b| %> <%= b.label(class: "checkbox-inline") { b.check_box + b.text } %> <% end %> </div> <% end %> <h4>AND/OR...</h4> <div class="field form-group"> <%= category_form.label(:title, "Create and Use a New Category:") %><br> <%= category_form.text_field(:title, class: "form-control") %> </div> <% end %> <div class="actions"> <%= submit_tag("Update!", class: "btn btn-block btn-primary btn-lg") %> </div> <% end %> 

Relevant part of the words /index.html.erb:

 <% current_user.word_list.words.alphabetical_order_asc.each do |word| %> <tr> <td> <%= link_to edit_word_path(word) do %> <%= word.title %> <span class="glyphicon glyphicon-pencil"></span> <% end %> </td> <td><%= word.description %></td> <td> <% word.categories.alphabetical_order_asc.each do |category| %> <a class="btn btn-info btn-sm", role="button"> <%= category.title %> </a> <% end %> </td> <td> <%= link_to word, method: :delete, data: { confirm: 'Are you sure?' } do %> <span class="glyphicon glyphicon-remove"></span> <% end %> </td> </tr> <% end %> 

words_controller.rb:

 class WordsController < ApplicationController before_action :set_word, only: [:show, :edit, :update, :destroy] before_action :authenticate_user! # GET /words # GET /words.json def index @words = Word.all @quotes = Quote.all end # GET /words/1 # GET /words/1.json def show end # GET /words/new def new @word = current_user.word_list.words.build end # GET /words/1/edit def edit end # POST /words # POST /words.json def create @word = Word.new(word_params) respond_to do |format| if @word.save format.html { redirect_to @word, notice: 'Word was successfully created.' } format.json { render :show, status: :created, location: @word } else format.html { render :new } format.json { render json: @word.errors, status: :unprocessable_entity } end end end # PATCH/PUT /words/1 # PATCH/PUT /words/1.json def update #need to first remove categories from the word @word.categories.each do |category| @word.categories.delete category end #then push categories in from the category_params if params["category"].include?(:category_ids) (params["category"])["category_ids"].each do |i| next if i.to_i == 0 @word.categories << Category.find(i.to_i) unless @word.categories.include?(Category.find(i.to_i)) end end if category_params.include?(:title) && ((params["category"])["title"]) != "" @word.categories << current_user.word_list.categories.build(title: (params["category"])["title"]) end respond_to do |format| if @word.update(word_params) format.html { redirect_to words_path, notice: 'Word was successfully updated.' } format.json { render :show, status: :ok, location: @word } else format.html { render :edit } format.json { render json: @word.errors, status: :unprocessable_entity } end end end # DELETE /words/1 # DELETE /words/1.json def destroy @word.destroy respond_to do |format| format.html { redirect_to words_url, notice: 'Word was successfully destroyed.' } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_word @word = Word.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def word_params params.require(:word).permit(:title, :description, :category_ids) end def category_params params.require(:category).permit(:title, :category_ids, :category_id) end end 

categories_controller.rb:

 class CategoriesController < ApplicationController before_action :set_category, only: [:show, :edit, :update, :destroy] # GET /categories # GET /categories.json def index @categories = Category.all end # GET /categories/1 # GET /categories/1.json def show end # GET /categories/new def new @category = current_user.word_list.categories.build end # GET /categories/1/edit def edit end # POST /categories # POST /categories.json def create @category = Category.new(category_params) respond_to do |format| if @category.save format.html { redirect_to @category, notice: 'Category was successfully created.' } format.json { render :show, status: :created, location: @category } else format.html { render :new } format.json { render json: @category.errors, status: :unprocessable_entity } end end end # PATCH/PUT /categories/1 # PATCH/PUT /categories/1.json def update respond_to do |format| if @category.update(category_params) format.html { redirect_to @category, notice: 'Category was successfully updated.' } format.json { render :show, status: :ok, location: @category } else format.html { render :edit } format.json { render json: @category.errors, status: :unprocessable_entity } end end end # DELETE /categories/1 # DELETE /categories/1.json def destroy @category.destroy respond_to do |format| format.html { redirect_to categories_url, notice: 'Category was successfully destroyed.' } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_category @category = Category.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def category_params params.require(:category).permit(:title, :word_list_id, :category_ids) end end 

word_lists_controller.rb:

 class WordListsController < ApplicationController before_action :set_word_list, only: [:show, :edit, :update, :destroy] def from_category @selected = current_user.word_list.words.joins(:categories).where( categories: {id: (params[:category_id])} ) respond_to do |format| format.js end end def all_words respond_to do |format| format.js end end # GET /word_lists # GET /word_lists.json def index @word_lists = WordList.all end # GET /word_lists/1 # GET /word_lists/1.json def show @words = Word.all @word_list = WordList.find(params[:id]) end # GET /word_lists/new def new @word_list = WordList.new end # GET /word_lists/1/edit def edit end # POST /word_lists # POST /word_lists.json def create @word_list = WordList.new(word_list_params) respond_to do |format| if @word_list.save format.html { redirect_to @word_list, notice: 'Word list was successfully created.' } format.json { render :show, status: :created, location: @word_list } else format.html { render :new } format.json { render json: @word_list.errors, status: :unprocessable_entity } end end end # PATCH/PUT /word_lists/1 # PATCH/PUT /word_lists/1.json def update respond_to do |format| if @word_list.update(word_list_params) format.html { redirect_to @word_list, notice: 'Word list was successfully updated.' } format.json { render :show, status: :ok, location: @word_list } else format.html { render :edit } format.json { render json: @word_list.errors, status: :unprocessable_entity } end end end # DELETE /word_lists/1 # DELETE /word_lists/1.json def destroy @word_list.destroy respond_to do |format| format.html { redirect_to word_lists_url, notice: 'Word list was successfully destroyed.' } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_word_list @word_list = WordList.find(params[:id]) end # Never trust parameters from the scary internet, only allow the white list through. def word_list_params params[:word_list] end end 

word_list.rb:

 class WordList < ActiveRecord::Base belongs_to :user has_many :words has_many :categories end 

word.rb:

 class Word < ActiveRecord::Base belongs_to :word_list has_and_belongs_to_many :categories validates :title, presence: true scope :alphabetical_order_asc, -> { order("title ASC") } end 

category.rb:

 class Category < ActiveRecord::Base has_and_belongs_to_many :words belongs_to :word_list validates :title, presence: true scope :alphabetical_order_asc, -> { order("title ASC") } end 

schema.rb:

 ActiveRecord::Schema.define(version: 20150609234013) do create_table "categories", force: :cascade do |t| t.string "title" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "word_list_id" end add_index "categories", ["word_list_id"], name: "index_categories_on_word_list_id" create_table "categories_words", id: false, force: :cascade do |t| t.integer "category_id" t.integer "word_id" end add_index "categories_words", ["category_id"], name: "index_categories_words_on_category_id" add_index "categories_words", ["word_id"], name: "index_categories_words_on_word_id" create_table "quotes", force: :cascade do |t| t.text "content" t.string "author" t.datetime "created_at", null: false t.datetime "updated_at", null: false end create_table "users", force: :cascade do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" t.datetime "created_at", null: false t.datetime "updated_at", null: false end add_index "users", ["email"], name: "index_users_on_email", unique: true add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true create_table "word_lists", force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "user_id" end create_table "words", force: :cascade do |t| t.string "title" t.text "description" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "word_list_id" end add_index "words", ["word_list_id"], name: "index_words_on_word_list_id" end 

routes.rb:

 Rails.application.routes.draw do resources :quotes resources :categories resources :words devise_for :users, controllers: { registrations: "users/registrations" } # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". # You can have the root of your site routed with "root" # root 'welcome#index' root 'pages#home' post 'create_word_and_category' => 'new_word#create_word_and_category' end 
+5
source share
5 answers

This should be closer to what you were looking for:

 <%= b.label(class: "checkbox-inline", :"data-value" => b.value) { b.check_box + b.text } %> 
+1
source

Try changing _edit_word_form.html.erb as follows

 <%= form_for(@word) do |f| %> <div class="field form-group"> <%= f.label(:title, "Word:") %><br> <%= f.text_field(:title, id: "new_word", required: true, autofocus: true, class: "form-control") %> </div> <div class="field form-group"> <%= f.label(:description, "Definition:") %><br> <%= f.text_area(:description, class: "form-control") %> </div> <%= f.fields_for :category do |category_form| %> <% if current_user.word_list.categories.count > 0 %> <div class="field form-group"> <%= category_form.label(:title, "Choose from existing Categories:") %><br> <%= category_form.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) do |b| %> <%= b.label(class: "checkbox-inline") { b.check_box + b.text } %> <% end %> </div> <% end %> <h4>AND/OR...</h4> <div class="field form-group"> <%= category_form.label(:title, "Create and Use a New Category:") %><br> <%= category_form.text_field(:title, class: "form-control") %> </div> <% end %> <div class="actions"> <%= f.submit("Update!", class: "btn btn-block btn-primary btn-lg") %> </div> <% end %> 
+1
source

Thus, the user edits a word from his list of words, but you want to show checkboxes for all categories for all words in their list of words, checking those categories that are attached to the edited word. It is right?

It looks like you are missing the first parameter in #collection_check_boxes, which should be the object you are calling: category_ids on.

If the object is a word_list user, then something like:

 <%= category_form.collection_check_boxes(current_user.word_list, :category_ids, current_user.word_list.categories.all, :id, :title) do |b| %> 

There can be no exact answer - I cannot verify this, but I hope that this will give you something to continue.

+1
source

What is wrong

When you call category_form.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) fields_for :category category_form.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) in fields_for :category , you say that there is a method on @word.category called category_ids that will return the identifiers of the categories associated with @word.category . The checkbox will be checked if there is a corresponding identifier both in the category_ids results and in the current_user.word_list.categories.all .

I don't think there is @word.category or @word.category.category_ids . I think there is @word.categories .

How to fix it

Saying my instinct tells me that you need to use something like:

 <%= form_for(@word) do |f| %> <div class="field form-group"> <%= f.label(:title, "Word:") %><br> <%= f.text_field(:title, id: "new_word", required: true, autofocus: true, class: "form-control") %> </div> <div class="field form-group"> <%= f.label(:description, "Definition:") %><br> <%= f.text_area(:description, class: "form-control") %> </div> <% if current_user.word_list.categories.count > 0 %> <div class="field form-group"> <%= f.label(:categories, "Choose from existing Categories:") %><br> <%= f.collection_check_boxes(:category_ids, current_user.word_list.categories.all, :id, :title) %> </div> <% end %> <div class="actions"> <%= f.submit("Update!", class: "btn btn-block btn-primary btn-lg") %> </div> <% end %> 

Note that I have moved collection_check_boxes to the @word form @word , since you are hoping to build the โ€œcategoriesโ€ value for the current @word . I think that in any case, it should be a step in the right direction.

+1
source

The discussion may be inactive, but I will share my answer with future visitors.

Adding the option "{checked: @ array.map (&: to_param)}", since the last argument to collection_check_boxes solves your problem. See Link.

Example:
Suppose you have a software model and a platform (OS) and you want to select one or more operating systems that support your software.

 #views/softwares/edit.html.erb <%= form_for @software do |f| %> ... <%= f.label :supported_platform %> <%= f.collection_check_boxes(:platform_ids, @platforms, :id, :platform_name, { checked: @software.platform_ids.map(&:to_param) }) %> ... <%= f.submit "Save", class: 'btn btn-success' %> <% end %> 

Note:
@ software.platform_ids should be an array. If you are using SQLite, you need to convert the string to an array when you pull the data. I tested it with SQLite and validated. See my post for more details.

+1
source

All Articles