I was working with nested attributes, everything seemed to be fine until when I submitted my information and I got this error. It says it is in my EventsController file:
class EventsController < ApplicationController
def new
#event = Event.new
#event.songs.build
end
def index
#songs = Song.all
end
def show
#event = Event.find(params[:id])
#songs = #event.songs.paginate(page: params[:page])
end
def create
#event = current_user.events.build(event_params)
if #event.save
flash[:success] = "Event Created!"
redirect_to user_path(#event.user)
else
render 'welcome#index'
end
end
def destroy
end
private
def event_params
params.require(:event).permit(:name, :partycode, song_attributes: [:event_id, :artist, :title, :genre, :partycode])
end
end
Here is my new.html.erb file in my songs view(handles song submission based on selected event)
<br>
<br>
<div class ="container">
<div class="jumbotron">
<%= form_for Event.new do |f| %>
<h3>Enter a song:</h3>
<%= f.fields_for :songs, Song.new do |song_form| %>
<%= song_form.collection_select(:event_id, Event.all, :id, :name) %>
<%= song_form.text_field :artist, placeholder: "Artist" %>
<%= song_form.text_field :title, placeholder: "Title" %>
<%= song_form.text_field :genre, placeholder: "Genre" %>
<% end %>
<%= link_to_add_fields "Add Song", f, :songs %>
<%= f.text_field :partycode %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
The link_to_add_fields method is defined in my ApplicationHelper.rb file:
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render("songs_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
current_user is defined in Session_helper.rb file:
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
def createEvent(event)
session[:event_id] = event.id
end
# Returns the current logged-in user (if any).
def current_user
#current_user ||= User.find_by(id: session[:user_id])
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
def log_out
session.delete(:user_id)
#current_user = nil
end
end
Finally, here is my songs_fields file that generates fields only when a user clicks a link that says 'Add songs'
<fieldset>
<%= f.text_field :artist, placeholder: "Artist" %>
<%= f.text_field :title, placeholder: "Title" %>
<%= f.text_field :genre, placeholder: "Genre" %>
</fieldset>
I feel as though this is the last portion before I get everything in my app to work! So help on this would be tremendous :D
You answered your own question... if you're not logged in, current_user will be nil so you will get this error.
Option 1 (ugly): change your logic so current_user.events doesn't get called if current_user is nil, and just go straight to the render
Option 2 (better): use a before_action statement to force current_user to be set before the action is run. Depends on what you're using to authenticate, but with Devise it would look like this:
class EventsController < ApplicationController
before_action :authenticate_user!
I think maybe:
class EventsController < ApplicationController
before_action :log_in(user)
might do it for you.
Related
I'm building an events app using Rails 5.0 and have comments as a nested resource. Users can create and destroy comments, I'm trying to implement the edit/update function using Ajax/ remote: true so they can update a comment on the same page but it's not working. When I click on the edit link nothing happens. Here's the relevant code -
comments_controller.rb
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
def create
#event = Event.find(params[:event_id])
#comment = #event.comments.create(comment_params)
#comment.user_id = current_user.id
if #comment.save
redirect_to #event
else
render 'new'
end
end
# GET /comments/1/edit
def edit
#event = #comment.event
#comment = #event.comments.find(params[:id])
respond_to do |format|
format.html { render :edit }
format.js {}
end
end
def show
end
def update
if #comment.update(comment_params)
redirect_to #event, notice: "Comment was successfully updated!"
else
render 'edit'
end
respond_to do |f|
format.html { redirect_to #event, notice: "Comment Successfully updated!" }
format.js # render 'comments/update.js.erb'
end
end
def destroy
#event = Event.find(params[:event_id])
#comment = #event.comments.find(params[:id])
#comment.destroy
redirect_to event_path(#event)
end
private
def set_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:name, :body)
end
end
_comment.html.erb
<div class="comment clearfix">
<div class="comment_content">
<div id="comments" class="comment">
<p id="comment_name"><strong><%= #comment.name %></strong></p>
<p id="comment_body"><%= #comment.body %></p>
</div>
<p><%= link_to 'Edit', edit_event_comment_path(comment.event), id: "comments", remote: true %></p>
<p><%= link_to 'Delete', comment.event,
method: :delete,
class: "button",
data: { confirm: 'Are you sure?' } %></p>
</div>
</div>
update.js.erb
$('#comments').append("<%= j render #comment %>");
edit.js.erb
$('#comments').html("<%= j render 'form' %>");
_form.html.erb
<%= simple_form_for([#event, #comment], remote: true) do |f| %>
<%= f.label :comment %><br>
<%= f.text_area :body %><br>
<br>
<%= f.button :submit, label: 'Add Comment', class: "btn btn-primary" %>
<% end %>
I've never implemented this action before using Ajax so I'm probably making a few schoolboy errors here. Any assistance appreciated.
You are calling edit method on controller with this
<%= link_to 'Edit', [comment.event, comment], id: "comment", remote: true %>
And you have no edit.js.erb
For updating your comment, you would have to create a form with it's action url pointing to your update method, and marking it as remote true. Then when you submit, it will reach update directly, there is no need to pass through edit method.
There is a method for creating forms with ajax option as default called form_with, you can check it's guide and documentation here:
http://guides.rubyonrails.org/working_with_javascript_in_rails.html#form-with
Updating answer after your question update
Your form would need to become something like this
<%= simple_form_for :comment, :url => "/events/#{comment.event_id}/comments/#{comment.id}", :method => :put do |f| %>
<%= f.label :comment %><br>
<%= f.text_area :body %><br>
<br>
<%= f.button :submit, label: 'Add Comment', class: "btn btn-primary" %>
<% end %>
I've been following this railscast: https://www.youtube.com/watch?v=t_FNKR7jahM for learning how to implement nested attributes into my application. When I implement his code in the certain areas necessary I always get the same NoMethod error on the fields.
On the events show page where you can add songs, I am trying to add a 'Add Field' link that will add another group of songs(a field for artist, title, and genre).
Here is my EventController:
class EventsController < ApplicationController
def new
#event = Event.new
#event.songs.build
end
def index
#songs = Song.all
end
def show
#event = Event.find(params[:id])
#songs = #event.songs.paginate(page: params[:page])
end
def create
#event = current_user.events.build(event_params)
if #event.save
flash[:success] = "Event Created!"
redirect_to user_path(#event.user)
else
render 'welcome#index'
end
end
def destroy
end
private
def event_params
params.require(:event).permit(:name, :partycode, song_attributes: [:artist, :title, :genre])
end
end
Here is my Event model:
class Event < ApplicationRecord
belongs_to :user
has_many :songs, dependent: :destroy
accepts_nested_attributes_for :songs, allow_destroy: true
validates :name, presence: true
validates :partycode, presence: true, length: {minimum: 5}
end
Here is the show page in which the songs are added:
<section class="song_form">
<%= render 'shared/song_form' %>
<%= form_for #event do |f| %>
<%= f.fields_for :fields do |builder| %>
<%= render 'events/songs_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Field", f, :fields %>
<% end %>
</section>
Here is the song_fields file:
<fieldset>
<%= f.select :field_type, %w[text_field check_box] %>
<%= f.text_field :artist, placeholder: "Artist" %>
</fieldset>
Here is the ApplicationHelper file :
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
Finally, here is my events coffee script file;
$(document).on 'click', 'form .add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
Sorry for the lengthy post, an answer to my question would be GREATLY appreciated. Let me know if there is anything else you need from me(note I am using rails 5). Cheers :)
Should be
f.fields_for :songs do |builder|
I'm completely lost as to how to make this request go through ajax without throwing back a 406 error. I am trying to add an item to a cart with ajax, and the item does get added and after a refresh the cart is updated. All I receive as an xhr notice with the 406 error. Any help is highly appreciated.
Product Controller
class ProductsController < ApplicationController
def index
#products = Shoppe::Product.root.ordered.includes(:product_categories, :variants)
#products = #products.group_by(&:product_category)
end
def show
#product = Shoppe::Product.root.find_by_permalink(params[:permalink])
end
def buy
#product = Shoppe::Product.root.find_by_permalink!(params[:permalink]) || params[:variant] ? Shoppe::Product.root.find_by_permalink!(params[:permalink]).variants.find(params[:variant].to_i) : #product
current_order.order_items.add_item(#product, 1)
respond_to do |format|
format.js
end
end
end
Show.html.erb
<h2><%= #product.name %></h2>
<% if #product.default_image %>
<%= image_tag #product.default_image.path, :width => '200px', :style => "float:right" %>
<% end %>
<p><%= #product.short_description %></p>
<p>
<% if #product.has_variants? %>
<% #product.variants.each do |v| %>
<%= form_tag product_path(#product.permalink, #product.permalink, :variant => v.id ),id: '#submit-form', remote: true do %>
<%= submit_tag 'Add to basket', :disabled => !v.in_stock? %>
<% end %>
<% end %>
<% else %>
<b><%= number_to_currency #product.price %></b>
<%= link_to "Add to basket", product_path(#product.permalink), :method => :post %>
<% end %>
</p>
<hr>
<%= simple_format #product.description %>
<hr>
<p><%= link_to "Back to list", root_path %></p>
routes.rb
Rails.application.routes.draw do
mount Shoppe::Engine => "/admin"
get "product/:permalink", to: "products#show", as: "product"
post "product/:permalink", to: "products#buy", as: "buy"
get "basket", to: "orders#show"
delete "basket", to: "orders#destroy"
root to: "products#index"
end
products.js.erb
$(document).ready(function() {
var data = $('#submit-form').attr('action')
$.ajax({
type: 'POST',
url: data
});
});
Looking up what format.js does, I found this: rails respond_to format.js API
It seems format.js in your buy action will render the buy.js file. You haven't shown this file, so I'm figuring it doesn't exist.
I'm figuring what you're trying to do is render json, which is simple enough. You could replace:
respond_to do |format|
format.js
end
with this:
render json: <your object>.to_json
make sure to strip any private data before sending the response.
By the way, you should probably be attaching a callback to your $.ajax call.
I'm doing a form_for a reviews section nested under a recipes#show page and doing this with ajax requests. I'm getting the ajax POST method as I intended too and it's showing on the page just fine, however the problem is it's not saving to the page after I do a page reload.
Here is my code:
reviews_controller.rb
class ReviewsController < ApplicationController
respond_to :html, :js
def create
#recipe = Recipe.find(params[:recipe_id])
#review = #recipe.reviews.new(reviews_params)
#review.chef = current_user
#new_review = Review.new
if #review.save
flash[:success] = "Review was saved"
else
flash[:danger] = "Review failed to save, please try again"
end
end
private
def reviews_params
params.require(:review).permit(:body)
end
end
recipes_controller.rb
class RecipesController < ApplicationController
before_action :set_recipe_find, only: [:edit, :update, :show, :like]
before_action :require_user_like, only: [:like]
before_action :admin_to_recipes, only: [:destroy]
before_action :require_same_user_to_recipes, expect: [:show, :index, :like]
before_action :require_same_user_to_recipes, only: [:edit, :update]
def index
#recipes = Recipe.paginate(page: params[:page], per_page: 4)
end
def show
#review = #recipe.reviews.build
end
def create
#recipe = Recipe.new(recipe_params)
#recipe.chef = current_user
if #recipe.save
flash[:success] = "Your recipe was created successfully!"
redirect_to recipes_path
else
render :new
end
end
def edit
end
def update
if #recipe.update(recipe_params)
flash[:success] = "Your recipe was updated successfully!"
redirect_to recipes_path
else
render :edit
end
end
def new
#recipe = Recipe.new
end
def like
like = Like.create(like: params[:like], chef: current_user, recipe: #recipe)
if like.valid?
flash[:success] = "Your selection was successful"
redirect_to :back
else
flash[:danger] = "You can only like/dislike a recipe once"
redirect_to :back
end
end
def destroy
Recipe.find(params[:id]).destroy
flash[:success] = "The Recipe was deleted successfully"
redirect_to recipes_path
end
private
def recipe_params
params.require(:recipe).permit(:name, :summary, :description, :picure, style_ids: [], ingredient_ids: [])
end
def require_same_user_to_recipes
if current_user != #recipe.chef and !current_user.admin?
flash[:danger] = "You can only edit/update your own recipes"
redirect_to root_path
end
end
def set_recipe_find
#recipe = Recipe.find(params[:id])
end
def admin_to_recipes
redirect_to recipes_path unless current_user.admin?
end
def require_user_like
if !logged_in?
flash[:danger] = "You must be logged in"
redirect_to :back
end
end
end
Here is my reviews form partial
<h4> Create a Review </h4>
<div class = "row">
<div class="well col-md-8 col-md-offset">
<%= form_for [#recipe, Review.new], remote: true do |f| %>
<%= f.label :body %>
<%= f.text_field :body, rows: 10%>
<%= f.submit "Create Review", class: "btn btn-success" %>
<% end %>
</div>
</div>
and my javascript code:
create.js.erb
<% if #review.valid? %>
$('.js-reviews').append("<%= escape_javascript(render(#review)) %>");
$('.new-review').html("<%= escape_javascript(render partial: 'reviews/form', locals: { recipe: #recipe, review: #new_review }) %>");
<% else %>
$('.new-review').html("<%= escape_javascript(render partial: 'reviews/form', locals: { recipe: #recipe, review: #review }) %>");
<% end %>
UPDATE
review partial for entries
<div class = "row review-row">
<div class = "media-left">
<%= link_to gravatar_for(#recipe.chef, size: 35), chef_path(#recipe.chef) %>
<small><%= #recipe.chef.name %></small>
</div>
<div class ="media-body review-words">
<h4> <%= #review.body %> </h4>
</div>
</div>
<br>
<hr>
section in show.html.erb showing the html rendering the partial
<div class ="row">
<br>
<h4 class= "review-heading"> Reviews </h4>
<hr>
<div class = 'col-md-12'>
<div class='js-reviews'>
<%= render 'reviews/review'%>
</div>
</div>
</div>
<% if logged_in? %>
<div class='new-review'>
<%= render 'reviews/form', recipe: #recipe, review: #review %>
</div>
<% else %>
<p> <%= link_to "Log in", login_path %> or <%= link_to "create an account", register_path %> to create an review! </p>
<% end %>
DB entry
Started POST "/recipes/6/reviews" for ::1 at 2016-06-02 16:08:55 -0400
Processing by ReviewsController#create as JS
Parameters: {"utf8"=>"✓", "review"=>{"body"=>"review example"}, "commit"=>"Create Review", "recipe_id"=>"6"}
Recipe Load (0.2ms) SELECT "recipes".* FROM "recipes" WHERE "recipes"."id" = ? ORDER BY "recipes"."updated_at" DESC LIMIT 1 [["id", 6]]
Chef Load (0.1ms) SELECT "chefs".* FROM "chefs" WHERE "chefs"."id" = ? LIMIT 1 [["id", 6]]
(0.1ms) begin transaction
SQL (27.3ms) INSERT INTO "reviews" ("body", "recipe_id", "chef_id", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["body", "review example"], ["recipe_id", 6], ["chef_id", 6], ["created_at", "2016-06-02 20:08:55.316206"], ["updated_at", "2016-06-02 20:08:55.316206"]]
(1.4ms) commit transaction
Chef Load (0.1ms) SELECT "chefs".* FROM "chefs" WHERE "chefs"."id" = ? LIMIT 1 [["id", 6]]
Rendered reviews/_review.html.erb (1.7ms)
Rendered reviews/_form.html.erb (1.7ms)
Rendered reviews/create.js.erb (30.4ms)
Completed 200 OK in 110ms (Views: 73.4ms | ActiveRecord: 29.2ms)
I am in the early stages of creating an app, and am just putting some basic code in place. Here is the current code...
app/views/cards/front.html.erb
<%= form_for(front_of_card_path) do |f| %>
<%= f.fields_for :competency_templates do |builder| %>
<%= render 'add_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add New Tag", f, :skill %>
<% end %>
routes
controller :cards do
get '/front', action: 'front', as: 'front_of_card'
post '/save', action: 'create', as: 'save_card'
get '/my_contact_info', action: 'back', as: 'back_of_card'
put '/save', action: 'update', as: 'save_card'
get '/my_card', action: 'show', as: 'card'
end
controller
def create
#skill= Skill.new(params[:skill])
#tag = Tag.new(params[:tag])
#tag.save
#skill.tag_id = #tag.id
#skill.save
redirect_to front_of_card_path, notice: 'Skill was successfully created.'
#get user/session
#save skills & tags
end
cards.js.coffee
jQuery ->
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
app_helper
module ApplicationHelper
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
end
end
So right now this code gives me two text fields. One for the a tag name and another for a tag weight, and the controller inserts everything in the DB. I would like use some javascript to dynamically add as many of these tag/weight fields as I like. Everything I've found seems to focus on nested attributes. Any ideas appreciated.
Update
Added more code to flesh this out. The issue I am having is the 3rd variable I am passing in on this line...
<%= link_to_add_fields "Add New Tag", f, :skill %>
It does not like ':skill', but I am not sure what I should be passing here.
So here is what I came up with...here are my two models...
class Skill < ActiveRecord::Base
belongs_to :tag
attr_accessible :tag_id, :weight
end
class Tag < ActiveRecord::Base
has_many :skills
attr_accessible :name
end
I'm calling a partial from app/views/skills/_form.html.erb and using a js tag to add new fields. Also note that I am re-rendering the partial, then hiding it in the last div tag.
<div id="skillSet">
<%= render partial: "skills_form" %>
</div>
Add New Tag
<div class="actions">
<%= f.submit %>
</div>
<% end %>
<div class="hide" id="new_skills_form">
<%= render partial: "skills_form", locals: {skill: false} %>
</div>
The partial is pretty simple. All I am doing here is storing the values in an array...
<div class="skillsForm">
<%= label_tag 'tag' %>
<%= text_field_tag 'tags[]' %>
<%= label_tag 'weight' %>
<%= text_field_tag 'weights[]' %>
</div>
...here is the javascript...real straight forward, just say when #addNewTag is clicked, appeand #new_skills_form to #skillSet
$(document).ready(function(){
$("#addNewTag").click(function(){
$("#skillSet").append($("#new_skills_form").html());
});
});
...and finally the controller action decontructs the arrays, and saves them...
def create
#skill = Skill.new(params[:skill])
tags = params[:tags]
weights = params[:weights]
tags.each_with_index do |tag, index|
tag = Tag.create :name => tag
Skill.create :tag_id => tag.id, :weight => weights[index]
end
end