I have a form that works well. Here is my partial:
<%= form_for #reservation, url: {controller: 'reservations', action: 'create'} do |f| %>
<div class="row">
<div class="col-sm-8 col-sm-offset-1">
<div class="form-group">
<%= f.text_field :name, class: 'form-control', id: 'name', placeholder: 'Party Name', required: true %>
</div>
<div class="form-group">
<div class="col-sm-4">
Party Size:
</div>
<div class="col-sm-6">
1 <%= f.radio_button :party_size, '1'%>
2 <%= f.radio_button :party_size, '2'%>
</div>
</div>
<div class="col-sm-4 col-sm-offset-8">
<div class="form-group">
<%= f.submit 'submit', class: 'btn default-btn btn-block' %>
</div>
</div>
</div>
</div>
Here is the controller action create:
def create
#reservation = Reservation.new(reservation_params)
#reservation.customer_id = session[:customer_id]
if #reservation.save
redirect_to :back
else
render 'reservations/new'
end
end
I would like to render 'reservations/confirmation' in place of the current partial reservations/form. I know I need to add remote: true, to the form_for tag, but I get stuck there. What & where do I need to write in JS to make this submit function work with AJAX? What do I need to add to the create action?
You need to submit it via JQuery with $.post() and you also have to use respond_to to respond to both JavaScript submissions and normal HTML submissions (in case someone has JavaScript disabled).
http://guides.rubyonrails.org/working_with_javascript_in_rails.html#server-side-concerns
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.js {}
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
Related
I am trying to implement a contact form on my one-paged portfolio site using the mail_form gem and cannot seem to get the form to show up when using AJAX. I resorted to AJAX in place of the redirect_to approach because I do not want the site to refresh and scroll down to the anchor upon submission.
views/pages/home.html.erb:
<%= render 'contacts/new' %>
<div id="contact_form"></div>
config/routes.rb:
Rails.application.routes.draw do
root 'pages#home'
resources :contacts, only: [:new, :create]
end
models/contact.rb:
class Contact < MailForm::Base
attribute :name, validate: true
attribute :email, validate: /\A([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})\z/i
attribute :message, validate: true
attribute :nickname, captcha: true
def headers
{
subject: "Regarding Your Portfolio",
to: "email (removed for privacy)",
from: %("#{name}" <#{email}>)
}
end
end
controllers/contacts_controller.rb:
class ContactsController < ApplicationController
before_action :contact_params, only: [:new, :create]
def new
#contact = Contact.new
end
def create
#contact = Contact.new(params[:contact])
#contact.request = request
if #contact.deliver
respond_to do |format|
# format.html {redirect_to root_path(anchor: "contact"), notice: 'Thank you for your message! I will get back to you shortly.'}
format.html {flash.now[:notice] = 'Thank you for your message! I will get back to you shortly.'}
format.js {render partial: 'contacts/new'}
end
else
respond_to do |format|
format.html {flash.now[:alert] = 'Your message could not be sent. Please try again.'}
format.js {}
end
end
end
private
def contact_params
params.require(:contact).permit(:name, :email, :message, :nickname, :captcha)
end
end
controllers/pages_controller:
class PagesController < ApplicationController
def home
#contact = Contact.new
end
end
contacts/_new.js.erb:
document.querySelector('#contact_form').innerHTML = '<%= j render 'new', locals: {contact: #contact} %>'
contacts/_new.html.erb:
<div class="row">
<div class="mb-md-0 mb-5">
<%= form_for #contact, id: 'contact_form', remote: true do |f| %>
<div class="row">
<div class="col-2"></div>
<div class="col-md-4">
<div class="md-form mb-0">
<%= f.text_field :name, required: true, class: 'form-control', placeholder: "Name" %>
</div>
</div>
<div class="col-md-4">
<div class="md-form mb-0">
<%= f.text_field :email, required: true, class: 'form-control', placeholder: "Email" %>
</div>
</div>
</div>
<div class="row">
<div class="col-2"></div>
<div class="col-md-8">
<div class="md-form mt-4">
<%= f.text_area :message, rows: 8, required: true, class: 'form-control md-textarea', placeholder: "Message"%>
</div>
</div>
</div>
<div class= "hidden d-none">
<%= f.text_field :nickname %>
</div>
<div class="text-center text-md-left">
<%= f.submit 'Send Message', class: 'btn btn-outline-secondary btn-sm col-3 mt-4 mx-auto' %>
</div>
<% end %>
</div>
</div>
The form works, but I would love to figure out why AJAX isn't working for it. I have searched for many hours and have also copied code from a previous application of mine that uses AJAX for a form to no avail. I want to be able to have the same effect as redirect_to root_path without having to reload the page and auto-scroll to the anchor. I have also tried copying the create method from the contacts_controller to the pages_controller with no success either. Any tips would be greatly appreciated!
I'm new to RoR and i have this problem in Rails 6 where i am trying to add a category to a categories list with ajax so that when i submit my form, the page shouldn't refresh. But it keeps refreshing.
I followed this tutorial and split the list of into a _category.html.erb partial and created a create.js.erb file that appends to the list but i don't get the same results.
here is my index.html.erb:
<body>
<main>
<section class="hero is-link is-fullheight pt-3">
<div class="hero-body">
<div class="container is-align-self-flex-start">
<h1 class="title has-text-centered">categories</h1>
<!-- container for the list of categories -->
<div class="container" id='category'>
<%= render #category %>
</div>
<h2 class="is-size-4">add a category</h2>
<%= render 'form', category: #category %>
</div>
</div>
</section>
</main>
</body>
this is my category list partial:
<!-- Loop over categories and add delete btn-->
<% #categories.each do |category| %>
<div class="columns is-mobile card mb-4">
<div class="column is-align-self-center ">
<p class="is-size-5"><%= category.name %></p>
</div>
<div class="column is-narrow">
<button class="button is-danger"><%= link_to 'delete', category, method: :delete, data: { confirm: 'Er du sikker?' } %></button>
</div>
</div>
<% end %> <!-- end of loop -->
this is my form partial for the input and submit btn:
<%= form_with(model: category) do |form| %>
<% if category.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(category.errors.count, "error") %> prohibited this category from being saved:</h2>
<ul>
<% category.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field has-addons">
<%= form.text_field :name , ({:class => "input", })%>
<%= form.submit "add", ({:class => "button is-primary", })%>
</div>
<% end %>
this is my create method in my categories controller that i suspect is the culprit because when i remove the format.html and format.json i get
"ActionController::UnknownFormat in CategoriesController#create"
def create
#categories = Category.all
#category = Category.new(category_params)
respond_to do |format|
if #category.save
format.js
format.html { redirect_to action: "index", notice: "Category was successfully created." }
format.json { render :show, status: :created, location: #category }
else
format.html { render :"index", status: :unprocessable_entity }
format.json { render json: #category.errors, status: :unprocessable_entity }
end
end
end
this is the js file to append the new category to the list:
var body = document.getElementByID("category");
body.innerHTML = "<%= j render #categories %>" + body.innerHTML;
I have tried
This tutorial with jquery also but that also reloads on submit.
I have triple checked that i don't have "local:true" anywhere.
Changed tried #category instead of #categories in some places with no luck
I generated the project with a scaffold
Is there something that i'm missing?
Any guidance is greatly appreciated.
You are not using data-remote attribute for your form. This attribute means the form will submit the results via Ajax.
I am trying to implement AJAX in my to do type app, but am getting a missing template error (Missing template items/create, application/create with {:locale=>[:en], :formats=>[:html], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee]}.) on my create action. There shouldn't be a create.html.erb page because the creation of items happens on a lists#show page:
<div class='new-item'>
<%= render 'items/form' %>
</div>
<div class="js-items">
<% #items.each do |item| %>
<%= div_for(item) do %>
<p><%= link_to "", list_item_path(#list, item), method: :delete, remote: true, class: 'glyphicon glyphicon-ok', style: "margin-right: 10px" %>
<%= item.name %>
<% if item.delegated_to != "" && item.user_id == current_user.id %>
<small>(Delegated to <%= item.delegated_to %>)</small>
<% elsif item.delegated_to != "" && item.user_id != current_user.id %>
<small>(Delegated by <%= item.user_id %>)</small>
<% end %></p>
<% end %>
<% end %>
</div>
And the _form.html.erb partial to which it links:
<div class="row">
<div class="col-xs-12">
<%= form_for [#list, #item], remote: true do |f| %>
<div class="form-group col-sm-6">
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control', placeholder: "Enter Item Name" %>
</div>
<div class="form-group col-sm-6">
<%= f.label 'Delegate To (Not Required)' %>
<%= f.text_field :delegated_to, class: 'form-control', placeholder: "Enter an Email Address" %>
</div>
<div class="text-center"><%= f.submit "Add Item to List", class: 'btn btn-primary' %></div>
<% end %>
</div>
</div>
Here's the create method in the items_controller:
def create
#list = List.friendly.find(params[:list_id])
#item = #list.items.new(item_params)
#item.user = current_user
#new_item = Item.new
if #item.save
flash[:notice] = "Item saved successfully."
else
flash[:alert] = "Item failed to save."
end
respond_to do |format| <<<<ERROR CALLED ON THIS LINE
format.html
format.js
end
end
And here's my create.js.erb file:
<% if #item.valid? %>
$('.js-items').prepend("<%= escape_javascript(render(#item)) %>");
$('.new-item').html("<%= escape_javascript(render partial: 'items/form', locals: { list: #list, item: #new_item }) %>");
<% else %>
$('.flash').prepend("<div class='alert alert-danger'><button type='button' class='close' data-dismiss='alert'>×</button><%= flash.now[:alert] %></div>");
$('.new-item').html("<%= escape_javascript(render partial: 'items/form', locals: { list: #list, item: #item }) %>");
<% end %>
Anyone have any ideas why I am getting this? I tried removing the format.html line from the items_controller but I got an unrecognizable format error.
You can force the form submission to use a JS format, instead of the default HTML format, so that your respond_to block will render the create.js.erb view instead of the create.html.erb view.
<%= form_for([#list, #item], format: :js, remote: true) do |f| %>
Typically, Rails will detect your remote: true option via "unobtrusive JavaScript (ujs)", which will effectively handle the format: :js option for you. There is something unique in your setup that we have not identified, which is requiring you to enforce the format option.
To see that your create.js.erb is working, you can change the line that renders your item partial:
$('.js-items').prepend("<%= escape_javascript(render(#item)) %>");
To render a the item's name, wrapped in a <p> tag:
$('.js-items').prepend("<p>" + <%= #item.name %> + "</p>");
You can customize that line to generate the html that you want to appear for your new list item. Based on your code above, this looks like it will work out for you.
As you finish your to-do app, you can create a partial for your item, and then change that line again so that your partial will be appended to your list of items.
Im trying to implement a bit a Ajax into my app seems to work correctly but it isnt updating the page after the action is executed .
I added Ajax to my Destroy method
def destroy
#item = Item.find(params[:id])
#item.user = current_user
if #item.destroy
flash[:notice] = "Item Completed"
else
flash[:alert] = "Item was unable to be marked completed "
end
respond_to do |format|
format.html
format.js
end
end
i have update my _item.html.erb and _form.html.erb partials with remote: true
<%= content_tag :div, class: 'media', id: "item-#{item.id}" do %>
<div class="media">
<div class="media-body">
<small>
<%= item.name.titleize %>
| submitted <%= time_ago_in_words(item.created_at) %>
<% if current_user == #user %>
<%= link_to "Completed ", [#user, item], method: :delete,remote: true, class: 'glyphicon glyphicon-ok' %><br>
<% end %>
</small>
</div>
</div>
<% end %>
_from.html.erb
<%= form_for [#user, item ], remote: true do |f|%>
<div class="form-group">
<%= f.label :name, class: 'sr-only' %>
<%= f.text_field :name , class: 'form-control', placeholder: "Enter a new item " %>
</div>
<%= f.submit "Submit Item", class: 'btn btn-primary pull-right' %>
<% end %>
User#show view <div class='new_item'>
<%= render :partial => 'items/form', :locals =>{:item => Item.new} %>
</div>
<% end %>
<div class='js-items'>
<% #user.items.order('created_at DESC').each do |item| %>
<%= render :partial => 'items/item' , :locals => {:item => item } %>
</div>
destroy.js.erb
$('#item-<%= #item.id %>').hide();
Like I said before, the item is deleted when i click the delete button but i need to refresh the page in order to see the updated page. Any ideas what i might be doing wrong, any push to the right direction would be greatly appreciated!
Try rendering the java script in the method itself and check,
def destroy
#item = Item.find(params[:id])
#item.user = current_user
if #item.destroy
flash[:notice] = "Item Completed"
else
flash[:alert] = "Item was unable to be marked completed "
end
respond_to do |format|
format.html
format.js { render
$('#item-<%= #item.id %>').hide();
}
end
end
I'm trying to edit content from my rails app Products through a zurb foundation modal. I can add new just fine. But when trying click the edit button, it loads a modal with the whole page again. Although it loads the correct edit page, I just want an edit form. Not a whole new render of my site.
Products_controller.rb
class ProductsController < ApplicationController
before_action :set_product, only: [:show, :edit, :update, :destroy]
# GET /products
# GET /products.json
def index
#products = Product.all
#product = Product.new
end
# GET /products/1
# GET /products/1.json
def show
end
# GET /products/new
def new
#product = Product.new
end
# GET /products/1/edit
def edit
#product = Product.find(params[:id])
end
# POST /products
# POST /products.json
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render :show, status: :created, location: #product }
else
format.html { render :new }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /products/1
# PATCH/PUT /products/1.json
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to #product, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #product }
else
format.html { render :edit }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
# DELETE /products/1
# DELETE /products/1.json
def destroy
#product.destroy
respond_to do |format|
format.html { redirect_to products_url, notice: 'Product was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_product
#product = Product.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def product_params
params.require(:product).permit(:name, :price)
end
end
index.html.erb
<h1>Listing products</h1>
Click Me For A Modal
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #products.each do |product| %>
<tr>
<td><%= product.name %></td>
<td><%= product.price %></td>
<td><%= link_to 'Show', product %></td>
<td><%= link_to 'Edit', edit_product_path(product) %></td>
<td><%= link_to 'EditM', edit_product_path(product), :remote => true, class: "", "data-reveal-id" => "editModal", "data-reveal-ajax" => edit_product_path(product) %></td>
<td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Product', new_product_path, class: "button radius", "data-reveal-id" => "addModal" %>
<div id="addModal" class="reveal-modal" data-reveal>
<%= render :partial => 'form', locals: { product: #products } %>
<a class="close-reveal-modal">×</a>
</div>
<div id="editModal" class="reveal-modal" data-reveal>
<%= render :partial => 'form' %>
<a class="close-reveal-modal">×</a>
</div>
_form.html.erb
<%= form_for(#product) do |f| %>
<% if #product.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#product.errors.count, "error") %> prohibited this product from being saved:</h2>
<ul>
<% #product.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="row">
<div class="small-3 columns">
<%= f.label :name, class: "right inline" %>
</div>
<div class="small-9 columns">
<%= f.text_field :name %>
</div>
</div>
<div class="row">
<div class="small-3 columns">
<%= f.label :price, class: "right inline", title: "Price In USD", data: { tooltip: true } %>
</div>
<div class="small-9 columns">
<%= f.text_field :price %>
</div>
</div>
<div class="row">
<div class="small-9 small-offset-3 columns">
<%= f.submit %>
</div>
</div>
<% end %>
Below is an image of how it loads after you select the EditM button.
I have updated my :edit method to the following. It appears to be working so it loads the action only, not the layout. Is this the proper way? Or should I be doing it differently?
def edit
#product = Product.find(params[:id])
render :layout => false
end
This is the modal in index.html.erb:
<div id="editModal" class="reveal-modal" data-reveal>
<a class="close-reveal-modal">×</a>
</div>