Ruby on Rails. Load dynamic partial layouts with share data - javascript

I'm novice to Ruby and Ruby on Rails
I'm trying to load templates dynamically with shared data
I have a quizz (named game in my code) and I will add some questions to the game and some answers to these questions dynamically.
In my games/index.html.erb I have
<div class="form-group game">
<%= form.label(':name', "Quizz title") %>
<%= form.text_field ':name', class: 'form-control form-control-sm' %>
</div>
<div class="questions"></div>
<div class="row form-actions">
<div class="col-sm">
<%= link_to 'Add a question', add_partial_question_games_path, remote: true, "data-turbolinks": false, class: "btn btn-secondary" %>
</div>
<div class="col-sm text-right">
<%= form.submit 'Save', :class => "btn btn-primary" %>
</div>
</div>
When I click on 'add a question' link I append the partial layout of question which contains a link to append the answer partial layout. So I have datas on this order:
QUIZZ
Question 1
Answer 1.1
Answer 1.2
Add answer link
Question 2
Answer 2.1
Answer 2.2
Answer 2.3
Answer 2.4
Add an answer link
Add a question link
SAVE THE QUIZZ
my controller remote actions are:
def add_partial_question
respond_to do |format|
format.js
end
end
def add_partial_answer
respond_to do |format|
format.js
end
end
and the corresponding *.js.erb files are:
// add_partial_question.js.erb
$(document).ready(function() {
$('.questions').append("<%= escape_javascript(render :partial => 'games/partials/question') %>");
});
// add_partial_answer.js.erb
$(document).ready(function() {
$('.answers').append("<%= escape_javascript(render :partial => 'games/partials/answer', :locals => { :question_key => question_key }) %>");
});
my 'partials/_question.html.erb' contains
<div class="row">
<div class="col-sm">
<%= link_to 'Add an answer', add_partial_answer_games_path, remote: true, "data-turbolinks": false, class: "btn btn-secondary" %>
</div>
</div>
and the partials/_answer.html.erb:
<div class="row">
<div class="col">
<%= text_field_tag "game[questions][#{question_key}][answers][#{answer_key}][text]", 'Your answer', class: "form-control" %>
</div>
</div>
I don't any idea how I shall init, passe and increment the question_key and answer_key from controller remote actions to these partial views. Which is the best way ?
When I try to pass any variables from remote actions to the *.js.erb and then to pass them to the views it doesn't work.
I've tried to do something like this:
respond_to do |format|
format.js { render :locals => { :question_key => params[:question_key].to_i + 1 } }
end
it doesn't work.
Any ideas ?
Thank you very mutch

There is a little bit of mess in your question, for example in your // add_partial_answer.js.erb you used variable question_key which isn't define, your controller isn't RESTful etc. So instead of checking your code step by step I am dropping here link for a great railscast where you will find answer. I am hoping it will help with your problem.

Related

Invoke Flash Notice in Rails using [action].js.erb

I am trying to get the rails flash notice to appear within my application by using JavaScript only. I have a form which leverages remote: true to make an AJAX request to the controller when submitting. The form looks like the example shown below:
# app/views/_email_register.html.erb
<%= simple_form_for :email, url: emails_path, remote: true do |f| %>
<div class="modal-body">
<div class="form-group">
<%= f.input :address, as: :email, placeholder: 'user#domain.com', label: false %>
</div>
</div>
<div class="modal-footer">
<%= f.submit 'Subscribe', class: 'btn btn-primary btn-block' %>
</div>
<% end %>
The rails application was generated using rails-composer which also installed bootstrap css.
# app/controllers/emails_controller.rb
def create
#email = Email.new(email_params)
respond_to do |format|
if #email.save
format.html { redirect_to :back, notice: 'Email was successfully registerd' }
format.js
else
format.html { render :new }
format.js
end
end
end
The controller then invokes the create.js.erb as part of the respond_to block.
# app/views/emails/create.js.erb
// flash to user it was successful
$(".alert").html("Email was successfully registered");
The _messages.html.erb partial was automatically generated from rails-composer. Not sure if this partial can be invoked from the JavaScript
# app/views/layouts/_messages.html.erb
<% flash.each do |name, msg| %>
<% if msg.is_a?(String) %>
<div class="alert alert-<%= name.to_s == 'notice' ? 'success' : 'danger' %>">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
<%= content_tag :div, msg, :id => "flash_#{name}" %>
</div>
<% end %>
<% end %>
Any assistance on how I can get the flash notice to show up via JavaScript would be much appreciated.
You just need to re-render the partial which renders the flash messages in your js.erb
# app/views/emails/create.js.erb
$('.flash_wrapper').html("<%= escape_javascript(render 'layouts/messages') %>");
In your controller set the flash
format.js { flash.now[:notice] = 'Email was successfully registered' }
This will show you flash
NOTE: .flash_wrapper is your wrapper where you are rendering the _message partial.

