I have a page that displays a title and then a group of links. It looks like this:
Right now they are displaying all the links that pertain to the entire page and not to the individual titles. I would like for each title to only the links that pertain to it. I am using rails 4, cocoon and haml. Any ideas as to how I might do that?
essential.rb
class Essential < ActiveRecord::Base
has_many :favorites, :dependent => :destroy
has_many :catalogs, :dependent => :destroy
has_many :labels, :dependent => :destroy
has_many :members,:dependent => :destroy
has_many :sub_catalogs, through: :catalogs, :dependent => :destroy
has_many :sub_favorites, through: :favorites, :dependent => :destroy
has_many :instruments, through: :members, :dependent => :destroy
belongs_to :user
accepts_nested_attributes_for :favorites, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :catalogs, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :labels, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :members, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :sub_catalogs, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :sub_favorites, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :instruments, :reject_if => :all_blank, :allow_destroy => true
validates :band_name, :bio, :image, presence: true
has_attached_file :image, styles: { medium: "400x400#" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/
end
catalog.rd
class Catalog < ActiveRecord::Base
belongs_to :essential
has_many :sub_catalogs, :dependent => :destroy
accepts_nested_attributes_for :sub_catalogs, :reject_if => :all_blank, :allow_destroy => true
end
sub_catalog.rb
class SubCatalog < ActiveRecord::Base
belongs_to :catalog
end
essentials_controller.rb
class EssentialsController < ApplicationController
before_action :find_essential, only: [:show, :edit, :update, :destroy]
def index
#essential = Essential.all.order("created_at DESC")
end
def new
#essential = current_user.essentials.build
end
def show
end
def create
#essential = current_user.essentials.build(essential_params)
if #essential.save
redirect_to #essential, notice: "Successfully created new essential"
else
render 'new'
end
end
def edit
end
def update
if #essential.update(essential_params)
redirect_to #essential
else
render 'edit'
end
end
def destroy
#essential.destroy
redirect_to root_path, notice: "Successfully deleted Essential"
end
private
def essential_params
params.require(:essential).permit(:band_name, :bio, :image, :country, :album,
favorites_attributes: [:id, :song_title, :url, :url_type, :_destroy, sub_favorites_attributes: [:id, :fav_url, :fav_url_type, :_destroy]],
members_attributes: [:id, :band_member, :position, :_destroy, instruments_attributes: [:id, :position, :_destroy]],
labels_attributes: [:id, :record_label, :_destroy],
catalogs_attributes: [:id, :song_name, :_destroy, sub_catalogs_attributes: [:id, :sub_url, :sub_url_type, :_destroy] ])
end
def find_essential
#essential = Essential.find(params[:id])
end
end
show.html.haml
.main_content
#essential_top.row
.col-md-4
= image_tag #essential.image.url(:medium), class: "essential_image"
.col-md-8
#essential_info
%h1= #essential.band_name
%p.bio= #essential.bio
.row
.col-md-6
#favorites
%h2 Favorites
%table
- #essential.favorites.each do |favorite|
%tr
%td= favorite.song_title
- #essential.sub_favorites.each do |sub_favorite|
%td= link_to sub_favorite.fav_url_type, "https://www.#{sub_favorite.fav_url}"
.col-md-6
#catalogs
%h2 Catalogs
%table
- #essential.catalogs.each do |catalog|
%tr
%td= "#{catalog.song_name} |"
- #essential.sub_catalogs.each do |sub_catalog|
%td= link_to sub_catalog.sub_url_type, "https://www.#{sub_catalog.sub_url}"
.row
.col-md-6
#labels
%h2 Record Label(s)
%table
- #essential.labels.each do |label|
%tr
%td= label.record_label
.col-md-6
#members
%h2 Members
%table
- #essential.members.each do |member|
%tr
%td= member.band_member
- #essential.instruments.each do |instrument|
%td= instrument.position
.col-md-12
= link_to "Back", root_path, class: "btn btn-secondary"
- if user_signed_in?
= link_to "Edit", edit_essential_path, class: "btn btn-secondary"
= link_to "Delete", essential_path, method: :delete, data: {confirm: "Are you sure?" }, class: "btn btn-secondary"
form.html.haml
= simple_form_for #essential, html: { multipart: true } do |f|
- if #essential.errors.any?
#errors
%p
= #essential.errors.count
Prevented this essential from saving
%ul
- #essential.errors.full_message.each do |msg|
%li = msg
.panel-body
= f.input :band_name, placeholder: "Band Name", label: false, input_html: { class: 'form-control form-inline'}
= f.input :bio, placeholder: "Bio", label: false, input_html: { class: 'form-control'}
= f.input :image, placeholder: "Image", label: false, input_html: { class: 'form-control'}
= f.input :country, collection: ["England", "United States", "Ireland", "Germany", "France", "Finalnd", "Sweden", "Wales", "Scotland", "Denmark", "Iceland", "Spain", "Italy"], input_html: { class: "form-control form-input" }
= f.input :album, collection: 1..25, input_html: { class: "form-control" }
.row
.col-md-6
%h3 Favorites
#favorites
= f.simple_fields_for :favorites do |favorite|
= render 'favorite_fields', f: favorite
.links
= link_to_add_association 'Add Favorite', f, :favorites, class: 'btn btn-secondary add-button'
.col-md-6
%h3 Catalog
#catalogs
= f.simple_fields_for :catalogs do |catalog|
= render 'catalog_fields', f: catalog
.links
= link_to_add_association 'Add Catalog', f, :catalogs, class: 'btn btn-secondary add-button'
.row
.col-md-6
%h3 Record Label(s)
#labels
= f.simple_fields_for :labels do |label|
= render 'label_fields', f: label
.links
= link_to_add_association 'Add Record Label', f, :labels, class: 'btn btn-secondary add-button'
.col-md-6
%h3 Band Members
#members
= f.simple_fields_for :members do |member|
= render 'member_fields', f: member
.links
= link_to_add_association 'Add Band Member', f, :members, class: 'btn btn-secondary add-button'
= f.button :submit, class: 'btn btn-primary'
In your view file, look at the catalogs section and change it to
%h2 Catalogs
%table
- #essential.catalogs.each do |catalog|
%tr
%td= "#{catalog.song_name} |"
- catalog.sub_catalogs.each do |sub_catalog|
%td= link_to sub_catalog.sub_url_type, "https://www.#{sub_catalog.sub_url}"
change #essential to the local variable catalog.
Related
I have 3 models :
user.rb
class User < ApplicationRecord
has_one :profile, inverse_of: :user, dependent: :destroy
accepts_nested_attributes_for :profile
has_many :bookings, inverse_of: :user, dependent: :destroy
has_many :owner_bookings, foreign_key: 'owner_id', class_name: 'Booking'
end
profile.rb
class Profile < ApplicationRecord
# mount_uploader :avatar, AvatarUploader
belongs_to :user, inverse_of: :profile
accepts_nested_attributes_for :user
end
booking.rb
class Booking < ApplicationRecord
belongs_to :user
belongs_to :owner, class_name: :User
belongs_to :poi
end
After a create booking, I would like to update the user profile (if some changes)
my form :
<%= simple_form_for [#poi.poitable, #booking] do |f| %>
<%= f.simple_fields_for #user.profile do |profile| %>
<%= profile.input :first_name,
input_html: { value: #user.profile.first_name } %>
<%= profile.input :last_name, input_html: { value: #user.profile.last_name } %>
<% end %>
<%= f.input :owner_id, input_html: { value: #poi.user.id } %>
<%= f.input :poi_id, input_html: { value: #poi.id } %>
<%= f.input :user_id, input_html: { value: #user.id } %>
<%= f.submit %>
<% end %>
booking_controller :
def new
#user = current_user
#poi = Sleep.friendly.find(params[:sleep_id]).poi
#booking = Booking.new
end
def create
#user = current_user
#poi = Sleep.friendly.find(params[:sleep_id]).poi
#booking = Booking.new(booking_params)
if #booking.save
#booking.user.profile.update_attribute(booking_params[:user_profile_attributes])
end
private
def booking_params
params.require(:booking).permit(
:poi_id,
:owner_id,
:user_id,
user_profile_attributes: [:user_id, :first_name, :first_name],
)
end
booking_params return only :poi_id, :owner_id, user_id, how can I get user_profile params to update #user.profile ?
Nested form (with simple_form gem) creates the record, but doesn't want to update it.
Checklist has many questions.
Question belongs to one checklist.
All strong params are setted.
So, in controller:
...
before_action :set_checklist, only: [:show, :edit, :update, :destroy]
...
def new
#checklist = Checklist.new
#checklist.questions.build
end
def create
#checklist = Checklist.new(checklist_params)
if #checklist.save
redirect_to checklists_url, notice: 'Checklist was successfully created.'
...
def edit
end
def update
if #checklist.update(checklist_params)
redirect_to #checklist, notice: 'Checklist was successfully updated.'
else
render :edit
end
end
...
private
def set_checklist
#checklist = Checklist.find(params[:id])
end
def checklist_params
params
.require(:checklist)
.permit(:title, :description,
questions_attributes: Question.attribute_names.map(&:to_sym).push(:_destroy))
end
in view form:
= simple_form_for(#checklist) do |f|
= f.error_notification
.form-inputs
= f.input :title
= f.input :description
...
%tbody.questions
= f.simple_fields_for :questions do |builder|
= render 'question_fields', f: builder
...
in _question_fields:
%tr.nested-fields
%td
= link_to_remove_association "remove", f, class: 'btn btn-primary btn-xs'
%td
= f.input :title, label: false
%td
= f.input :description, label: false
in checklist model:
has_many :questions, dependent: :destroy
accepts_nested_attributes_for :questions,
allow_destroy: true,
reject_if: proc { |att| att['title'].blank? }
in question model:
belongs_to :checklist, optional: true
Thank you
%tr.nested-fields
%td
= link_to_remove_association "remove", f, class: 'btn btn-primary btn-xs'
%td
= f.input :title, label: false
%td
= f.input :description, label: false
your partial is missing = f.hidden_field :id and = f.hidden_field :_destroy
I am developing a recipe app and am not able to show a dropdown menu for category and sub categories.
I created menu for categories and it is showing correctly, but now I want when I click on category it will show a subcategories which belongs to them and after clicking on subcategories I want it to show the recipes.
Here is my code:
controller
def index
if params[:category].blank?
#recipe = Recipe.all.order("created_at DESC")
elsif params[:category]
#category_id = Category.find_by(name: params[:category]).id
#recipe = Recipe.where(category_id: #category_id).order("created_at DESC")
else params[:subcategory]
#subcategory_id = Subcategory.find(name: params[:subcategory]).id
#recipe = Recipe.where(subcategory_id: #subcategory_id).order("created_at DESC")
end
end
def show
end
def new
#recipe = current_user.recipe.build
end
def create
#recipe = current_user.recipe.build(recipe_params)
if #recipe.save
redirect_to #recipe, notice: "recipe sucessfully created"
else
render 'new'
end
end
def edit
end
def update
if #recipe.update(recipe_params)
redirect_to #recipe
else
render 'edit'
end
end
def destroy
#recipe.destroy
redirect_to root_path, notice: "sucessfully deleted"
end
private
def recipe_params
params.require(:recipe).permit(:title, :description, :image, :category_id, :subcategory_id, ingredients_attributes: [:id, :name, :_destroy], directions_attributes: [:id, :step, :_destroy])
end
def find_recipe
#recipe = Recipe.find(params[:id])
end
end
applicatio.html.haml
!!! 5
%html
%head
%title Recipe App
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
= javascript_include_tag 'application', 'data-turbolinks-track' => true
= csrf_meta_tags
body
%nav.navbar.navbar-default
.container
.navbar-brand= link_to "Recipe Box", root_path
%ul.nav.navbar-nav
- Category.all.each do |category|
%li= link_to category.name, recipes_path(category: category.name)
- category.subcategories.each do |subcategory|
%li= link_to subcategory.name, recipes_path(subcategory: subcategory.name)
= render 'layouts/header'
- if user_signed_in?
%ul.nav.navbar-nav.navbar-right
%li= link_to "New Recipe", new_recipe_path
%li= link_to "Sign Out", destroy_user_session_path, method: :delete
- else
%ul.nav.navbar-nav.navbar-right
%li= link_to "Sign Up", new_user_registration_path
%li= link_to "Sign In", new_user_session_path
.container
- flash.each do |name, msg|
= content_tag :div, msg, class: "alert"
= yield
recipe.rb model
class Recipe < ActiveRecord::Base
searchkick
belongs_to :user
belongs_to :subcategory
has_many :ingredients
has_many :directions
accepts_nested_attributes_for :ingredients,
reject_if: proc { |attributes| attributes ['name'].blank? },
allow_destroy: true
accepts_nested_attributes_for :directions,
reject_if: proc { |attributes| attributes ['step'].blank? },
allow_destroy: true
validates :title, :description, :image, presence: true
has_attached_file :image, style: { :medium => "400x400#>" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
category.rb
class Category < ActiveRecord::Base
has_many :subcategories
has_many :recipes, :through => :subcategories
end
subcategory.rb
class Subcategory < ActiveRecord::Base
belongs_to :category
has_many :recipes
end
Please help me out with solving this question.
You can do this with javascript or JQuery. Call change function on category_name id and within this function change the option values of subcategory and so on.
First of all sorry about my English and about my knowledge of Rails, I am beginner who just started.
I have 2 models with relations:
CargoItems:
class CargoItem < ActiveRecord::Base
belongs_to :cargo
attr_accessible :cargo_id, :height, :length, :pieces, :weight, :width
validates :cargo, :height, :length, :width, :weight, :pieces, presence: true
validates :height, :length, :width, :pieces, numericality: { only_integer: true, greater_than_or_equal_to: 1}
validates :weight, numericality: { only_integer: true, greater_than_or_equal_to: 100}
end
Cargos:
class Cargo < ActiveRecord::Base
belongs_to :airport
belongs_to :user
belongs_to :cargo_state
belongs_to :cargo_price
belongs_to :cargo_description
has_many :cargo_items, :inverse_of => :cargo, :dependent => :destroy
attr_accessible :departure_date, :cargo_state_id, :airport_id, :cargo_price_id, :cargo_description_id, :cargo_items_attributes
accepts_nested_attributes_for :cargo_items, :allow_destroy => true, :reject_if => :all_blank
validates_associated :cargo_items
validates :departure_date, :cargo_state, :airport, :cargo_price, :cargo_description, presence: true
validates :departure_date, date: { after: Proc.new { Date.today - 1.day }, before: Proc.new { Time.now + 1.year } }, :on => :create
default_scope :order => 'departure_date DESC'
end
I use following GEMs: simple_forms, nested_forms. This is form to add new Cargo with multiple CargoItems (they have possibility to be added dynamically) belonging to Cargos:
<%= simple_nested_form_for #cargo, :wrapper => false do |f| %>
<%= f.association :airport, :label_method => :full_airport_name, :value_method => :id , :order => :iata_code %>
<%= f.input :departure_date , as: :date, start_year: Date.today.year,
end_year: Date.today.year + 16,
order: [:day, :month, :year] %>
<%= f.association :cargo_description, :label_method => :description, :value_method => :id, :order => :description %>
<%= f.association :cargo_price, :label_method => :price, :value_method => :id %>
<%= f.association :cargo_state, :label_method => :state, :value_method => :id %>
<hr>
<table>
<tr>
<th><%= :length %></th>
<th><%= :width %></th>
<th><%= :height %></th>
<th><%= :weight %></th>
<th><%= :pieces %></th>
</tr>
<%= f.simple_fields_for :cargo_items, #cargo_item do |cargo_items_fields| %>
<tr class="fields">
<td><%= cargo_items_fields.text_field :length %></td>
<td><%= cargo_items_fields.text_field :width %></td>
<td><%= cargo_items_fields.text_field :height %></td>
<td><%= cargo_items_fields.text_field :weight %></td>
<td><%= cargo_items_fields.text_field :pieces %></td>
<td><%= cargo_items_fields.link_to_remove "Remove this item", :confirm => 'Are you sure you want to remove this item?' %></td>
</tr>
<% end %>
</table>
<%= f.link_to_add "Add a item", :cargo_items %>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
Cargo controller:
def new
#cargo = Cargo.new
#cargo_item = CargoItem.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #cargo }
end
end
def create
#cargo = Cargo.new(params[:cargo])
#cargo.user_id = current_user[:id]
respond_to do |format|
if #cargo.save
format.html { redirect_to #cargo, notice: 'Cargo was successfully created.' }
format.json { render json: #cargo, status: :created, location: #cargo }
else
format.html { render action: "new" }
format.json { render json: #cargo.errors, status: :unprocessable_entity }
end
end
end
My problem is, validation errors are not shown for CargoItems, model is actually validating. I am not able to save Cargos with CargoItems which does not fulfill validating rules. But in case validations are not met, Cargo is not saved and it just stayed on the same page without any notification that CargoItems fields are invalid. Cargos fields validation errors are shown properly.
Thanx a lot for helping me.
You need to add this errors notication before your form. Take a look at mine for example
<% if #author.errors.any? %>
<div class="alert alert-block">
<ul>
<% #author.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% if #book.errors.any? %>
<div class="alert alert-block">
<ul>
<% #book.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for([#author, #book], html: { class: "well" }) do |f| %>
#labels and buttons...
<% end %>
I have an Author who has many Books, like so:
Author:
class Author < ActiveRecord::Base
attr_accessible :name
has_many :books, dependent: :destroy
accepts_nested_attributes_for :books, allow_destroy: true
validates :name, presence: true
validates :name, length: { minimum: 3 }
end
Book:
class Book < ActiveRecord::Base
attr_accessible :name, :year
belongs_to :author
validates :name, :year, presence: true
validates :year, numericality: { only_integer: true, less_than_or_equal_to: Time.now.year }
end
Simple Form also allows you to use label, hint, input_field, error and full_error helpers. for more detail documentation --> https://github.com/plataformatec/simple_form
Example code with error,
<%= simple_form_for #user do |f| %>
<%= f.label :username %>
<%= f.input_field :username %>
<%= f.hint 'No special characters, please!' %>
<%= f.error :username, id: 'user_name_error' %>
<%= f.full_error :token %>
<%= f.submit 'Save' %>
<% end %>
I'm getting this really weird error when I submit my nested form.
Can't mass-assign protected attributes: _destroy
Any idea why this may be? It's a bit of a concern as I'm having to remove the 'destroy' hidden_field with javascript temporarily until i figure out what it is, meaning i can't delete anything!
_form.html.erb
<%= nested_form_for(#post, :html=> {:multipart => true, :class=> "new_blog_post", :id=> "new_blog_post"}) do |f| %>
<%= field do %>
<%= f.text_field :title, placeholder: "Give your post a title", :class=>"span12" %>
<% end %>
<%= field do %>
<%= f.text_area :body, placeholder: "Write something here...", :id=>"blog-text", :class=>"span12" %>
<% end %>
<%= f.label :search_locations, "Add locations to your post" %>
<%= text_field_tag :name,"",:class=>"localename", :id=>"appendedInput", :placeholder=> "Name of the location", :autocomplete => "off" %>
<%= f.link_to_add "Add a location", :locations %>
<%= actions do %>
<%= f.submit "Submit", :class=>"btn", :disable_with => 'Uploading Image...' %>
<% end end%>
_posts_controller.rb_
class PostsController < ::Blogit::ApplicationController
...
def new
#post = current_blogger.blog_posts.new(params[:post])
#location = #post.locations.build
end
def edit
#post = Post.find(params[:id])
##post = current_blogger.blog_posts.find(params[:id]) removed so any use can edit any post
#location = #post.locations.build
end
def create
location_set = params[:post].delete(:locations_attributes) unless params[:post][:locations_attributes].blank?
#post = current_blogger.blog_posts.new(params[:post])
#post.locations = Location.find_or_initialize_location_set(location_set) unless location_set.nil?
if #post.save
redirect_to #post, notice: 'Blog post was successfully created.'
else
render action: "new"
end
end
def update
#post = current_blogger.blog_posts.find(params[:id])
if #post.update_attributes(params[:post])
redirect_to #post, notice: 'Blog post was successfully updated.'
else
render action: "edit"
end
end
def destroy
#post = current_blogger.blog_posts.find(params[:id])
#post.destroy
redirect_to posts_url, notice: "Blog post was successfully destroyed."
end
location.rb
class Location < ActiveRecord::Base
after_save { |location| location.destroy if location.name.blank? }
has_many :location_post
has_many :posts, :through => :location_post
has_many :assets
attr_accessible :latitude, :longitude, :name, :post_id, :notes, :asset, :assets_attributes
accepts_nested_attributes_for :assets, :allow_destroy => true
include Rails.application.routes.url_helpers
def self.find_or_initialize_location_set(location_set)
locations = []
locations = locations.delete_if { |elem| elem.flatten.empty? }
location_set.each do |key, location|
locations << find_or_initialize_by_name(location)
end
locations
end
end
EDIT:
Snippet of rendered form in new.html.erb
<div class="row span locsearch">
<div class="input-append span3">
<input autocomplete="off" class="localename" id="appendedInput" name="name" placeholder="Name of the location" type="text" value="">
<span class="add-on"><input id="post_locations_attributes_0__destroy" name="post[locations_attributes][0][_destroy]" type="hidden" value="false"><i class="icon-trash"></i></span> </div>
<div class="latlong offset3 span4"> <p class="help-block">Enter the name of the town or city visited in this blog entry.</p>
</div>
<input class="LegNm" id="post_locations_attributes_0_name" name="post[locations_attributes][0][name]" type="hidden" value="Dresden">
<input class="long" id="post_locations_attributes_0_longitude" name="post[locations_attributes][0][longitude]" type="hidden" value="13.7372621">
<input class="lat" id="post_locations_attributes_0_latitude" name="post[locations_attributes][0][latitude]" type="hidden" value="51.0504088">
</div>
</div>
EDIT2:
post.rb
class Post < ActiveRecord::Base
require "acts-as-taggable-on"
require "kaminari"
acts_as_taggable
self.table_name = "blog_posts"
self.paginates_per Blogit.configuration.posts_per_page
# ==============
# = Attributes =
# ==============
attr_accessible :title, :body, :tag_list, :blogger_id, :coverphoto, :locations_attributes
# ===============
# = Photo Model =
# ===============
has_attached_file :coverphoto,
:styles => {
:coverbar => "600x300>", :medium => "250x250^" , :thumb => "100x100^"},
#:source_file_options => {:all => '-rotate "-90>"'},
:storage => :s3,
:s3_credentials => "#{Rails.root}/config/s3.yml",
:bucket => "backpackbug",
:path => "/:style/:id/:filename"
# ===============
# = Validations =
# ===============
validates :title, presence: true, length: { minimum: 6, maximum: 66 }
validates :body, presence: true, length: { minimum: 10 }
validates :blogger_id, presence: true
# =================
# = Associations =
# =================
belongs_to :blogger, :polymorphic => true
has_many :location_post
has_many :locations, :through => :location_post
belongs_to :profile
accepts_nested_attributes_for :locations, :allow_destroy => true, :reject_if => proc { |attributes| attributes['name'].blank? }
end end
This was solved with a combination of this and another answer, found here:
How can I fix this create function?
A short term fix is to add attr_accessible :_destroy and attr_accessor :_destroy.
Thanks both!
Add :_destroy to your attr_accessible list
attr_accessible ...., :_destroy
you should write to post.rb,
attr_accessible: locations_atributes
and
accepts_nested_attributes_for :locations, :allow_destroy => true
as you are updating the location object via post object.
replace f.hidden_field it must stay thus(line 2)
module ApplicationHelper
def link_to_remove_fields(name, f)
text_field_tag(:_destroy) + link_to_function(name, "remove_fields(this)")
end