I'm creating a simple comment form using React.js inside my Rails application. When I submit a comment, I'm unable to render the author's name with the comment.
TypeError: comment.user is undefined
React.createElement(Comment, {username: comment.user.name, body: comment.body key: comment.id}
commentlist.jsx.erb
var CommentList = React.createClass({
render: function () {
var commentNodes = this.props.comments.map(function ( comment ) {
return <Comment username={ comment.user.name } body={ comment.body } key={ comment.id } />
});
return (
<div className="comment-list">
{ commentNodes }
</div>
)
}
});
commentscontroller.erb
def index
#presenter = {
:comments => Comment.last(5),
:form => {
:action => comments_path,
:csrf_param => request_forgery_protection_token,
:csrf_token => form_authenticity_token
}
}
end
def create
#comment = Comment.new(comment_params)
#comment.commentable = #event
#comment.body = comment_params[:body]
#comment.user_id = current_user.id
#comment.save
if request.xhr?
render :json => Comment.last(5)
else
redirect_to comments_path
end
end
comments/index.html.erb
<%= react_component('CommentBox',
{:presenter => #presenter.to_json(:include => :user)},
{:prerender => true}) %>
Anyone know how I can fix this? Just let me know if I'm missing any code samples.
UPDATE
This doesn't answer my question, but it's a solution. I added an author method to the comment model that returns the commenter's name, so I can now access the name directly from the comment.
def author
User.find(self.user_id).name
end
Though I'd still like to know how to access through an association.
def as_json
{:author => self.user.name}
end
And wherever you are rendering json object just call Comment.last.as_json
Refer http://jonathanjulian.com/2010/04/rails-to_json-or-as_json/
Related
UPDATE: The answer bellow is correct. Just wanted to update what I did to solve the problem.
First I had to delete all my previous lines in the rails console.
Then I used the bye bug gem in my lines controller at the bottom of the create method to discover where the next bug occurred. I created a test line that I needed to delete again. so I ran
Line.last.delete in console.
This is the way my lines controller create method looks now (working no bugs)
def create
if user_signed_in?
#line = Line.create(line_params)
if #line
if params[:line][:previous_line_id].empty?
#line.story = Story.create
#line.save
else
#line.story = #line.previous_line.story
#line.save
end
redirect_to line_path(#line)
else
flash[:error] = #line.errors
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
end
else
Finally I ran #Lines.each { |line| line.update.attribute(:story_id: 3)}
This gave the necessary association between lines and story.
ORIGINAL POST BELLOW.
I'm getting this error in my rails app. I think that when I create a new line or start a story, it doesn't automatically add it to a story object. I've listed my show.html.erb file as well as my lines controller.rb file.
What am I missing? How do I get the controller to add data to the story object correctly?
Thanks!
I added a few lines of code to my lines controller:
class LinesController < ApplicationController
def new
params[:previous_line_id].nil? ? #line = Line.new : #line = Line.find(params[:previous_line_id]).next_lines.create
#lines = #line.collect_lines
#ajax = true if params[:ajax]
render :layout => false if params[:ajax]
if #line.previous_line
#line.update_attribute(:story_id, #line.previous_line.story.id)
else
story = Story.create
#line.story = story
#line.save
end
end
def create
if user_signed_in?
#line = Line.create(line_params)
if #line
redirect_to line_path(#line)
else
flash[:error] = #line.errors
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
end
else
flash[:error] = "Please sign in or register before creating a line!"
unless params[:line][:previous_line_id].empty?
redirect_to line_path(Line.find(params[:line][:previous_line_id]))
else
redirect_to root_path
end
end
end
# params[:id] should correspond to the first line of the story.
# if params[:deeper_line_id] is not nil, that means that they want to render up to the nested line id
def show
#lines = Line.find(params[:id]).collect_lines
#next_lines = #lines.last.next_lines.ranked
#lines.last.update_attribute(:score, #lines.last.score + 1)
end
def select_next
#line = Line.find(params[:id])
#line.update_attribute(:score, #line.score + 1)
#lines = [#line]
#next_lines = #line.next_lines.ranked
render :layout => false
end
def send_invite
if user_signed_in?
UserInvite.send_invite_email(current_user,Line.find(params[:id]), params[:email]).deliver
flash[:notice] = "Your invite was sent!"
else
flash[:error] = "Please sign in"
end
redirect_to Line.find(params[:id])
end
private
def line_params
params.require(:line).permit(:text, :previous_line_id, :user_id)
end
end
I added these lines to the controller pictured above
if #line.previous_line
#line.update_attribute(:story_id, #line.previous_line.story.id)
else
story = Story.create
#line.story = story
#line.save
end
Here is my show.html.erb file
<div class="row">
<div class="col-lg-2">
</div>
<div class="box-container col-lg-7 ">
<div id="story" class="box">
<% #lines.each do |line| %>
<span class="story-line" data-id="<%=line.id%>"><%= link_to line.text, '#', :class=>"story-line" %></span>
<% end %>
</div>
<div id="next-steps">
<%= render 'next_steps' %>
</div>
<span style="font-size:.9em; margin-bottom:15px; display:block;">*If the links don't work, try refreshing.</span>
</div>
<div class="col-lg-2" style="padding-right:25px;">
<%= render 'invite' %>
Your Fellow Collaborators: <br />
<div class="collaborators">
<% #lines.last.story.collaborators.uniq.each do |collaborator| %>
<%= link_to profile_path(:id => collaborator.id) do %>
<%= image_tag collaborator.profile_image_uri, :class => "prof-icon" %>
<% end %>
<% end %>
</div>
Story model
class Story < ActiveRecord::Base
has_many :lines
has_and_belongs_to_many :collaborators, :class_name => "User", :join_table => "collaborators_stories", :association_foreign_key => :collaborator_id
def first_line
self.lines.first_lines.first_lines.first
end
end
Here is my lines.rb file
class Line < ActiveRecord::Base
scope :first_lines, -> { where previous_line_id: nil}
scope :ranked, -> { order("score + depth DESC")}
belongs_to :user
belongs_to :story
belongs_to :previous_line, :class_name => "Line", :foreign_key => "previous_line_id"
has_many :next_lines, :class_name => "Line", :foreign_key => "previous_line_id"
validates_presence_of :text
after_create :update_depths
def update_depths
line = self.previous_line
while !line.nil?
line.update_attribute(:depth, line.depth + 1)
line = line.previous_line
end
end
def first_line
line = self
while !line.previous_line.nil?
line = line.previous_line
end
line
end
def collect_lines
line = self
lines = [self]
while !line.previous_line.nil?
lines.unshift(line.previous_line)
line = line.previous_line
end
lines
end
end
Problem is orphaned lines in your database. Look for them and associate it to a story, or delete it:
How to find orphaned records:
http://antonzolotov.com/2013/01/26/how-to-find-and-delete-orphaned-records-with-ruby-on-rails.html
Then review the create method to ensure a line should be part of a story:
#short example review activerecord relations
#story = Story.find(params[:story_id])
story.lines.create(line_params)
That should work.
EDIT:
def self.find_orphan_ids
Lines.where([ "user_id NOT IN (?) OR story_id NOT IN (?)", User.pluck("id"), Story.pluck("id") ]).destroy_all
end
As a new to Ruby on rails, I stumble on a part of my app. I read the basics of RoR framework and know the 'convention over configuration' feature of rails MVC. I have two tables, one is apps_events and another is apps_events_attributes. The id of the first one is the foreign key of the second and in has many relationship. The app_events table has a field of foreign key attribute 'app_id', so selecting on a particular app I will be redirected to its events and attributes. There is also a field called 'is_standard' which actually distinguish the event type whether it's a Standard or Custom event.
Now I have to render those events and its attributes of a particular app in two different tab on the view layer with it's attributes using nested_form_for feature. User can toggle to Standard and Custom event through this tab click. Can anyone suggest me how will I achieve the same and can show me the ideal flow of this scenario (model name and checking part of 'is_standard', propagate the same in controller and render to the view)?
By the way, can I use different controller over the same model and if I do the same then is it capable of doing the same CRUD functionality for different Event and its attributes?
I have all it done alone and it is not very hard what I think at first, all trick is done by JQuery... and share the concept if somebody got same problem as me
My Models are
class AppsEvent < ActiveRecord::Base
belongs_to :app
has_many :apps_events_attributes, :foreign_key => 'apps_event_id',
:class_name => 'AppsEventsAttribute',
:dependent => :destroy
accepts_nested_attributes_for :apps_events_attributes,
:reject_if => lambda { |a| (a[:name].blank? && a[:description].blank?) },
:allow_destroy => true
validates_presence_of :description
validates_presence_of :name
validates_presence_of :code
validates_presence_of :apps_events_attributes, :message => "can't be empty"
end
and
class AppsEventsAttribute < ActiveRecord::Base
set_table_name :apps_events_attributes
belongs_to :apps_event, :foreign_key => 'apps_event_id'
attr_accessible :id, :apps_event_id, :name, :description, :attribute_type, :is_std, :created_at, :updated_at
def type
self.attribute_type
end
end
and My Controller is...
class AppsEventsController < ApplicationController
layout :layout_by_resource
before_filter :initialize_default_app
before_filter :check_permission
before_filter :load
def load
#app = current_user.find_app(params[:app_id])
#apps_events = AppsEvent.where(:app_id => #app.id)
#apps_event = AppsEvent.new
end
def index
#app = current_user.find_app(params[:app_id])
#default_app = App.default(current_user)
#apps_events = AppsEvent.where(:app_id => #app.id)
#apps_event_jsons = Hash.new
#apps_events.each do |app_event|
json = Hash.new
json['User_ID'] = 548741213
json['Session_ID'] = 2568639390
json['Action_Type'] = app_event.code
json['eventsData'] = {}
app_event.apps_events_attributes.each do |apps_event_attributes|
if (apps_event_attributes.attribute_type == 'Integer')
json['eventsData'][apps_event_attributes.name] = 1234
elsif (apps_event_attributes.attribute_type == 'Float')
json['eventsData'][apps_event_attributes.name] = 1234.23
else
json['eventsData'][apps_event_attributes.name] = 'abcd'
end
end
#apps_event_jsons[app_event.id] = json
end
end
def new
#apps_event = AppsEvent.new
#app = current_user.find_app(params[:app_id])
#apps_event.app_id = #app.id
#apps_event.apps_events_attributes.build
#action = 'create'
render 'edit'
end
def edit
#app = current_user.find_app(params[:app_id])
#apps_event = AppsEvent.find(params[:id])
#action = 'update'
end
def create
#apps_event = AppsEvent.new(params[:apps_event])
#show_custom_event = 'true'
#apps_event.name = #apps_event.name.strip
respond_to do |format|
if #apps_event.save
format.html {
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:notice => "Successfully created #{#apps_event.name} custom definition.")
}
format.js {
flash[:notice] = 'Successfully created event.'
#apps_events = AppsEvent.where(:app_id => #app.id)
}
else
#app = current_user.find_app(params[:app_id])
if (#apps_event.apps_events_attributes == nil || #apps_event.apps_events_attributes.size <= 0)
#apps_event.apps_events_attributes.build
end
#apps_event.populate_code
#action = 'create'
format.html {
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:alert => "Error in creating #{#apps_event.name} custom definition.")
}
format.js
end
end
end
def update
#apps_event = AppsEvent.find(params[:apps_event][:id])
params[:apps_event][:name] = params[:apps_event][:name].strip
respond_to do |format|
if #apps_event.update_attributes(params[:apps_event])
format.html {
if(#apps_event.is_std == 'y')
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:notice => "Successfully updated #{#apps_event.name} standard definition.")
else
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:notice => "Successfully updated #{#apps_event.name} custom definition.")
end
}
format.js {
flash[:notice] = 'Successfully updated event.'
#apps_events = AppsEvent.where(:app_id => #app.id)
render :nothing => true
}
else
#app = current_user.find_app(params[:app_id])
if (#apps_event.apps_events_attributes == nil || #apps_event.apps_events_attributes.size <= 0)
#apps_event.apps_events_attributes.build
end
#apps_event.populate_code
#action = "update"
format.html {
if(#apps_event.is_std == 'y')
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:alert => "Error in updating #{#apps_event.name} standard definition.")
else
redirect_to("/app/#{params[:apps_event][:app_id]}/apps_events",
:alert => "Error in updating #{#apps_event.name} custom definition.")
end
}
format.js
end
end
end
def delete
if AppsEvent.delete(params[:id])
redirect_to "/app/#{params[:app_id]}/apps_events", :notice => "Successfully deleted #{#apps_event.name} custom definition."
else
redirect_to "/app/#{params[:app_id]}/apps_events",
:alert => "Error in deleting #{#apps_event.name} custom definition."
end
end
end
and I have 5 view files which are index.html.erb, edit.js.erb, _form_custom.html.erb, _form_standard.html.erb and _events.html.erb beside that have also a helper file for update, create and delete using ajax call by setting remote => true. In index file I am doing partial rendering all events(_events.html.erb) and here I done the trick :P
My _events.html.erb
<% for apps_event in #apps_events %>
<% if (apps_event.is_std == 'y') %>
<div class="standardEvent showStandard">
<ul>
<li class="column_1"><span style="font-weight: bold;"><%= apps_event.name %></span></li>
<li class="column_2"><span><%= apps_event.code %></span></li>
<li class="column_3"><span><%= apps_event.description %></span></li>
<li class="column_4">
<%= link_to edit_apps_event(apps_event) %>
</li>
<li class="column_5">
</li>
</ul>
</div>
<% else %>
<div class="customEvent showCustom" style="display:none">
<ul>
<li class="column_1"><span style="font-weight: bold;"><%= apps_event.name %></span></li>
<li class="column_2"><span><%= apps_event.code %></span></li>
<li class="column_3"><span><%= apps_event.description %></span></li>
<li class="column_4">
<%= link_to edit_apps_event(apps_event) %>
</li>
<li class="column_5">
<%= remove_apps_event_prompt_link(apps_event) %>
</li>
</ul>
</div>
<% end %>
<div class="clrBoth"></div>
<% end %>
now you can figure out the left part mean -- JQuery part to hide or show a div.
I want to save the id of the wishlist in a wishlist_entry table in a column like wishlist_id.
I have an link_to element like this:
<%= link_to 'Create', new_wishlist_entry_path(:wishlist => #wishlist.id) %>
This gives me the following url:
http://localhost:3000/wishlist_entries/new?wishlist=36
The wishlist_entry_controller's new action is:
def new
#wishlist_entry = WishlistEntry.new
#wishlist = Wishlist.find(params[:wishlist])
respond_to do |format|
format.html # new.html.erb
format.json { render json: #wishlist_entry }
end
end
The create action is:
def create
#wishlist_entry = WishlistEntry.new(
:wishlist_id => Wishlist.find(params[:id]),
:entry_name => params[:wishlist_entry][:entry_name],
:description => params[wishlist_entry][:description]
)
I get this error:
Couldn't find Wishlist without an ID
app/controllers/wishlist_entries_controller.rb:44:in `create'
The error is in the link:
:wishlist_id => Wishlist.find(params[:id]),
Whats wrong? How can I save the id of the new wishlist in the field wishlist_id of wishlist_entry?
I think the problem is that the wishlist id is coming in params[:wishlist] not params[:id]. The error you are seeing in your controller is because params[:id] is nil and the Wishlist class doesn't know what to do with Wishlist.find(nil).
Try this:
def create
#wishlist_entry = WishlistEntry.new(
:wishlist_id => params[:wishlist]),
:entry_name => params[:wishlist_entry][:entry_name],
:description => params[wishlist_entry][:description]
)
Without knowing exactly what you're trying to do here, it looks like the problem is that wishlist_id isn't getting passed to your create action.
The best way to fix this is to take advantage of the nested resources you've already set up by changing
<%= link_to 'Create', new_wishlist_entry_path(:wishlist => #wishlist.id) %>
to
<%= link_to 'Create', new_wishlist_wishlist_entry_path(:wishlist => #wishlist.id) %>
(wishlist_wishlist_entry looks pretty bad, but you can change it by using :as => 'whatever' in your routes)
Then in your new action:
#wishlist = Wishlist.find(params[:wishlist_id])
In your form:
form_for([#wishlist, #wishlist_entry])
And in your create action:
#wishlist = Wishlist.find(params[:wishlist_id])
#wishlist_entry = #wishlist.wishlist_entries.build(params[:wishlist_entry])
I'm using an API instead of a database, so I'm not using ActiveRecord but ActiveModel (I mostly did like here: railscasts.com/episodes/219-active-model)
Thing is, when I try to edit an item (in my case a parking), the action of the form still remains the action of the create and not update.
so when I go on /parkings/2/edit to edit a parking, the form is still:
<form accept-charset="UTF-8" action="/parkings" class="form-horizontal" id="new_parking" method="post">
when it should be more like that with the put hidden field and the parkings/2 as the action:
<form accept-charset="UTF-8" action="/parkings/2" class="form-horizontal" id="edit_parking" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" /><input name="_method" type="hidden" value="put" />
Anybody knows where the method & action of the form_for is set according to the route? What I'm trying to do is be as close as if I was using ActiveRecord with a database.
Here is some code :
_form.html.erb
<%= form_for(#parking, :html => { :class => "form-horizontal" }) do |f| %>
...
<% end %>
edit.html.erb & new.html.erb, simply has
<%= render 'form' %>
Controller
class ParkingsController < ApplicationController
def index
#parkings = Parse.get("Parking")
respond_to do |format|
format.html
format.json { render :json => #parking }
end
end
def new
#parking = Parking.new
respond_to do |format|
format.html
format.json { render :json => #parking }
end
end
def edit
#parking = Parking.find(params[:id])
respond_to do |format|
format.html
format.json { render :json => #parking }
end
end
def create
#parking = Parking.new(params[:parking])
if (#parking.save)
flash[:success] = "Parking was just added!"
redirect_to :action => "new"
else
render :action => "new"
end
end
def update
# Testing
parking = Parse.get("Parking", params[:id])
parking.delete("updatedAt")
parking["name"] = params[:parking][:name]
parking.save
redirect_to :action => "index"
end
Model
class Parking
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :address, :city, :longitude, :latitude, :contributor_name, :contributor_email
validates_presence_of :name, :address, :city, :longitude, :latitude
#id = nil
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def self.find(id)
#id = id
raw = Parse.get("Parking", #id.to_s)
parking = Parking.new
parking.name = raw["name"]
parking.address = raw["address"]
parking.city = raw["city"]
parking.longitude = raw["location"]["longitude"]
parking.latitude = raw["location"]["latitude"]
parking.contributor_name = raw["contributorName"]
parking.contributor_email = raw["contributorEmail"]
return parking
end
def save
if (!valid?)
return false
else
parking = Parse::Object.new("Parking")
data =
{
:longitude => longitude.to_f,
:latitude => latitude.to_f
}
point = Parse::GeoPoint.new(data)
parking["location"] = point
parking["name"] = name
parking["address"] = address
parking["city"] = city
parking["contributorName"] = contributor_name
parking["contributorEmail"] = contributor_email
if (parking.save)
return true
end
end
end
def persisted?
false
end
end
Please note that the create is working and if I add the id of my parking in the form action="" using the Web Inspector or Firebug, and add :method => "put" in my form_for, my record successfully update.
The real problem here is really the form_for action & method who doesn't get updated when I'm editing a parking and remains like if I was adding a new one.
I'm still learning Rails, so sorry if some infos aren't clear!
Thank you!
--- SOLUTION ---
persisted? shouldn't only return false, and my model needed to define a method that returns the id of the object (so they can update the action="") so here's is my updated model:
class Parking
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :objectId, :name, :address, :city, :longitude, :latitude, :contributor_name, :contributor_email
validates_presence_of :name, :address, :city, :longitude, :latitude
#id = nil
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def self.find(id)
raw = Parse.get("Parking", id.to_s)
parking = Parking.new
parking.objectId = id
parking.name = raw["name"]
parking.address = raw["address"]
parking.city = raw["city"]
parking.longitude = raw["location"]["longitude"]
parking.latitude = raw["location"]["latitude"]
parking.contributor_name = raw["contributorName"]
parking.contributor_email = raw["contributorEmail"]
return parking
end
def save
if (!valid?)
return false
else
parking = Parse::Object.new("Parking")
data =
{
:longitude => longitude.to_f,
:latitude => latitude.to_f
}
point = Parse::GeoPoint.new(data)
parking["location"] = point
parking["name"] = name
parking["address"] = address
parking["city"] = city
parking["contributorName"] = contributor_name
parking["contributorEmail"] = contributor_email
if (parking.save)
return true
end
end
end
def update_attributes(aParking)
parking = Parse.get("Parking", #id.to_s)
parking.delete("updatedAt")
parking["name"] = aParking["name"]
parking.save
return true
end
def destroy
parking = Parse.get("Parking", #id)
#parking.parse_delete
end
def id
return self.objectId
end
def persisted?
!(self.id.nil?)
end
end
I think your problem is in your model's persisted? method. Since it always returns false, Rails always thinks it's building a form for a newly created record, so it uses POST and submits to the collection URL.
You need some sort of logic in that method so that existing records return true and new records return false.
Hi friend you can to tell the form builder which method to use.So try
<%= form_for(#parking, :method => ["new", "create"].include?(action_name) ? :post : :put,
:html => { :class => "form-horizontal" }) do |f| %>
...
<% end %>
If you are not using ActiveRecord you should use 'form_tag' instead 'form_for'
I have a view which contain multiple links:
<% a.each do |q| %>
<%= link_to "stock it",
{ :action => "stock",
:qid => q.question_id,
:qur => q.question_answers_url,
:qti => q.title } ,
:remote => true %>
<div id="<%= "stock" + q.question_id.to_s %>"></div>
<% end %>
Each link generate AJAX-request. Here is a controller:
def stock
if(!Later.where(:question_id => params[:qid]).exists?)
later = Later.new(:question_id => params[:qid], :name => params[:qti], :url => params[:qur])
later.save
end
respond_to do |format|
format.js { render :layout=>false }
end
end
Now return to the view. Each link has a 'div' with unique id='stock'. When user press the link I need to add text to specific div with corresponding id.
I have a stock.js.erb file:
$("#stock<number>").html("some text");
How can I pass div-id to stock.js.erb and how can I use it ?
Common use is to add object.id to your DOM id. That what you exactly did:
<div id="<%= "stock_#{q.question_id}" %>"></div>
Then in your controller you shoud define your question_id or your exact question:
def stock
if(!Later.where(:question_id => params[:qid]).exists?)
later = Later.new(:question_id => params[:qid], :name => params[:qti], :url => params[:qur])
later.save
end
#question_id = params[:qid]
end
Now it will be shared with your stock.js.erb file:
$("#stock_<%= #question_id %>").html("some text");