Javascript not executing after Rails redirect

My application contains a very simple login and two separate AJAX calls. I have a loading spinner that is hidden automatically whenever a page is ready it is contained in index.js
$(function() {
$("loading").hide();
$("#scan_button").on("ajax:success", function(e, data, status, xhr) {
$("#standard_results").hide();
$("#results").show();
$("#results").html(data);
});
$(document).ajaxStop(function() {
$("#loading").hide();
});
$(document).ajaxStart(function() {
$('#loading').show();
});
});
When a user logs in the following happens
class SessionController < ApplicationController
def create
user = User.find_by(username: params[:session][:username])
if(user.password == params[:session][:password])
log_in user
redirect_to root_path
end
end
def destroy
logout
redirect_to root_path
end
end
The route path takes me back to 'home#index' for which the index.js file exists. When the user logs in the redirect happens and the loading spinner is hidden and all JavaScript executes as expected.
When the user hits log out however the redirect occurs, but the loading spinner is shown and no JavaScript ever gets executed. Here is my index.html.erb file that contains the JavaScript.
<% content_for :title, "Network Monitor" %>
<div class="container-fluid">
<nav class="navbar narvbar-dark bg-inverse">
<span class="navbar-brand">Network Monitor</span>
<%= link_to "Scan", scan_path, remote: true, class: "btn btn-secondary", id: "scan_button" %>
<% if logged_in? %>
<span class="pull-xs-right"><%= link_to "Logout", logout_path, class: "btn btn-secondary" %></span>
<% else %>
<%= form_for(:session, url: login_path, :html => {:class => 'form-inline pull-xs-right'}) do |f| %>
<%= f.text_field :username, class: 'form-control', placeholder: "Username" %>
<%= f.password_field :password, class: 'form-control', placeholder: "Password" %>
<%= f.submit "Log in", class: 'btn btn-primary', id: 'login_button' %>
<% end %>
<% end %>
</nav>
<div id="loading">
<%= image_tag "loading.gif", class: "loading_gif" %>
</div>
<div class="row">
<div class="col-md-12">
<div id="scan_bar">
<h3>Welcome to your network monitor!</h3> <br />
<p>To get started hit the scan button in the top right corner, this will detect the computers that are alive on your network and show them below.</p>
</div>
</div>
</div>
<div class="row">
<div id="results">
</div>
<div id="standard_results">
</div>
Solved. It was as simple as using
$(document).on('turbolinks:load', function() {});
Instead of what I had posted, this seems to be the way that Rails 5 and turbolinks want you to use, and it worked.
According to the
turbolinks document, I use the event page:change to solve the problem:
$(document).on("page:change", function(){...});
It works for me.
In case others run into this. If you are using an href or link_to try doing something like this:
<li><%= link_to "Home", jobs_path, method: :get %></li>
...where you specify the get method in the request.

remove comment from view using AJAX rails

