Error with Nested Model Edit page in Rails - ruby-on-rails

I'm tying to perform an edit action on one of my nested models. I keep getting the following error "undefined method `model_name' for NilClass:Class"
I don't know what's going on. Any help?
Routes
resources :users, :except => [ :create, :new ] do
resources :store do
get "inbox", :on => :collection
get "openedmessages", :on => :collection
end
end
Store Actions
def edit
#store = current_user.store.id
end
def create
#store = current_user.store.new(params[:store])
end
def update
#stores = current_user.store.id
if #stores.update_attributes
flash[:success] = "Store has been updated"
render 'edit'
else
render 'edit'
end
end
View for Edit Action in Store
<ul class="storedashboard_header">
<li><%= link_to "Manage Gear", user_store_index_path(current_user) %></li>
<li><%= link_to "Store Appearance", edit_user_store_path(current_user, #store) %></li>
<li><%= link_to "Announcements", '#' %></li>
</ul>
<%= render "users/userdashboard_header" %>
<%= render "store/storemenu" %>
<div style="clear:both"></div>
<% flash.each do |name, msg| %>
<div class="alert alert-success">
<a class="close" data-dismiss="alert">×</a>
<%= msg %>
</div>
<% end %>
<div>
<%= simple_form_for #stores, :html => {:multipart => true, :class => 'form-horizontal'} do |f| %>
</br>
<div style="float: left;">
<%= f.input :storename %>
<%= f.input :storeimage %>
<%= f.input :storelogo %>
<%= f.button :submit, class: 'btn btn-large', style: 'margin-left: 40px;' %>
</div>
<% end %>
</div>
UPDATED
Changed my Controller to the following:
def edit
#store = Store.find(current_user.store.id)
end
def create
#store = current_user.store.new(params[:store])
end
def update
#store = Store.find(current_user.store.id)
if #store.update_attributes(params[:store])
flash[:success] = "Store has been updated"
render 'edit'
else
render 'edit'
end
end
New Error after Update
undefined method `store_path' for #<#:0x007fec93b97238>
FINAL FIX
Needed to fix the view...
<%= simple_form_for [current_user, #store], :html => {:multipart => true, :class => 'form-horizontal'} do |f| %>

if i understand your question properly:
#store needs to be an object for the model Store instead of just the id.
something like:
#store = Store.find(current_user.store.id) will return the object

Related

more rails partials on the same page

I have a rails app. I'm displaying more partials (user, task, conversation, message) from different classes on the users/:id/show page. I set all the instance variables (for other classes as well) in the users.controller's def show action.
It seems to be a bit heavy, so is there a better approach than this? (I'm using #task and #message for the AJAX calls.)
def show
#user = User.find(params[:id])
if Task.between(current_user.id, #user.id).present?
#tasks = Task.uncompleted.between(current_user.id, #user.id).order("created_at DESC").includes(:assigner).paginate(page: params[:page], per_page: 12)
#task = Task.new
if Conversation.between(current_user.id, #user.id).present?
#conversation = Conversation.between(current_user.id, #user.id).first
#messages = #conversation.messages.includes(:user)
#message = Message.new
respond_to do |format|
format.html
format.js { render :template => "tasks/update.js.erb", :template => "tasks/destroy.js.erb", layout: false }
end
end
else
redirect_to user_profile_path(#user)
end
end
UPDATED:
users/show:
<%if #conversation%>
<%= render 'conversations/show' %>
<% end %>
<tbody class="newtaskinsert2">
<%= render partial: "tasks/task_between", collection: #tasks, as: :task %>
</tbody>
conversations/_show:
<div class="chatboxcontent">
<% if #messages.any? %>
<%= render #messages %>
<% end %>
</div>
<div class="chatboxinput">
<%= form_for([#conversation, #message], :remote => true, :html => {id: "conversation_form_#{#conversation.id}"}) do |f| %>
<%= f.text_area :body, class: "chatboxtextarea", "data-cid" => #conversation.id %>
<% end %>
<%= form_for([#conversation, #message], html: {class: "refile_form"}, remote: true) do |form| %>
<span class="btn btn-success btn-sm btn-file">Choose file
<%= form.attachment_field :message_attachment, direct: true, presigned: true, class: "choosefile" %></span>
<%= form.submit "Send File", class: "btn btn-primary btn-sm btn-submit-refile", style:"display:none"%>
<% end %>
<span id="progresspercent"></span>
</div>
You can keep only #user instance variable in the controller, and in partials use: #user.tasks instead of #tasks, #user.tasks.new instead of #task etc. Note also, that you can pass parameters to partials (3.4.4 Passing Local Variables)
For sample:
<%= render partial: "your_partial", locals: {tasks: #user.tasks} %>
Update:
With your way (call methods from class instead of objects) you can do something like it:
def show
#user = User.find(params[:id])
if Task.between(current_user.id, #user.id).present?
# #user.tasks.where(another_user_field_name: current_user).present? - looks more like Rails way
#tasks = Task.uncompleted.between(current_user.id, #user.id).order("created_at DESC").includes(:assigner).paginate(page: params[:page], per_page: 12)
#conversation = Conversation.between(current_user.id, #user.id).first
if #conversation
respond_to do |format|
format.html
format.js { render :template => "tasks/update.js.erb", :template => "tasks/destroy.js.erb", layout: false }
end
end
# Do not forget that if #conversation is not exists this code render views by default way
else
redirect_to user_profile_path(#user)
end
end
<%= render 'conversations/show' %>
<tbody class="newtaskinsert2">
<%= render partial: "tasks/task_between"%>
</tbody>
<%if #conversation%>
<div class="chatboxcontent">
<%= render '_your_messages_partial', locals: {messages: #conversation.messages.includes(:user)}%>
</div>
<div class="chatboxinput">
<%= form_for([#conversation, #conversation.messages.new], :remote => true, :html => {id: "conversation_form_#{#conversation.id}"}) do |f| %>
<%= f.text_area :body, class: "chatboxtextarea", "data-cid" => #conversation.id %>
<% end %>
<%= form_for([#conversation, #conversation.messages.new], html: {class: "refile_form"}, remote: true) do |form| %>
<span class="btn btn-success btn-sm btn-file">Choose file
<%= form.attachment_field :message_attachment, direct: true, presigned: true, class: "choosefile" %></span>
<%= form.submit "Send File", class: "btn btn-primary btn-sm btn-submit-refile", style:"display:none"%>
<% end %>
<span id="progresspercent"></span>
</div>
<% end %>
You can make this code more shorter in case of using relations (#user.conversations instead of Conversation.between... etc)
I would suggest to use caching techniques for your views:
http://edgeguides.rubyonrails.org/caching_with_rails.html#fragment-caching
http://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching
In the extreme scenario where performance is still an issue for you, I would recommend to start denormalising the partials until you re happy with the performance.

"undefined method `errors' for nil:NilClass" when calling on errors method

I am currently teaching myself some RoR and doing the tutorial, but adding some nicer layout and stuff with bootstrap and I am running into a problem which I cant figure out.
I am trying to do the validation part (http://guides.rubyonrails.org/getting_started.html#adding-some-validation), but when I use:
<% #post.errors.any? %>
I get this message:
undefined method `errors' for nil:NilClass
Extracted source (around line #9):
<legend><h1>Add Post</h1></legend>
<%= form_for :post, url: posts_path, html: {class: 'form-horizontal'} do |f| %>
<% if #post.errors.any? %>
<div id="errorExplanation">
Nothing works and I even copied and pasted the parts from the tutorial.
Here is the code for the view:
<p> </p>
<div class="span6"
<fieldset>
<legend><h1>Add Post</h1></legend>
<%= form_for :post, url: posts_path, html: {class: 'form-horizontal'} do |f| %>
<% if #post.errors.any? %>
<div id="errorExplanation">
<h2><%= pluralize(#post.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="control-group">
<%= f.label :title, :class => 'control-label' %>
<div class="controls">
<%= f.text_field :title, :class => 'span4' %>
</div>
</div>
<div class="control-group">
<%= f.label :content, :class => 'control-label' %>
<div class="controls">
<%= f.text_area :content, :rows => '7', :class => 'input-block-level' %>
</div>
</div>
<div class="form-actions">
<%= f.submit "Add Post", :class => 'btn btn-success' %>
<%= link_to "Cancel", posts_path, :class => 'btn', :style => 'float:right;' %>
</div>
<% end %>
</fieldset>
</div>
And my posts_controller:
class PostsController < ApplicationController
def new
end
def create
#post = Post.new(params[:post].permit(:title, :content))
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
def index
#posts = Post.order("created_at desc")
end
private
def post_params
params.require(:post).permit(:title, :content)
end
end
What am I missing? Thanks in advance!
You need to define #post in your new action too.
def new
#post = Post.new
end
You're getting the NilClass error because #post has no value (it's nil) when you first load the form on the new action.
When you do the render :new in your create action there is no problem because it's using the #post you've defined at the top of create.
Update the create method in posts.controller.rb file with the below piece of code. It worked for me.
def create
#post = Post.new(params[:post].permit(:title, :text))
#post.save
redirect_to #post
end
In your posts_controller, add this :
def new
#post = Post.new
end
If you have made the changes to edit/create etc. and it still gives you ActionView::Template::Error (undefined method `errors' for nil:NilClass):
Try restarting rails server
I kept receiving the error even after correcting the files in articles_controller.rb
and still got the error. It was fixed after i restarted rails server.

How do I re-populate form fields when validation fails?

This is the erb template:
<div id='recipe-form'>
<% if #recipe.errors %>
<div id='errors'>
<% #recipe.errors.messages.each do |field, messages| %>
<div class='error'>
<div class=field'><%= field %></div>
<div class='messages'>
<ul>
<% messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
</div>
<% end %>
<%= form_for #recipe, :html => {:multipart => true}, :url => '/recipes' do |f| %>
<%= f.label :title, 'title' %>
<%= f.text_field :title %>
<div id="photo-upload">
<%= file_field :photo0, :image, :id => 0 %>
</div>
<div id='existing-photos'>
<% recipe.photos.each do |photo| %>
<div id='<%= photo.id %>'>
<img src='<%= photo.image.url(:thumb) %>' />
<ul>
<li>
<%= link_to 'delete',
recipe_photo_url(
:recipe_id => #recipe.slug,
:id => photo.id
),
:method => :delete,
:remote => true
%>
</li>
</ul>
</div>
<% end %>
</div>
<%= f.label :body, 'body' %>
<%= f.cktext_area :body, :ckeditor => {:width => "500"} %>
<%= f.label :tags, 'tags (comma separated)' %>
<%= text_field_tag :tags %>
<%= submit_tag 'submit' %>
<% end %>
</div>
This is the create action:
def create
#recipe = Recipe.new(params[:recipe])
photo_keys = params.keys.select{|k|k.match(/^photo/)}
#photos = []
photo_keys.each do |photo_key|
#photos << Photo.new(params[photo_key])
end
#recipe.tags = Tag.parse(params[:tags])
#recipe.author = current_user
if #recipe.save &&
#photos.all?{|photo|photo.save}
#photos.each do |photo|
photo.recipe_id = #recipe.id
photo.save
end
flash[:notice] = 'Recipe was successfully created.'
redirect_to recipe_url(#recipe.slug)
else
flash[:error] = 'Could not create recipe. '
flash[:error] += 'Please correct any mistakes below.'
render :action => :new
end
end
And this is the new action:
def new
#recipe = Recipe.new
end
I read that if I use form_for as I am using above, the fields will be re-populated automatically.
When I inspect #recipe.errors from within the erb template, I can see that the errors generated by create are also available when the new action is rendered, but the fields do not re-populate.
I'm actually not sure about what render action: does but what I do and works is: Instead of rendering the action just render the template using render :new.
You need to set the same instance variables (those with #), which you already in your create action.

_error_messages does not work for forms

I am working on a project that a user can create a post and others can send posts about that topic.My resources file is :
resources :users
resources :sessions, only: [:new, :create, :destroy]
resources :topics , only: [ :show, :create, :destroy] do
resources :posts, only: [:create, :new]
My topics_form.html.erb:
<%= form_for(#topic) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :title, placeholder: "yeni başlık girin..." %>
</div>
<%= f.submit "Gönder", class: "btn btn-large btn-primary" %>
<% end %>
my create action is :
def create
#topic = current_user.topics.build(params[:topic])
if #topic.save
flash[:success] = "Konu oluşturuldu!"
redirect_to root_path
else
render 'static_pages/home'
end
end
My posts_form.html.erb is :
<%= form_for [#topic, #post] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "yorumunuzu girin..." %>
</div>
<%= f.submit "Gönder", class: "btn btn-large btn-primary" %>
<% end %>
my post_controller create action is :
def create
#topic= Topic.find(params[:topic_id])
#post = #topic.posts.build(params[:post])
#post.user = current_user
#post.topic_id = #topic.id
if #post.save
flash[:success] = "Yorum oluşturuldu!"
redirect_to topic_path(#topic)
else
render 'static_pages/home'
end
end
This is my error_messages.html.erb:
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li>* <%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
If I test the empty post and empty topic. I get this error :
undefined method `each' for nil:NilClass
Extracted source (around line #2):
1:
2: <% #topics.each do |topic|%>
3: <li><%=link_to topic.title, topic_path(topic) %></li>
4:
5: <%= will_paginate #topics %>
My static_pages_controller.rb :
def home
if signed_in?
#topic = current_user.topics.build if signed_in?
end
#topics = Topic.paginate :page => params[:page], :per_page => 20
end
and my home.html.erb:
<% if signed_in? %>
<div class="row">
<%= render 'shared/user_info' %>
<%= render 'shared/topic_form' %>
<ol class="topics-signedin">
<%= render 'shared/topics' %>
</ol>
Why does not errors does not show ?
You are missing #topics in your 'create' action. Yes, it gets set in your 'home', which rendered the form, but on submission, it passed to 'create', which then has to load the variables again, and in your case, there is no #topics set when it goes to render 'static_pages/home'. You need..
...
if #post.save
flash[:success] = "Yorum oluşturuldu!"
redirect_to topic_path(#topic)
else
#topics = Topic.paginate :page => params[:page], :per_page => 20
render 'static_pages/home'
end
You never set #topics, only #topic. So #topics is nil.
And on error you render the static_pages/home, which requires those. You should re-render the new or edit instead.

rails ignores my redirect

Rails is ignoring my redirect.
Use Case:
user navigates to his account and selects the option to display his subscription status
app lists his current subscription and the option to cancel it
user cancels the subscription
app updates the subscription record and should redirect the user back to the subscriptions action
-> but rails ignores this last step... any ideas what I am doing wrong?
Routes
map.resources :users, ..., :member => { ..., :subscriptions => :get, :subscribe => :post, :unsubscribe => :put}
Controllers:
def subscriptions
#tradesman = User.find_by_id(params[:id])
#subscription = #tradesman.current_subscription || Subscription.new
#all_subscriptions = Subscription.find(:all)
end
def subscribe
#tradesman = User.find_by_id(params[:id])
#subscription = current_user.subscriptions.build(params[:subscription])
#subscription.update_attributes(:started_at => Time.zone.now)
#subscription.save
redirect_to :action => 'subscriptions', :id => #tradesman.id
end
def unsubscribe
#tradesman = User.find_by_id(params[:id])
#subscription = #tradesman.current_subscription
#subscription.update_attributes(:ended_at => Time.zone.now)
#subscription.save
redirect_to :action => 'subscriptions', :id => #tradesman.id
end
View:
<div class = 'wrapper'>
<%= render :partial => "my_account_leftbar" %>
<% form_for #subscription, :url => subscribe_user_path(current_user) do |f| %>
<% #all_subscriptions.each do |subscription| %>
<div class="field">Start: <%= subscription.started_at %></div><br><br>
<% end %>
<% if #subscription.new_record? %>
<div class="field">
<%= f.check_box :subscription_type %>
<div class="actions">
<%= f.submit "Subscribe", :class => "button mr8" %>
</div>
</div>
<% else -%>
<%= f.check_box :subscription_type, :value => #subscription.subscription_type, :disabled => true %>
<% form_for #subscription, :url => unsubscribe_user_path(current_user) do |f| %>
<div class="actions">
<%= f.submit "Unsubscribe", :class => "button mr8" %>
</div>
<% end %>
<% end %>
<% end %>
</div>
Use redirect_to instead of render (last method call in your unsubscribe action).
Found the bug in the view where I nested the 'unsubscribe' form within the 'subscribe' form - what didn't make sense.
I rearranged my view (separated those two forms, one in the 'if' part and the other in the 'else' part. Works fine now:
<div class = 'wrapper'>
<%= render :partial => "my_account_leftbar" %>
<% #all_subscriptions.each do |subscription| %>
<div class="field">Start: <%= subscription.started_at %></div><br><br>
<% end %>
<% if #subscription.new_record? %>
<% form_for #subscription, :url => subscribe_user_path(current_user) do |f| %>
<div class="field">
<%= f.check_box :subscription_type %>
<div class="actions">
<%= f.submit "Subscribe", :class => "button mr8" %>
</div>
</div>
<% end %>
<% else -%>
<% form_for #subscription, :url => unsubscribe_user_path(current_user) do |f| %>
<%= f.check_box :subscription_type, :value => #subscription.subscription_type, :disabled => true %>
<div class="actions">
<%= f.submit "Unsubscribe", :class => "button mr8" %>
</div>
<% end %>
<% end %>
</div>

Resources