I am fairly new to rails, so please bear with me. I have a very simple form that takes a couple values and sends it to my EmailController. I run very basic logic on it and just want to return the JSON to the view. It looks like the controller action is properly rendering the data (I can see in the server output), but the view doesn't change at all.
Here is my home.html.erb
<%= form_with(url: '/api/v1/emails', method: 'post') do |f| %>
<%= f.fields_for :email do |ev| %>
<%= ev.label :address %>
<%= ev.text_field :address %>
<% end %>
<%= f.submit 'Verify' %>
<% end %>
and emails_controller.rb (simplified)
class Api::V1::EmailController < Api::V1::BaseController
def create
#email = Email.new(email_params)
if #email.save
redirect_to api_v1_email_path(#email.id)
else
render json: #email.errors, status: :unprocessable_entity
end
end
def show
if #email
render 'show'
else
render json: #email, status: :not_found
end
end
def email_params
params.require(:email).permit(:address, :id)
end
It says it renders the template, but the view doesn't change:
Rendering api/v1/email/show.html.erb within layouts/application
Rendered api/v1/email/show.html.erb within layouts/application (0.9ms)
Completed 200 OK in 611ms (Views: 593.6ms | ActiveRecord: 1.3ms)
The template is just simple "hello world" plain text
Thank you in advance for the help!
I ended up having to create the form with local: true and it resolved the issue.
<%= form_with(url: '/api/v1/emails', method: 'post', local: true) do |f| %>
Related
I'm very new to RoR. I'm trying to learn.
This is my user_controller update/edit part.
def edit
binding.break
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
#binding.break
if #user.update(params.require(:user).keep_if{|key| #user.attributes[key] != params[:user][key]}.permit(:username, :email, :password))
# Call to debugger
flash[:notice] = "Article was updated successfully."
redirect_to #user
else
#binding.break
flash[:notice] = "Article was not updated successfully."
render 'edit'
end
end
This is my edit.html.erb file
<h1>Edit an existing User</h1>
<% if #user.errors.any? %>
<h2>The following errors prevented the user from being saved</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<%= puts msg%>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= form_with(model: #user, local: true) do |f| %>
<p>
<%= f.label :username %><br/>
<%= f.text_field :username %>
</p>
<p>
<%= f.label :email %><br/>
<%= f.text_field :email %>
</p>
<p>
<p>
<%= f.label :password %><br/>
<%= f.password_field :password %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
The success case is working fine.
For the negative case where the update fails, I wanted to print the error messages for which the update failed and render it. But currently it just renders the edit page again. Also the #user in the else part of the controller is having the values which is invalid and those are getting filled in the edit page upon rendering. I want to reset to original values and show the errors for which it got failed.
The errors can be anything like, email format not correct or something.
Server logs says
↳ app/controllers/users_controller.rb:19:in `update'
TRANSACTION (0.2ms) rollback transaction
↳ app/controllers/users_controller.rb:19:in `update'
Rendering layout layouts/application.html.erb
Rendering users/edit.html.erb within layouts/application
Username has already been taken
Rendered users/edit.html.erb within layouts/application (Duration: 23.0ms | Allocations: 2337)
Rendered layout layouts/application.html.erb (Duration: 59.2ms | Allocations: 4962)
Completed 200 OK in 152ms (Views: 87.9ms | ActiveRecord: 5.0ms | Allocations: 11120)
Can someone shed some light on this?
render 'edit' with render edit without controller, you need to call #user = User.find(params[:id]) before render to get original value.
If you want to show errors message, grant it to other const and render it in views
def edit
binding.break
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
#binding.break
if #user.update(params.require(:user).keep_if{|key| #user.attributes[key] != params[:user][key]}.permit(:username, :email, :password))
# Call to debugger
flash[:notice] = "Article was updated successfully."
redirect_to #user
else
#binding.break
#errors = #user.errors
#user = User.find(params[:id])
flash[:notice] = "Article was not updated successfully."
render 'edit'
end
end
in view, using #errors.full_messages
I have a system with posts and comments, each Post has_many Comments. I am trying to setup a turbostream so that when you post a comment it displays immediately.
Everything works in that data is persisted to the database but it seems like the turbo stream is not properly returning. When I hit the "comment" button nothing changes and I get a :no_content message for CommentsController#create
↳ app/controllers/comments_controller.rb:11:in `create'
[ActiveJob] Enqueued Turbo::Streams::ActionBroadcastJob (Job ID: b0be3a08-d7bb-4216-aac5-2f274a22dcbf) to Async(default) with arguments: "Z2lkOi8vY2lhby9Qb3N0LzM", {:action=>:append, :target=>"comments", :locals=>{:comment=>#<GlobalID:0x00007fa94123baf8 #uri=#<URI::GID gid://ciao/Comment/16>>}, :partial=>"comments/comment"}
[ActiveJob] Enqueued Turbo::Streams::ActionBroadcastJob (Job ID: 382e45b4-7a8f-4c8c-9e48-819fab0c19c4) to Async(default) with arguments: "Z2lkOi8vY2lhby9Qb3N0LzM", {:action=>:replace, :target=>#<GlobalID:0x00007fa9401ea938 #uri=#<URI::GID gid://ciao/Comment/16>>, :locals=>{:comment=>#<GlobalID:0x00007fa9401ea0f0 #uri=#<URI::GID gid://ciao/Comment/16>>}, :partial=>"comments/comment"}
No template found for CommentsController#create, rendering head :no_content
Completed 204 No Content in 37ms (ActiveRecord: 8.5ms | Allocations: 8931)
Turbo seems to work as far as creating the comment in the database, and sending back a comments POST request which I can see in the browser network tab. I'm wondering why there is :no_content and why the comments are not displaying until a page refresh.
Place.rb
class Post < ApplicationRecord
belongs_to :place
has_many :comments
broadcasts
end
Comment.rb
class Comment < ApplicationRecord
belongs_to :post
broadcasts_to :post
end
comment_controller.rb
def new
#comment = #post.comments.new
end
def create
#comment = current_user.comments.create!(comment_params)
respond_to do |format|
if #comment.save
format.turbo_stream do
render turbo_stream: turbo_stream.append(#comment, partial: 'comments/comment', locals: { comment: #comment })
end
format.html { redirect_to #comment.post.place }
end
end
end
On the post I render the comment as a partial:
<div class="post__comments--inner">
<%= render '/comments/show', post: post %>
</div>
Then comments/_show.html.erb
<%= turbo_stream_from post %>
<%= render post.comments %>
<% puts post.comments %>
<div class="comments__post">
<%= turbo_frame_tag "new_comment", src: new_post_comment_path(post), target: "_top" %>
</div>
_comment.html.erb
<div class="comment" id="<%= dom_id comment %>">
<%= comment.content %>
</div>
new.html.erb
<%= turbo_frame_tag "new_comment", target: "_top" do %>
<%= form_with model: [#comment.post, #comment], class: "comment-row__form",
data: { controller: "reset_form", action: "turbo:submit-end->reset_form#reset" } do |form| %>
<%= form.text_area :content, class: "comment-form--input form-control", data: {target: "comments.body"} %>
<%= form.hidden_field :post_id, value: #comment.post.id %>
<%= form.submit "comment", class: "btn btn-primary mt-2 mt-sm-0 ml-sm-3" %>
<% end %>
<% end %>
I think I may have found the issue but I'm not sure why it's happening the final think in the log is:
Turbo::StreamsChannel transmitting "<turbo-stream action=\"replace\" target=\"comment_36\"><template> <div class=\"comment\" id=\"comment_36\">\n <div class=\"comment__user\">\n
to my thinking it would be action=\"append\" rather than replace, especially since comment_36 does not currently exist on the page yet.
Try following this example from the docs:
respond_to do |format|
format.turbo_stream do
render turbo_stream: turbo_stream.append(:comments, partial: "comments/comment",
locals: { comment: #comment })
end
end
This should append it to a div in your html with an id of comments.
Goal: Update existing records with a modal without needing to link_to a new page.
Issue: I assume my issue is that I am unable to identify the exact record on the page with the form because I can't know this until the form is submitted.
ShopProduct Controller:
def new
#shop_product = ShopProduct.new
end
def create
#shop_product = ShopProduct.new(shop_product_params)
#shop = Shop.find_by(params[:shop_id])
product = Product.find_by(params[:product_id])
#shop_product.product_id = product.id
#shop_product.shop_id = #shop.id
if #shop_product.save!
redirect_to '/'
flash[:notice] = "saved"
else
redirect_to '/'
flash[:notice] = "no saved"
end
end
def update
#shop_product = ShopProduct.find_by(store_variant_id: params[:store_variant_id])
respond_to do |format|
if #shop_product.update_attributes!(product_id: params[:product_id], sync: params[:sync])
format.html { redirect_to #shop_product, notice: 'Shop product was successfully updated.' }
format.json { render :show, status: :ok, location: #shop_product }
else
format.html { render :edit }
format.json { render json: #shop_product.errors, status: :unprocessable_entity }
end
end
end
Aside from linkingto a new page, I can only think of defining directly on the
I load the form from this ShopDashboardController:
def product_variants
#shop = Shop.find(params[:shop_id])
session = ShopifyAPI::Session.new(domain: #shop.shopify_domain, token: #shop.shopify_token, api_version: '2019-04')
ShopifyAPI::Base.activate_session(session)
#in_store_products = ShopifyAPI::Product.find(:all)
#in_store_product = ShopifyAPI::Product.find(params[:shopify_product_id])
#in_store_variants = ShopifyAPI::Variant.find(:all, params: { product_id: params[:shopify_product_id]})
#shop_products = ShopProduct.where(shop_id: #shop)
#products = Product.all
#shop_product = ShopProduct.find_or_create_by(store_variant_id: params[:store_variant_id])
end
Now, as mentioned above, the only unique record for any ShopProduct is the id and the store_variant_id... If i use find_by in the def product_variants, the page won't load due to not being able to identify the #shop_product. I am unable to pass those params through because there may be multiple store_variant_ids, so I pass the Shop.id and ShopProduct.store_product_id only. But the store_product_id isn't a unique identifier as multiple records can have the same one. The only unique records are the id and store_variant_id.
Form (the variant is from a do loop):
<% #in_store_variants.each do |variant| %>
...
<%= form_for #shop_product do |f| %>
<%= f.collection_select :product_id, #products, :id, :sku %>
<%= f.hidden_field :store_product_id, value: variant.product_id %>
<%= f.hidden_field :store_variant_id, value: variant.id %>
<%= f.hidden_field :shop_id, value: #shop.id %>
<%= f.hidden_field :sync, value: true %>
<%= f.submit "Sync" %>
...
<% end %>
I am able to create new records only.
When i use the form again to update I get:
ActiveRecord::RecordInvalid (Validation failed: Store variant has already been taken):
app/controllers/shop_products_controller.rb:61:in `create'
Model ShopProduct:
belongs_to :product
has_one :shop
has_one :order
validates :store_variant_id, uniqueness: true, on: :create
If the record exists, shouldn't it update? Or is there something I am missing here?
It is possible to pursue my goal with rails/ruby alone or is javascript needed?
UPDATE:
I tried defining the ShopProduct on the front-end like so:
<% #in_store_variants.each do |variant| %>
<% shop_product = #shop_products.find_by(store_variant_id: variant.id) %>
<%= form_for shop_product do |f| %>
<%= f.collection_select :product_id, #products, :id, :sku %>
<%= f.hidden_field :store_product_id, value: variant.product_id %>
<%= f.hidden_field :store_variant_id, value: variant.id %>
<%= f.hidden_field :shop_id, value: #shop.id %>
<%= f.hidden_field :sync, value: true %>
<%= f.submit "Sync" %>
<% end %>
When submitting:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"gaMboYCSE8v63TVzmgx4pZDMhoz205f1MV+VMhmFA/WWhVh5Pcu6u/qayU8lDmjeRXw==", "shop_product"=>{"product_id"=>"1", "store_product_id"=>"1965345", "store_variant_id"=>"19364273", "shop_id"=>"1", "sync"=>"true"}, "commit"=>"Sync", "id"=>"12"}
Error:
NoMethodError (undefined method `update' for nil:NilClass):
or with update attributes:
NoMethodError (undefined method `update_attributes!' for nil:NilClass):
If it's finding it, shouldn't it be working? The param is being passed
It's because that form only caters the create action. Usually, if you need to update a resource, you go to /shop_products/:id/edit.
But if you really wanted to reuse that form, it's a little bit complicated adding more conditions, but what you want is to send a PUT request to /shop_products/:id and it would call the #update action of your controller. A form, by default, sends a POST request so consider that.
I am following a tutorial and we are creating a partial to to display a collection of data, but it is not rendering, my rails server command prompt shows that the database transaction is succesful:
Started GET "/portfolios" for ::1 at 2019-05-09 11:52:03 -0500
Processing by PortfoliosController#index as HTML
Rendering portfolios/index.html.erb within layouts/portfolio
Portfolio Load (0.3ms) SELECT "portfolios".* FROM "portfolios"
↳ app/views/portfolios/index.html.erb:5
Rendered collection of portfolios/_portfolio_item.html.erb [11 times] (2.1ms)
Rendered portfolios/index.html.erb within layouts/portfolio (5.3ms)
Rendered shared/_nav.html.erb (0.8ms)
Completed 200 OK in 31ms (Views: 28.6ms | ActiveRecord: 0.3ms)
Here is my partial under portfolios/_portfolio_item.html.erb:
<p><%= link_to portfolio_item.title, portfolio_show_path(portfolio_item) %></p>
<p><%= portfolio_item.subtitle %></p>
<p><%= portfolio_item.body %></p>
<%= image_tag portfolio_item.thumb_image unless portfolio_item.thumb_image.nil? %>
<%= link_to "Edit", edit_portfolio_path(portfolio_item) %>
<%= link_to 'Delete Portfolio Item', portfolio_path(portfolio_item), method: :delete, data: { confirm: 'Are you sure?' } %>
My index view under portfolios/index.html.erb :
<h1>Portfolio Items</h1>
<%= link_to "Create New Item", new_portfolio_url %>
<%= render partial: 'portfolio_item', collection: #portfolio_items %>
And my portfolios_controller :
class PortfoliosController < ApplicationController
layout 'portfolio'
def index
#portfolio_items = Portfolio.all
end
def show
#portfolio_item = Portfolio.find(params[:id])
end
def new
#portfolio_item = Portfolio.new
3.times { #portfolio_item.technologies.build }
end
def create
#portfolio_item = Portfolio.new(portfolio_params)
respond_to do |format|
if #portfolio_item.save
format.html { redirect_to portfolios_path, notice: 'Your portfolio item is now live!' }
else
format.html { render :new }
end
end
end
def edit
#portfolio_item = Portfolio.find(params[:id])
end
def update
#portfolio_item = Portfolio.find(params[:id])
respond_to do |format|
if #portfolio_item.update(portfolio_params)
format.html { redirect_to portfolios_path, notice: 'The record was successfully updated.' }
else
format.html { render :edit }
end
end
end
def destroy
#portfolio_item = Portfolio.find(params[:id])
#portfolio_item.destroy
respond_to do |format|
format.html { redirect_to portfolios_url, notice: 'Portfolio was successfully destroyed.' }
end
end
private
def portfolio_params
params.require(:portfolio).permit(:title,
:subtitle,
:body,
:main_image,
:thumb_image,
technologies_attributes: [:name])
end
end
If there is anything further needed please let me know and thanks in advance!
I'm not familiar with the collection syntax bbut for a quick fix try:
## index.html.erb
<h1>Portfolio Items</h1>
<%= link_to "Create New Item", new_portfolio_url %>
<% #portfolio_items.each do |item| %>
<%= render partial: 'portfolio_item', portfolio_item: item %>
<% end %>
Or if you really want to be fancy, and your model is called Portfolio, rename your partial to portfolios/_portfolio.html.erb. That will let you do:
## index.html.erb
<h1>Portfolio Items</h1>
<%= link_to "Create New Item", new_portfolio_url %>
<% #portfolio_items.each do |item| %>
<%= render item %>
<% end %>
Make sure there is no CSS or javascript that is removing/hiding your views. Also make sure you actually have some instances(rows) of the portfolio in your database.
<%= render partial: 'portfolio_item', collection: #portfolio_items %>
Although your usage of collection in partial is correct you can try to pass local variables and see if it works, because collection usage is dependent on file/model names which could cause issues.
When you use collection: #portfolio_items then you get access to use #portfolio_items instance variable within the partial view (portfolios/_portfolio_item.html.erb in your case).You are passing #portfolio_items but not using it anywhere, instead you are using portfolio_item variable which is not available in the partial view.
You need to iterate over #portfolio_items in order to render your items. If you need to pass variables to your partial use locals. Update your portfolios/index.html.erb as below
<h1>Portfolio Items</h1>
<%= link_to "Create New Item", new_portfolio_url %>
<% #portfolio_items.each do |portfolio_item| %>
<%= render partial: 'portfolio_item', locals: { portfolio_item: portfolio_item } %>
<% end %>
I have a triple nested form where a Form has many questions and questions have many answers. I have each form rendering on the show page with the form title and the form questions and input fields to create new answers associated with the asked question. The association is linking up nicely, however I cant seem to get the answers to insert into the db. I feel like it is something real easy but I cant seem to figure it out.
-Form-
<%= simple_form_for #form do |f| %>
<%= f.error_notification %>
<h1><%= #form.name %></h1>
<div class="fields">
<%= f.simple_fields_for :form_questions do |q| %>
<%= label_tag q.index+1 %>
<%= q.simple_fields_for :form_answers, q.object.form_answers.build do |a| %>
<%= a.input :answer, label: false %>
<% end %>
<% end %>
</div>
<div class="fields">
<%= f.button :submit, :class => "button", value: 'Submit' %>
</div>
<% end %>
-Form Controller-
class FormsController < ApplicationController
def new
#form = Form.new
#form.form_questions.form_answers.build
end
def create
#form = Form.new(form_params)
end
def index
#forms = Form.includes(:form_questions).all
end
def show
#form = Form.find(params[:id])
end
def edit
#form = Form.find(params[:id])
end
def form_params
params.require(:form).permit(:id, :name, form_questions_attributes: [:title, form_answers_attributes: [:answer]])
end
end
-Form Answer Controller (although if the associations are correct I feel like I should be able to handle everything in the forms controller)
class FormAnswersController < ApplicationController
def new
#form_answer = FormAnswer.new
end
def create
#form_answer = FormAnswer.new(form_answer_params)
if #form_answer.save
RequestMailer.request_submit(#form).deliver
else
format.html {render action: 'new'}
end
end
def form_answers_params
params.require(:form_answer).permit(:form_id, :form_question_id, :id, :answer)
end
end
-Server Logs-
Started PATCH "/forms/12" for 127.0.0.1 at 2014-08-05 13:31:44 -0400
Processing by FormsController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"CcaCGjdoYPH2Boi1prIFhG+h5YuPHQvAxMp1dcKvWr8=", "form"=>{"form_questions_attributes"=>{"0"=>{"form_answers_attributes"=>{"0"=>{"answer"=>"hey"}}, "id"=>"50"}, "1"=>{"form_answers_attributes"=>{"0"=>{"answer"=>"ya"}}, "id"=>"51"}, "2"=>{"form_answers_attributes"=>{"0"=>{"answer"=>"go"}}, "id"=>"52"}}}, "commit"=>"Submit", "id"=>"12"}
Rendered forms/update.html.erb within layouts/application (0.1ms)
Completed 200 OK in 12ms (Views: 11.4ms | ActiveRecord: 0.0ms)