I have a directory app that has comments. I add and delete comments with remote: true and I want to fade them out in my view with Jquery.
How do I go about getting a reference to the comment I want to fade out in the view? I can delete the comment just fine, but I don't know how to fade the comment out in my view
Here is my controller:
class CommentsController < ApplicationController
def create
business = Business.find(params[:business_id])
new_comment = business.comments.create
new_comment.comment = params[:comment_text]
new_comment.save
respond_to do |format|
format.html {redirect_to business}
format.js
end
end
def destroy
business = Business.find(params[:business])
comment = Comment.find(params[:id])
comment.destroy!
params[:id] = business.id
respond_to do |format|
format.html {redirect_to business_url(id: params[:id])}
format.js
end
end
end
Comments are rendered in my business view like this:
<aside class="comment-section">
<h4 class="thin-grey-underline"><span class="glyphicon glyphicon-comment glyphicon-left-space"></span> Reviews </h4>
<% if !#business.comments.empty? %>
<% #business.comments.each do |comment| %>
<div id="comments">
<%= render partial: 'comment', locals: {comment: comment, business: #business} %>
</div>
<% end %>
<% else %>
<p> No reviews yet... </p>
<% end %>
<%= render partial: 'comments_form' %>
</aside>
my single comment partial:
<div class="row single-comment vertical-align">
<div class="col-sm-12 col-md-3">
<div class="profile-image" style="background: url('<%= business.profile_image_url(:thumb).to_s %>') no-repeat; background-position:center"></div>
</div>
<div class="col-sm-12 col-md-9">
<h5><%= comment.title %></h5>
<p><%= comment.comment %></p>
<%= button_to(business_comment_path(#business.id,comment.id), {method: :delete, remote: true, class:"btn btn-danger delete-comment-button pull-right", params:{id: comment.id, business: business.id}} ) do%>
<i class="glyphicon glyphicon-trash"></i>
<% end %>
</div>
</div>
the form partial to create a new comment:
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="comments-form">
<%= form_tag(business_comments_path(#business), html: { id: "comment-form" }, method: 'post', remote: true) do %>
<h5 class="thin-grey-underline"><span class="glyphicon glyphicon-pencil glyphicon-left-space"></span> Write a Review </h5>
<%= text_area_tag(:comment_text,nil, class: "comment-form-input")%>
<%= hidden_field_tag 'business_id', params[:id] %>
<%= submit_tag("Comment", class:"btn btn-primary") %>
<% end %>
</div>
</div>
</div>
If I have a list of comments, how do I delete a specific comment by clicking on the delete link and also have it removed from my view in a fadeout manner?
I have a destroy.js.erb file that gets called, but I don't know how to get a reference to fade out the specific comment.. not sure if I am explaining myself clearly here
You could try something like this:
In your comment partial:
<div id="comment_<%= comment.id %>" class="row single-comment vertical-align">
destroy.js.erb:
$('#comment_<%= #comment.id %>').remove();
Knockoutjs can do that The "if" binding
First step is to make sure that the thing you want to delete has a unique id, and that this id is easy to generate. I patched AR::Base with an instance method called dom_id for this, which is like so:
def dom_id
"#{self.class.name.underscore}-#{self.id}"
end
which means if i'm rendering out a list of Foo objects i can do something like
<%= render :partial => "foos/foo", :collection => #foos %>
and in the partial do this
<li id="<%= foo.dom_id %>">
....
</li>
and the id will be something like "foo-123".
Now, in my destroy.js.erb action, after deleting a foo object, i know that it's dom id on the page can be got with #foo.dom_id. To fade it out and delete it i would then do
page << "$('##{#foo.dom_id}').fadeOut('slow', function(){$(this).remove();});"
You can use helper dom_id method (docs):
Change <div id="comments"> to <div id="<%= dom_id comment %>"> or add id attribute to div.single-comment
In js response do $('#<%= dom_id #comment %>').fadeOut();
Another option is to bind to jQuery event like this:
$('body').on('ajax:success', '.delete-comment-button', function(event) {
$(event.original_target).closest('.single-comment').fadeOut();
})
Then you can just return status 2XX for the delete request

How to make items on a rails form sortable

Using Rails 4 and Ruby 1.9.3. Styling is handled by an internally developed gem based on bootstrap 3.
I've been looking around for answers to this and have found a number of different examples that show how to do this with a basic list in a view. The railscast is one such example I've looked at.
http://railscasts.com/episodes/147-sortable-lists
However, I am trying to achieve this in a rails nested form using a partial but having no success. Sadly I am new to ruby, rails and have no prior knowledge of javascript so this is all a step learning curve.
My service model relates to a places model through service places. The service_places.position field holds the order of the stops.
My nested form (_form.html.erb) for services is shown below:
<!-- Adds the Service_Places (stops) associations via partial (sort applied in model) -->
<div>
<div class="links" id="sortable">
<%= link_to_add_association 'Add Stop', f, :service_places, :class => "btn btn-default", :data => {"association-insertion-method" => "after" } %>
</div>
<%= f.fields_for :service_places do |service_places| %>
<%= render 'service_place_fields', :f => service_places %>
<% end %>
</div>
My service_place partial is shown below:
<!-- This will hold the partial form for service places -->
<div class="nested-fields">
<%= f.label :service_place, "Stops", :class=>"col-sm-2 control-label" %>
<div class="col-sm-1">
<%= f.text_field :position, :class=> "form-control", :placeholder => "Position" %>
</div>
<div class="col-sm-3">
<%= f.collection_select :place_id, Place.where('active = true').order(:place_name), :id, :place_name, :prompt => "Select Place" %>
</div>
<div>
<%= link_to_remove_association "Remove Stop", f, :class => "btn btn-default" %>
</div>
</div>
I started looking at trying to assign an ID to each of the service_place partials DIV tags but couldn't get it to work.
The questions I would like to know are:
1) Is it possible to allow user to reorder items within forms? and save the new order in the server?
2) If it is possible could someone give me a hint on how to go about doing this.
Thanks in advance for taking time to look at this post.
My colleague showed me a way to achieve this using the "acts as list" gem.
His blog can be found here:
http://nicholshayes.co.uk/blog/?p=344
To get this working I made the amendments as shown below.
1) In my service model I added these three methods:
# Used in the positioning of service places using the acts_as_list gem
def method_missing(symbol, *args, &block)
if acts_as_list_method?(symbol)
pass_method_to_service_service_place(symbol, args.first)
else
super
end
end
# Used in the positioning of service places using the acts_as_list gem
def pass_method_to_service_service_place(symbol, service_place)
raise "A Service_Place is needed for re-ordering places" unless service_place.kind_of? ServicePlace
service_place.send(symbol) if service_place
end
# Used in the positioning of service places using the acts_as_list gem
def acts_as_list_method?(symbol)
ActiveRecord::Acts::List::InstanceMethods.instance_methods.include?(symbol.to_sym)
end
2) Added route's
resources :services, :concerns => :paginatable, only: [:create, :destroy, :edit, :index, :new, :show, :update] do
# Required for the re-ordering of the routes
resources :service_places do
member do
get :move_up
get :move_down
end
end
end
3) Amend the SERVICE show.html
<% #service.service_places.reorder("service_places.position asc").each do |serviceplace| %>
<dd>
<% if user_signed_in? %>
<% if #service.first?(serviceplace) %>
<%= link_to('', move_up_service_service_place_path(serviceplace, service_id: #service), class: "btn btn-default btn-xs glyphicon glyphicon-arrow-up invisible") %>
<% else %>
<%= link_to('', move_up_service_service_place_path(serviceplace, service_id: #service), class: "btn btn-default btn-xs glyphicon glyphicon-arrow-up") %>
<% end %>
<% if #service.last?(serviceplace) %>
<%= link_to('', move_down_service_service_place_path(serviceplace, service_id: #service), class: "btn btn-default btn-xs glyphicon glyphicon-arrow-down invisible") %>
<% else %>
<%= link_to('', move_down_service_service_place_path(serviceplace, service_id: #service), class: "btn btn-default btn-xs glyphicon glyphicon-arrow-down") %>
<% end %>
<% end %>
<!-- Unfortunately this is not very efficient way of showing the place because it calls the db for each item. -->
<%= Place.find(serviceplace.place_id).place_name %>
</dd>
<% end %>

Form Jquery Functions

I am trying to show div by clicking on a button form. I know its wrong just not to sure what to write, and should i write it on my javascript page or just in the view.
<%= submit_tag "Next Step", :type => 'button', :onclick => '$('#devregothinf').hide();' %>
<div id="devregothinf">
<h2>General Information</h2>
<%= render 'geninfor_step', form: f %>
</div>
i am getting an error as follow
syntax error, unexpected $end, expecting ')'
Write as following:
<span onclick = "$('#devregothinf').slideToggle('slow');"> Next Step </span>
<div id="devregothinf" style="display:none;">
<h2>General Information</h2>
<%= render 'geninfor_step', form: f %>
</div>
If you're trying to get it to show and not hide, you just need to change $('#devregothinf').hide(); to $('#devregothinf').show();
You could do something like this
view (app/views/home.html.erb)
<%= link_to 'Next Step', 'home/next_step', :remote => true %>
<div id="devregothinf">
controller (app/controllers/home_controller.rb)
def next_step
<your code>
respond_to do |format|
format.js
end
end
*view (js.erb) (app/views/next_step.html.erb)*
$("#devregothinf").html("<%= raw escape_javascript(render(:partial => 'form')) %>")
*view (app/views/_form.html.erb)*
Your form partial
HTH

Categories