How to add random button to form - javascript

I want to provide users a 'random' option so they can select a previous created date idea to use from the database (inside the letsgos table). There’s a “Let’s Go...” section that users can fill out a form and propose a date they would like to go on. There are going to be users who won’t be able to come up with a date idea on their own. So for those users who cannot create their own date I want to provide a ‘random’ button that with each click will insert a date (that’s from the database) into the form. The dates in the database from the letsgos table have content and tag assign to them. When a user clicks on random it should populate the form with the content and tag (each random click should show new data from the database). I don't have any javascript experience so I am not sure if I am doing it the right way.
/views/letsgos/_form.html.erb:
<%= form_for(#letsgo) do |f| %>
<div class="field">
<%= f.text_area :content, placeholder: "Propose new date..." %>
</div>
<%= f.select :tag, options_for_select( [["Select One", ""], "Eat/Drink", "Listen/Watch", "Play", "Explore", "Other"]) %>
Click here for a Random letsgo
<%= f.submit "Post" %>
<% end %>
/views/layouts/application.html.erb
<head>
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script>
$(document).ready(function() {
$('.ajax').click(function() {
$.get(this.href, function(response) {
console.log(response);
$('body').html(response);
});
});
});
<script>
</head>
letsgo controller:
def create
#letsgo = current_user.letsgos.build(letsgo_params)
if #letsgo.save
flash[:success] = "Date posted!"
redirect_to root_url
else
flash[:error] = "Date was not posted!"
redirect_to root_url
end
end
def destroy
#letsgo.destroy
redirect_to root_url
end
def random
#letsgo = Letsgo.random.first
if request.xhr?
end
end
private
def letsgo_params
params.require(:letsgo).permit(:content, :tag)
end
def correct_user
#letsgo = current_user.letsgos.find_by(id: params[:id])
redirect_to root_url if #letsgo.nil?
end
Caching columns migration:
rails g migration add_ids_count
def self.up
add_column :letsgos, :ids_count, :integer, :default => 0
Letsgo.reset_column_information
Letsgo.all.each do |l|
l.update_attribute :id_count, l.id.length
end
end
def self.down
remove_column :letsgos, :id_count
end
end

A creative solution to this would be to set up a caching column to store an array of IDs of Letsgo's, if you're worried about the performance on Antarr Byrd's suggestion. Basically, this would cache the Letsgo.pluck(:id) in a single column in the DB. (Maybe do this in a worker in a post-save and/or post-delete hook on Letsgos.) I'd recommend doing this in a buffer of some sort, or maybe as an hourly task.
You could then either pull this in as a JavaScript array (letsgos_ids_array in the example) and create a Math.random() value based on the length of that array and send it to the .find(). Of course you could also just output the array's length directly.
var item_index = Math.floor(Math.random() * letsgos_ids_array_length);
$.get("/letsgos/random", {
"ind" : item_index
}, function(data){
/* do something with the data */
});
Then, this index can be used to pull out the actual ID value from the array from the db.
letsgoarray = Letsgosarray.first # this is the single-column "cached" array of IDs
item_id = letsgosarray[params[:id_index]]
#random_letsgo = Letsgos.find(item_id)
format.json do {
render json: #random_letsgo
}
Array access is fast, and so is single db column querying.

Here you have some good read about random rows:
http://jan.kneschke.de/projects/mysql/order-by-rand/

I've never done this but you can probably do
def random
Letsgos.find(Letsgo.pluck(:id).sample)
end

Related

Rails 7 how to open table inside a row of another table

In my Rails 7 (with bootstrap) app I need to create a table with all users transaction that has on each row an arrow that when clicked on, expands that row inside which is a new table (that's what I think it is - a new table). I think the attached design will better explain what I mean:
To achieved that I could try javascript with something like this https://jsfiddle.net/Wfxpu/180/ but I'm not sure if it's a modern approach. I'm wondering is it possible to a write something like this without any JS code using turbo maybe? or even pure HTML ?
Here is what I was trying to do:
# transaction controller
class TransactionsController < ApplicationController
def index
response = client.transactions.list(platform_id: current_user.platform_id, page: 1, per_page: 100)
#transactions = response.body['data']
end
private
def client
#client ||= TestAPI::Client.new
end
end
so then sample table would be:
# views/transactions/index
<table>
<% #transactions.each do |transaction| %>
<tr><%= transaction.amount %></tr>
<% end %>
</table>
You could try using a remote link_to as your "arrow" to invoke show_more action.
<%= link_to your_path, remote: true do %>
your image
<% end %>
In your controller you would need to respond to that with
def show_more
...
#data_to_show = some_database_call
respond_to do |format|
format.js
...
end
end
then in show_more.js.erb you can run javascript using the data that you just received.
var content = document.querySelector("#hidden-section-1");
content.innerHTML = ""
content.insertAdjacentHTML("beforeend",
"<%= j render('layouts/your_template', data: #data_to_show) %>");
where layouts/your_template would be a template with all the stuff you want to show(the "other" table you mentioned)
<h1> merchant: <%= data.merchant %> <h1>
...
<p> transaction_id: <%= data.transaction_id %> </p>
...

The jquery code is not executed after a renderJS

(Sorry for my english )
I realized 3 tabs that dynamically reload my activities, a GET request is made in order to display a specific list of activities (activity of a group / of the user / news)
However I have a jquery code that no longer runs when I click on one of these tabs,
With rails I return a collection to display the activities, a solution remains to put the jquery in this collection, but when I do this the jquery is executed for each activities loaded and is not that i want.
Activities :
<div class="activity-card-container" id="activity-cards">
<%= render partial: 'activities/activity', collection: #activities %>
</div>
$('.newness').on('mouseenter', function (event) {
event.preventDefault();
$(this).toggleClass('d-none');
let num = parseInt($.trim($('.body__activity_counter').html()));
$('.body__activity_counter').html(--num);
if (num == 0) {
$('.body__activity_counter').addClass('hover');
}
newnessId = $(this).data('id');
$.ajax({url: '/user_activities.json', method: 'POST', data: {user_activity: {activity_id: newnessId}}})
});
Partial Activity :
<div class="activity--js margin-card activity-card card card-body">
.......
<%= activity.title %>
<%= activity.content %>
.......
</div>
One of the Tab controller :
def fetch_activities
user = current_user
activities = []
user.groups.each do |group|
activities << Activity.where(group: group)
end
#all_activities = activities.flatten.sort_by(&:updated_at).reverse
respond_to do |format|
format.js
end
end
and his route :
get "/fetch_activities" => 'activities#fetch_activities', as: 'fetch_activities'
Thanks

Save two models (which belong_to a third model) with one submit?

When the user clicks submit how can the info from two different models/DB tables be passed?
The user should be able to create a note in the missed_dates form and then that note should be saved to the respective #challenge the missed date is referring to.
missed_dates/form.html.erb
<%= simple_form_for(#missed_date, url: challenge_missed_dates_path({ routine_id: #challenge }), remote: request.xhr?, html: { data: { modal: true } }) do |a| %>
<%= form_for [#notable, #note] do |b| %>
<%= a.text_field :one %>
<%= b.text_field :two %>
<%= button_tag(type: 'submit') do %>
Save
<% end %>
<% end %>
<% end %>
missed_date.rb
class MissedDate < ActiveRecord::Base
belongs_to :user
belongs_to :challenge
end
missed_date_controller
def new
#challenge = current_user.challenges.find(params[:challenge_id])
#missed_date = current_user.missed_dates.build
#notable = #challenge
#note = Note.new
end
def create
challenge = current_user.challenges.find(params[:challenge_id])
challenge.missed_days = challenge.missed_days + 1
challenge.save
#missed_date = challenge.missed_dates.build(missed_date_params)
#missed_date.user = self.current_user
#missed_date.save
respond_modal_with #missed_date, location: root_path
flash[:alert] = 'Strike added'
end
Short: use "belongs_to" and "has_many :through" association between Note and MissedDates. Then you can use nested attributes.
Long version: This in probably an issue of an improper or incomplete structure of your models. Usually, you can use nested attributes (see http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html) to achieve this.
But, this implies that the models have a direct relation. You should consider if you can do a belongs_to/has_many relation between the note and the missed_date model. This could be done e.g. by "has_many :through..." (http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association) without changing your current db scheme.

How can I update an instance variable with each ajax request?

I have a long block of comments on a view of model Page. Instead of showing all the comments on page load, I'm trying to create a "view more" button that shows the next ten comments. The button sends an ajax request to the controller, which then renders this block using jquery:
_view_more.html.erb
<% comments.each_with_index do |comment, index|%>
<% if (( index > #start_number) && (index < #end_number) ) %>
<%= comment.text %>
<% end %>
Let's say I always want to show the next 10 comments. I would just set #start_number = #start_number + 10 and #end_number = #end_number + 10
in the controller, but instance variables get reset, so #start_number would be nil. How can I set a variable that increases by 10 upon every ajax request?
"view more" button
<%= link_to "view more", view_more_page_path, remote: true %>
pages_controller.rb
def view_more
#page = Page.find(params[:id])
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
view_more
$("#comments-body").append("<%= escape_javascript(render 'view_more') %>");
I will use haml and coffee-script
When rendering comments you put an html5 data attribute with the id of the comment:
#view where to render comments
%div#comments-wrapper{:"data-pageid" => #page.id}
=render #comments
=link_to "View more", "#", id: "view-more-link"
The comment partial
#comments/_comment.html.haml
%p.single-comment{:"data-commentid" => comment.id}
=comment.body
application.coffee
$ ->
$("#view-more-link").on 'click', ->
last_comment_id = $("#comments-wrapper .single-comment").last().data('commentid')
page_id = $("#comments-wrapper").data("pageid")
$.ajax
url: "/comments/view_more"
dataType: "script"
data:
last_comment_id: last_comment_id
page_id: page_id
comments_controller
def view_more
#page = Page.find(params[:pageid])
if params[:last_comment_id]
#comments = #page.comments.where("comments.id > ?", params[:last_comment_id]).limit(10)
end
respond_to do |format|
format.js
end
end
comments/view_more.js.erb
$("#comments-wrapper").append("<%= escape_javascript(render #comments) %>");
Note: I don't how your routes were set up so I put the page.id as a data-attribute as well
I would use already implemented pagination gems kaminari or will_paginate. I'll create this example using will_paginate.
First of all, it's important to say that your approach is incorrect, because it loads all comments every view_more request. If you want to show 10 comments, makes sense select only they from database, right? The pagination gem will do it for you!
Let's to the code:
"view more" button
<%= link_to "view more", view_more_page_path, remote: true, id: 'view-more-btn' %>
pages_controller.rb
def view_more
#comments = Page.find(params[:id]).comments.paginate(page: params[:page], per_page: 10)
respond_to do |format|
format.html { redirect_to root_path }
format.js
end
end
_view_more.html.erb
<% #comments.each do |comment| %>
<%= comment.text %>
<% end %>
view_more.js.erb
$("#comments-wrapper").append("<%= escape_javascript(render 'view_more') %>");
# We need to update the 'view more' link with the next page number
$('#view-more-btn').attr('href', '<%= view_more_page_path((params[:page] || 0) + 1) %>')
is it not good to update an hidden variable before making ajax call with the count..?
var currentVal = parseInt($("[type=hidden]").val());
$("[type=hidden]").val( currentVal + 1 );
add the hidden field right at the begining of the comments section with default value as "0"
<input type="hidden" name="hiddenId" id="hiddenId" value="0">
Hope it will help
If you want a quick and dirty approach, you could save start_number and end_number inside a cookie.
But keeping track of what needs to be rendered next on client side would be a right thing to do.

Rails Updating a DIV (with reload from model) after a javascript call

OK, I have a page which shows the customer how many Widgets he has. Here's the view (haml):
#available
= "Available widgets: #{#customer.widgets.unused.count()}"
("unused" is a scope in the model showing the available widgets).
When Customer redeems Widgets with a form with ":remote => true", some javascript places a nice DIV on the page with animation and the model is updated by the controller.
Here's the controller:
def redeem
#customer = Customer.find(params[:customer_id])
number = params[:amount].to_i
unless #customer.widgets.unused.empty?
number.times do
#customer = Customer.find(params[:customer_id])
widget = #customer.widgets.unused.first # Grab first unused pass
widget.status = "Redeemed"
widget.save!
end
else
#pay = "true"
# customer.widgets.new
end
# redirect_to #customer
end
And here's the javascript (js.erb):
var number = <%= params[:amount] %>;
<% if #pay.eql? "true" %>
$("#widget-pay").modal('toggle');
<% else %>
while (number > 0) {
var item = $('<div class="widget-show">...</div>');
$('#allwidgets').isotope('insert', item);
number --;
}
<% end %>
My problem is I now want to update the "#available" DIV with the new Widget count. How do I do this?
At worst I could reload the page so the data is pulled from the model again, at best just update the DIV. Neither which I seem to be able to do from the javascript.
You can do something like this:
render :js => "$('#available').append(widget)"
widget.save!

Categories

Resources