Change a partial with AJAX - javascript

I learn Ajax from some time, which is great by the way, so i'm not really an expert of it. So I need some advice about it.
I'm currently building a group system. I have a tab system to differentiate your own groups and the others. What I trying to do know, is script an ajax to send my partial group from a tab to the other. For example when the user quit a group, it will disappear of my groups to appear in the tab others.
I succeed the first step but I don't really know how I can send the form into the other tab.
As you can see bellow, I've try to send the id, but there is no partial form include. So it's just rendering the id of my group.
Any advices to include the partial (_group2.html.erb) ?
My code :
Index(group):
<div class="panel-body">
<div class="tab-content">
<div class="tab-pane fade in active" id="tab1default_group" style="padding:0px;">
<div class="group_area">
<% #my_groups.each do |group| %>
<div class="group_box<%=group.id%>">
<%= render 'group', group: group %>
</div>
<% end %>
</div>
</div>
<div class="tab-pane fade in" id="tab2default_group" style="padding:0px;">
<div class="group_area2">
<% #groups_out.each do |group| %>
<div class="group_box2<%=group.id%>">
<%= render 'group2', group: group %>
</div>
<% end %>
</div>
</div>
</div>
</div>
rem_req.js.erb(group):
$(".group_box<%=#group.id%>").fadeOut();
$(".group_area2").append('<%= render partial: #group2 %>');
Groups_controller :
def rem_req
if #group.groupes_admin.count == 1 && #group.groupes_admin.where(user_id: current_user).any?
flash[:error] = "Vous devez définir un nouvel Admin ou supprimer ce groupe"
else
RemReqGroupJob.perform_later(current_user, #group)
flash[:error] = "Vous ne faites plus parti de #{#group.name}"
#group2 = Group.where(id: #group.id).last
end
respond_to do |format|
format.html { redirect_back }
format.js
end
end
And when i try to run this, I have this error :
ActionView::Template::Error ('nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.):

I assume the happy case scenario to give you direction. I think you are close but you need to make some tweak
From the snippet
<% #groups_out.each do |group| %>
<div class="group_box2<%=group.id%>">
<%= render 'group2', group: group %>
</div>
<% end %>
first move <div class="group_box2<%=group.id%>">..</div> also in group2 partial.
Then replace $(".group_area2").append('<%= render partial: #group2 %>');
with $(".group_area2").append('<%= j(render(partial: "group2")) %>');

Related

Dynamically generate copies partial form based on user selection in a nested form

I'm trying to create a form that will generate copies of a nested form depending on a user's selection. I have 3 models: Visualizations, which has_many Rows, which has_many Panes.
I want to be able to allow the user to click a small dropdown menu that will generate a row and then select 1, 2, or 3 panes. Depending on their selection, this will generate the form of their choice from a render.
here is an example where a user chose 3 panes (3 upload boxes for images) with just styled divs in place of the form
For starters. I know I would need to change up my controller a bit, but I'm not sure how to do that with a nested form since in every example I've seen so far, you need to predetermine how many times you would "build" a nested model form (not sure if that is the correct terminology). I'm not sure how to make that part dynamic.
Here is what I mean:
//visualization_controller.rb
def new
#visualization = Visualization.new
rows = #visualization.rows.build
panes = rows.panes.build
end
here is an example of the render that would be called if the usr wanted to generate a row with just 1 Pane:
<div class="medium-12 columns one_box_wrapper first" style="display:none">
<%= f.fields_for :rows, Row.new do |r| %>
<%= r.label :rowtitle %>
<%= r.text_field :rowtitle %>
<div class="nested" style="display:inline-block">
<%= r.fields_for :panes, Pane.new do |p| %>
<%= p.label :text %>
<%= p.text_field :text %>
<div class="mehv">
<img id="img_prev" width=300 height=300 src="#" alt="your image" class="img-thumbnail hidden"/> <br/>
<span class="meh">
Upload Pane Image<%= p.file_field :pane_photo, id: "pane_photo" %>
</span>
<%= p.hidden_field :pane_photo_cache %>
</div>
<% end %>
</ul>
</div>
<% end %>
</div>
Is this possible in rails? I feel like javascript would come into play but I don't think you are allowed to render server-side code through JS.
I feel a bit in over my head with this and any help would be greatly appreciated!
EDIT
I ended up using cocoon gem as it seems to have made my life a lot easier however.. I can't get the visualizations to all display on the index page! I'm not sure what the issue is.. The cover Title and Image show up but nothing else does!
//schema.rb
create_table "panes", force: :cascade do |t|
t.integer "row_id", null: false
t.text "text"
t.string "pane_photo"
end
create_table "rows", force: :cascade do |t|
t.integer "visualization_id", null: false
t.string "rowtitle"
end
create_table "visualizations", force: :cascade do |t|
t.string "title", null: false
t.string "cover_image"
end
//controller.rb
class VisualizationsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
before_action :authorize_user, except: [:index, :show]
helper_method :resource
def index
#visualizations = Visualization.all
#visualization = Visualization.new
end
def new
#visualization = Visualization.new
rows = #visualization.rows.build
panes = rows.panes.build
end
def create
#visualization = Visualization.new(visualization_params)
if #visualization.save
redirect_to edit_visualization_path(#visualization)
else
render :new
end
end
def edit
#visualizations = Visualization.all
#visualization = Visualization.find(params[:id])
end
protected
def visualization_params
params.require(:visualization).permit(:title, :cover_image, rows_attributes: [:visualization_id, :rowtitle, panes_attributes:[:text, :pane_photo]])
end
//index.html
<div class="visual_wrap text-center">
<ul class="no-bullet text-center">
<% #visualizations.each do |visualization| %>
<li class="text-center">
<div class="row visualization text-center">
<%= image_tag visualization.cover_image.to_s, id: "visualization_image", class: "output_image" %>
<p class="visualization_title"><%= visualization.title %></p>
<p>+</p>
</div>
<div class="placeholder">
<% visualization.rows.each do |row| %>
<%= row.rowtitle %>
<% row.panes.each do |p| %>
<%= p.text %>
<%= image_tag p.pane_photo.to_s, id: "pane_image", class: "output_image" %>
<% end %>
<% end %>
</div>
</li>
<% end %>
</ul>
</div>
//new.html.erb
<% if user_signed_in? %>
<% if current_user.admin? %>
<div class="try">
<div class="breadcrumb-wrapper">
<ul class="breadcrumbs">
<%= form_for #visualization, :html => {:multipart => true} do |f| %>
<li>Preview</li>
<li><div class="separating-bar">|</div>
<li><input type="submit" name="commit" value="Save" class="save button secondary"></li>
</ul>
</div>
<div class="cover_image text-center">
<div class="row pre_img_prev">
<div class="gray_bar"></div>
<div class="img_prev">
<img id="img_preview" src="#" alt="your image" class="img-thumbnail hidden text-center"/>
<input type="text" name="visualization[title]" id="visualization[title]" class="inputtext" />
<label for="visualization[title]" id="text_preview">+ Add Title</label>
<div class="trash-can_title">
<i class="fa fa-trash fa-3x" aria-hidden="true"></i>
</div>
</div>
<input type="file" name="visualization[cover_image]" id="visualization_cover_image" class="inputfile" />
<label for="visualization_cover_image">+ Add Cover Image</label>
<%= f.hidden_field :cover_image_cache %>
<div class="trash-can_cover">
<i class="fa fa-trash fa-3x" aria-hidden="true"></i>
</div>
</div>
</div>
<div class="row linkstest">
<div class="rows">
<%= f.fields_for :rows, Row.new do |task| %>
<%= render 'row_fields', f: task %>
<% end %>
<div class="links">
<%= link_to_add_association "Add Row", f, :rows %>
</div>
</div>
<% end %>
<% end %>
<% end %>
</div>
</div>
You don't need really need JS except to make it more "snappy". I think what you are looking for are Nested Forms.
Take a look at RaisCast it's a bit outdated but it's a good foundation.
You might want to look into Cocoon gem to help you create the new fields dynamically.
Rails nested_form inside nested_form
Update
If you don't want to use Javascript at all, you need to make an extra request to the controller to tell it to initialize (build) the rows and panes for you so that it renders the empty fields in the view.
For example:
def new
#visualization = Visualization.new
num_rows = params[:num_rows]
num_rows.each { #visualization.rows.build }
end
You would also need to have the number of panes in each row, so the parameter would have to be inside the row attributes, e.g. params[:visualization][:rows][0][:num_panes], and using that parameter you initialize the panes in the row. It's probably best to put that logic in a Factory service.
However if you create the empty fields using JS you can save yourself from making that extra request and all that logic. This is because Rails will automatically initialize (build) the rows and panes if you give it the right attributes, such as:
{
visualization: {
rows_attributes: {
# These are the attributes from an existing record
0: {
id: 39,
panes_attributes: {
0: {
id: 992
}
}
},
# This is a new set of attributes for a new record
# passed by the fields generated by cocoon where xxx
# and yyy are random numbers
xxxx: {
panes_attributes: {
yyyy: {
}
}
}
}
}
}
Then all you need to do in your controller is:
def new
# visualization_params are the permitted attributes
#visualization = Visualization.new(visualization_params)
end

Rails Kaminari example infinite scroll not working

I am trying to implement kaminari rails infinite scroll using this example.
It is working fine in my other apps but not working in this app.
Kaminari example
My company controller
def index
#companies = Company.all.page(params[:page]).per(4)
end
My index.html.erb
<div class="posts">
<div class="page">
<%= render #companies %>
</div>
</div>
<%= paginate #companies %>
My _company.html.erb
<div class="post">
<% company_decorator = CompanyDecorator.new(company)%>
<h4><%= company.name %> ||
No. of operations: <%= company_decorator.number_of_operations %> ||
Average Amount : <%= company_decorator.average_of_amount %> ||
Highest of Month: <%= company_decorator.highest_of_month%> ||
Accepted Operations: <%= company_decorator.accepted_operations%>
</h4>
<ul>
<% company.operations.each do |operation| %>
<li><%= operation.id%></li>
<%end%>
</ul>
</div>
My assets/javascripts/companies.js.coffee
$(document).ready ->
$(".posts .page").infinitescroll
navSelector: "nav.pagination"
nextSelector: "nav.pagination a[rel=next]"
itemSelector: ".posts .post"
My index.js.erb
$(".posts").append("<div class='page'><%= escape_javascript(render(#companies)) %></div>");
I am unable to find problem please help me. This same code is working in my other app.
Try
def index
#companies = Company.all.page(params[:page]).per(25)
end
playing with the pagination length.. I've heard that sometimes works

Rails: Checkbox to change the value of a variable on click

I have a basic to do type app and I am trying to change the variable completed for an item to the current date/time when I click a checkbox. I found this post, which has led me to the following code structure:
My lists#show, on which items are created, checked off (updated), edited, and deleted:
<div class="container">
<div class="row" style="height: 70px"></div>
</div>
<div class="col-xs-10 col-xs-push-1 container">
<div class="md-well">
<h1 class="text-center"><%= #list.name %></h1>
<% if #items.count == 0 %>
<h4 class="text-center">You don't have any items on this list yet! Want to add some?<h4>
<% elsif #items.count == 1 %>
<h4 class="text-center">Only <%= #items.count %> Item To Go!</h4>
<% else %>
<h4 class="text-center">You Have <%= #items.count %> Items To Go!</h4>
<% end %>
<%= render 'items/form' %>
<% #items.each do |item|%>
<p>
<%= form_for [#list, item], class: "inline-block", id: 'item<%= item.id %>' do |f| %>
<%= f.check_box :completed, :onclick => "$this.parent.submit()" %>
<% end %>
<%= item.name %>
( <%= link_to "Delete", list_item_path(#list, item), method: :delete %> )
</p>
<% end %>
<div class="text-center">
<%= link_to "Back to My Lists", lists_path %>
</div> <!-- text-center -->
</div> <!-- well -->
</div> <!-- container -->
Here is my update method in my items_controller (which I believe has jurisdiction instead of the lists_controller even though it is the lists#show page because it is an item being updated:
def update
#list = List.friendly.find(params[:list_id])
#item = #list.items.find(params[:id])
#item.name = params[:item][:name]
#item.delegated_to = params[:item][:delegated_to]
#item.days_til_expire = params[:item][:days_til_expire]
#item.completed = params[:item][:completed]
#item.user = current_user
if #item.update_attributes(params[:item])
#item.completed = Time.now
end
if #item.save
flash[:notice] = "List was updated successfully."
redirect_to #list
else
flash.now[:alert] = "Error saving list. Please try again."
render :edit
end
end
Here is what is currently appearing:
And here are the two problems I'm having:
LESS IMPORTANT PROBLEM: The checkboxes should be displayed inline with the list items but aren't. My class inline-block simply makes links to this in the application.scss file:
.inline-block {
display: inline-block !important;
}
MORE IMPORTANT PROBLEM: Even after the checkbox is clicked, the value for completed remains nil as per my console:
[6] pry(main)> Item.where(name: "Feed Dexter")
Item Load (0.1ms) SELECT "items".* FROM "items" WHERE "items"."name" = ? [["name", "Feed Dexter"]]
=> [#]
Any ideas how to make this functional?

Rails auto clicking a button to refresh div

I'm new to programming and teaching myself Rails as I build an app.
I have a page with a list of jobs. Each job is rendered via a job partial which has a div which renders a status partial via a "Update Status" button with ajax.
The button works as expected and refreshes each job's status when clicked.
I am now trying to make the status div auto-refresh every x seconds.
After trying different methods, I've implemented a javascript which simply periodically refreshes the button (which I can later hide if I want). It was suggested here by #de_an777: Rails 3 - Periodic Refresh of DIV.
This seems to almost work for me.
The problem is that it doesn't refresh ALL jobs in the list. So if I have 2 jobs in the list, I see two refresh operations happening in the server log, but both of them refresh only the first job.
When inspecting the job id for each button element in the browser inspector, they all seem to have the correct id via job[:id].
I've been struggling with this for days, changing my routes and methods but cannot make it work. Hoping that someone can direct me in the right way...
Jobs_controller.rb:
def update
#job = Job.find(params[:id])
#some updating logic
respond_to do |format|
format.js
end
end
index.html.erb:
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<h1>Job List</h1>
</section>
<section class="job_form">
<%= link_to "New Job", upload_path, class: "btn btn-lg btn-primary" %>
</section>
</aside>
</div>
<%= render #jobs %>
_job.html.rb:
<span class="jobs">
<p> Job ID: <%= job.id %></p>
<p> Job Name: <%= job.name != "" ? job.name : "Untitled" %>
<p> Source File: <%= job.source_file %></p>
<% #job = job %>
<div id="status_div_<%= #job.id %>">
<%= render :partial => 'jobs/status', locals: {job: job} %>
</div>
<%= button_to("Update Status", jobs_update_path(id: job.id), remote: true, method: :patch, id: "refresh_#{job[:id]}") %>
</span>
</li>
<script type="text/javascript">
function doSomething() {
$('#refresh_<%= job[:id] %>').click();
}
setInterval('doSomething()',10000);
</script>
routes:
get 'new_job' => 'jobs#new'
get 'upload' => 'jobs#upload'
patch "jobs/update", to: "jobs#update"
resources :jobs
update.js.erb:
$('#status_div_<%= #job.id %>').html("<%= j render :partial => 'status', object: #job, as: :job %>");
_status.html.erb:
<p> Status: <%= job[:status] %></p>

Displaying flash messages on modal dialog box on submit rails

I am trying to build the modal box for registering emails addresses in rails.
I have done it but can not able to find the way to display the flash messages on modal dialog box when registration fails.
On submit the box gets closed and message is only getting display on home page. I do not have good knowledge of web or rails, but would be nice if I can get some pointers.
The functionality is working fine expect on fail, dialog box should display the messages on it instead of on home page. I know where is issue in my code but do not know approach to do it. Here is my code for dialog box
<div id="register" class="modal fade" role="dialog">
<div class="modal-dialog">
<!-- Modal content-->
<%= form_for(#email, url: home_esave_path, :html => {class:"model-dialog"}) do |f| %>
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Register</h4>
</div>
<div class="modal-body">
<div class="form-group">
<%= f.text_field :email, class:"form-control", placeholder: "Enter a valid gmail address" %>
</div>
<%= f.show_simple_captcha(:email=>"email", :label => "Human Authentication", :refresh_button_text => "Refresh text", class:"form-control", :placeholder => "Enter the code") %>
<%= f.submit "Register me!", :class => "btn register_button" %>
</div>
<div class="modal-footer" style="text-align:left">
<!-- show in case of success -->
<% if #email.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#email.errors.count, "error") %> prohibited
this email from being saved:
</h2>
<ul>
<% #email.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
<% end %>
</div>
</div>
my controller code to get the messages
def save
#email = Email.new(user_params)
if !user_params.nil?
if #email.save_with_captcha
flash[:success] = "Success! The email address #{#email.email} has been successfully registered. We will contact you as soon as we have available rooms for new beta testers."
redirect_to :action => 'index'
else
if #email.valid?
flash[:error] = "The captcha is not correct, enter again the captcha."
redirect_to :action => 'index'
else
flash[:error] = "You have put invalid #{#email.email}"
redirect_to :action => 'index'
end
end
end
end
Flash messages I am displaying outside this model which is obvious that will be displayed on home page. But I did not find way to display it on model dialog box.
here is how I am displaying the messages
<% flash.each do |type, message| %>
<div class="alert <%= alert_for(type) %> fade in">
<button class="close" data-dismiss="alert">×</button>
<span class="sr-only">Close</span>
<%= message %>
</div>
<% end %>
Thanks for suggestions
Flash is designed as a way to pass data between (controller) actions (see documentation for more information). In your case, you specifically, do not want an action, so I don't think there is a way to do this.
You can however generate a message that looks like your other flash messages using Ajax. Here is one way to do it.
You will need the following components:
Remote call from modal
Add remote: true or :remote => true to your form within the modal. This tells Rails that you want an Ajax call to the server when the form is submitted, not a full page post.
Controller changes
You'll need to respond to formats other than HTML. Something like this:
def save
if !user_params.nil?
#email = Email.new(user_params)
#email.save_with_captcha
respond_to do |format|
format.html { redirect_to #email, notice: 'This should not happen.' }
format.json { render action: 'show' }
format.js { render action: 'show' }
end
end
end
Javascript to insert/clear "flash" error message in your modal
View file - show.js.erb (can be named differently but name should match action above).
$('#build_error').remove();
<% if #email.errors.any? %>
var build_error = "<div class='alert alert-danger' id='build_error'>";
build_error += "The form contains <%= pluralize(#email.errors.count, 'error') %>.";
build_error += "<ul id='error_explanation'>";
<% #email.errors.full_messages.each do |msg| %>
build_error += "<li><%= j msg %></li>";
<% end %>
build_error += "</ul>";
build_error += "</div>";
$(build_error).insertBefore( "#xxxxxx" );
<% else %>
$('#email-modal').modal_success();
<% end %>
Basically, you'll need to clear any previous "flash" error message (the first line) and then either display the new one (you'll need to find somewhere in the DOM to attach it - see xxxxxx) or close the modal. Pretty brute force but it works for me.
Hopefully you get the idea - I doubt any of this will work as is so you will need to understand the concept.

Categories

Resources