In my Home controller, I basically have a variable inside of def index: target = 13.5. I use this to do a lot of calculations which I send to my index.html view. My aim is to allow the user to input a value that directly updates this variable. I am using the below Stimulus controller, but want it so that if someone types in '14', the variable becomes target = 14, which my controller then updates the calculation and is updated to the user in real time.
In ajax_controller.js:
import { Controller } from "#hotwired/stimulus"
// Connects to data-controller="ajax"
export default class extends Controller {
static targets = ['form', 'output']
connect() {
console.log(this.formTarget);
console.log(this.outputTarget);
}
send(event) {
event.preventDefault();
fetch(this.formTarget.action, {
method: 'POST',
headers: { "Accept": "application/json" },
body: new FormData(this.formTarget)
})
.then(response => response.json())
.then(data => {
this.outputTarget.innerHTML = data.body.number;
});
}
}
In index.html.erb:
<div class="input-form" data-controller="ajax">
<%= form_with url: receive_path, data: { ajax_target: 'form', action: 'submit->ajax#send' } do |form| %>
<%= form.text_field :number %>
<%= form.submit "Submit" %>
<% end %>
<p data-ajax-target="output"></p>
</div>
In home_controller.rb:
def index
target = 13.5
#DO LOTS OF CALULATIONS HERE THAT EVENTUALLY OUTPUT TO A VIEW. I WOULD LIKE THE ABOVE TARGET UPDATED BY INPUT THAT THEN UPDATES THE CALCULATIONS
end
def receive
render json: { body: params }
end
Routes.rb:
root 'home#index'
post 'receive', to: 'home#receive'
Related
I have created a chat app with rails 6 and I'm using actionable along with stimulus in order to broadcast the messages and notifications in realtime. The setup I have thus far works just fine!
Now I'm trying to add a user is typing feature as well, but I'm having trouble implementing it! The ideas is to create a typing_channel with a is_typing method in order to broadcast the event and then show the output data via the received method inside the stimulus controller.
With all the previous implementations I call the broadcast action after the creation of a message and then I show the data via the received method inside the stimulus controller.
But for the is_typing feature the precess is different since I will not be creating a message this time.
So my main issue here is, how to trigger the broadcast event when I start typing inside the form??
class TypingChannel < ApplicationCable::Channel
def subscribed
stop_all_streams
stream_from "typing_channel"
end
def unsubscribed
stop_all_streams
end
def is_typing(data)
ActionCable.server.broadcast "typing_channel", typing: data['typing'], user: current_user
end
end
Th stimulus controller:
import { Controller } from "stimulus"
import consumer from "../channels/consumer"
export default class extends Controller {
static targets = ['status'];
connect() {
this.channel = consumer.subscriptions.create('TypingChannel', {
connected: this._connected.bind(this),
disconnected: this._disconnected.bind(this),
received: this._received.bind(this),
});
}
disconnect() {
consumer.subscriptions.remove(this.subscription)
}
_connected() {
}
_disconnected() {
}
_received() {
alert("receiving data")
}
}
And this is the form that I create the messages and I want to show when a user is typing feature:
<div data-controller="typing" data-target="typing.status" id="user_is_typing"></div>
<%= simple_form_for #message, url: consumer_create_conversation_message_path, html: { data: { action: "ajax:success->channel#clearMessage" }, method: :post, remote: true, id: :chat_form } do |f| %>
<%= f.input :body, label: false, placeholder: "Type your message here", :input_html => { id:"text_send", :rows => 2, class: "text_area_none" } %>
<%= button_tag type: 'submit', id:"button", class: "btn btn-primary btn-m fa-pull-right" do %><i class="fas fa-paper-plane"></i><% end %>
<% end %>
I have a form that has a partial like this:
parent_form.html.erb
<%= form_for :questions, url: { controller: 'questions', action: 'update' }, method: :put, authenticity_token: true, html: { id: 'questions_form' }, remote: true do |f| %>
<%= render :partial => 'question', :locals => {
:question => question,
:answers => question.answers
} %>
<%= hidden_field_tag :marked_questions_list, '', :id=>'marked_questions_list' %>
<%= hidden_field_tag :hidden_field_2, '', :id => 'hidden_field_2' %>
<button id="mark_question">Mark Question</button>
<%= f.submit 'Answer', :class => '', id: 'answer_btn' %>
_question.html.erb
<div><%= question.body %></div>
<table class="choices">
<% answers.each do |answer| %>
<tr class="answer_selection">
<td class="answer_input" data-update-url="<%= update_path %>" id="answer_input">
<%= radio_button_tag 'question[answer_id]',
answer.id,
false,
{
class: 'answer_option',
# onclick: "this.form.submit();"
onclick: "answerQuestion(#{answer.id});"
} %>
</td>
<td class="answer_text">
<%= label_tag 'question[answer_id]',
html_safe(answer.body_html) %>
</td>
</tr>
<% end %>
</table>
ajax.js
answerQuestion = async (answerId) => {
let updateUrl = document.getElementById('answer_input').dataset.updateUrl;
let marked_questions_list_value = document.getElementById('marked_questions_list').value;
let hidden_field_2_value = document.getElementById('hidden_field_2').value;
let data = { marked_questions_list: marked_questions_list_value, hidden_field_2: hidden_field_2_value }
try {
const resp = await fetch(updateAnswerUrl, {
method: 'POST',
credentials: 'include',
body: data,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
});
const response = await resp.json();
if (response.success) {
document.dispatchEvent(answerEvent);
} else {
alert('Oops !!! Something went wrong. Check Your Internet connection or try refreshing the page');
}
} catch (err) {
alert('Oops !!! Something went wrong. Check Your Internet connection or try refreshing the page');
}
};
Here if I click on answer_btn in the parent_form or if I use onclick: this.form.submit(); then the form is submitted along with all the required parameters in hidden_field_tag. How can I submit the form without refreshing the partial on clicking the radio_button through updateQuestion method along with the hidden_field_tag and other form parameters? Basically I want the functionality of answer_btn or onclick: this.form.submit(); without refreshing the page on clicking any radio button in partial so that I can remove the answer_button from the parent_form completely. Also there another button Mark Questions in the parent_form. On clicking this button, one of the hidden_field_tags is modified. The values of hidden_field_tags that are sent from parent_form to controller on clicking either of the buttons are changed from javascript. So they are dynamic. So hidden_field_tags should be present when I click the radio buttons in partial or any button in parent_form.
According to the rails nature, it will refresh, however, workaround for what you wanted is
After updating in the controller take a #marked_question_list and store marked question in it. Next store hidden_field_2 values in #hidden_field_2.
Create a update.js.erb
In that
$('#marked_questions_list').val(<%=#marked_question_list%>);
$('#hidden_field_2').val(<%=#hidden_field_2%>);
If I have understood your question correctly this supposed to solve your problem. Try this and let me know.
I have a form that renders partial 'form'.
= form_for(#booking, :as => :booking, :url => admin_bookings_path, :html => { :multipart => true }) do |f|
= render partial: "form", locals: { f: f }
Inside the partial form again another partial is rendered based on new_record?.
- if f.object.new_record?
#extra-products
= render partial: 'new_booking_extra_product', locals: { f: f }
- else
= render partial: 'extra_product', locals: { f: f }
In bookings#new, when the user selects particular car_id, I want to show associated products with it through ajax. For that I have used ajax to make a request to bookings#fetch_extra_product.
# Ajax request to fetch product
$('#booking_car_id').on('change', function(e){
var selectedCarId = $("#booking_car_id option:selected").val();
var url = "/admin/bookings/" + selectedCarId + "/fetch_extra_product";
$.ajax(url,{
type: 'GET',
success: function(msg){
},
error: function(msg) {
console.log("Error!!!");
}
});
});
# Bookings#fetch_extra_product
def fetch_extra_product
#car_id = params[:car_id] || Car.all.order('id desc').first.id
#extra_product = Car.find(#car_id).extra_products
respond_to do |format|
format.js
end
end
The fetch_extra_product.js.erb looks as follow:
$('#extra-products').html('$("<%= j render(:partial => 'new_booking_extra_product', :locals => {:f => f}) %>")');
But the form object (f) is undefined on this state. What's the best way to fetch the object or the best way to get through this problem?
You'll want to render a partial view with the associated products server-side when the Ajax request is received. Then you can send the HTML from that partial as part of your response and use the Ajax success callback to attach the partial view to the DOM however you want it. Then you can have (in your controller) code something like this:
def fetch_extra_product
# Unless you're using #car_id in a view or something, I'd just
# just use a local variable instead of an instance variable
#
car_id = params[:car_id] || Car.all.order('id desc').first.id
#extra_products = Car.find(car_id).extra_products
respond_to do |format|
format.js { render partial: 'extra_products_div', status: :ok }
end
Then your partial code is something like this:
<div class="extra-products">
<% #extra_products.each do |product| %>
<h2><%= product.name %></h2>
...
<% end %>
</div>
That way, your JavaScript can then go along the lines of:
$.ajax(url, {
type: 'GET',
success: function(msg) {
$('#foo').attach(msg);
},
error: function(msg) {
console.log('Error!!!');
}
});
One other comment: If your API is only going to get a request to this route via Ajax, you don't need the respond_to block. In that case you can just put the render partial: 'extra_products_div', status: :ok under the line where you
define #extra_products.
Using simple_form, I'm trying to get the entry of an id input (team_id) to autofill a couple of the other inputs (team_city and team_name) in a form creating a new team_game. I've gone through every implementation I can find, but to no avail (my most recent reference: Jquery ajax and controller method). This one is like quicksand for me, the more I struggle with, the farther I get pulled down. My code is below. Currently, its throwing the AJAX error in the javascript. I'm also getting this in the console:
Started GET "/team_games/populate_team_city_and_name?team_id=1" for ::1 at 2016-02-12 13:28:50 -0800
Processing by TeamGamesController#show as JSON
Parameters: {"team_id"=>"1", "id"=>"populate_team_city_and_name"}
TeamGame Load (0.3ms) SELECT "team_games".* FROM "team_games" WHERE "team_games"."id" = $1 LIMIT 1 [["id", 0]]
Completed 404 Not Found in 2ms (ActiveRecord: 0.3ms)
ActiveRecord::RecordNotFound - Couldn't find TeamGame with 'id'=populate_team_city_and_name:
So, in addition to my AJAX issue, I appear to be doing something wrong with my routing (used this as a guide: How to add a custom action to the controller in Rails 3).
Using Ruby 2.3.0 and Rails 4.2.5. Any help would be greatly appreciated.
models/team_game.rb
class TeamGame < ActiveRecord::Base
attr_accessor :team_city
attr_accessor :team_name
attr_accessor :opposing_team_city
attr_accessor :opposing_team_name
belongs_to :team, class_name: 'Team',
inverse_of: :team_games
end
models/team.rb
class Team < ActiveRecord::Base
has_many :team_games, class_name: 'TeamGame',
inverse_of: :team
end
view/team_games/_form.html.haml
= simple_form_for #team_game do |f|
= f.input :team_id, input_html: { id: 'team-id' }
= f.input :team_city, input_html: { id: 'team-city' }
= f.input :team_name, input_html: { id: 'team-name' }
controllers/team_games_controller.rb
def populate_team_city_and_name(team_id)
#team = Team.where(id: team_id)
respond_to do |format|
format.json { render json: #team }
end
end
private
def team_game_params
params.require(:team_game).
permit(:team_id,
:team_city,
:team_name,
:is_home_team,
:opposing_team_id,
:opposing_team_city,
:opposing_team_name,
:stadium_name
:game_date,
:game_time)
end
assets/javascripts/team_games.js
$(document).ready(function() {
$('#team-id').change(function(e) {
e.preventDefault();
var teamId = $('#team-id').val();
request = void 0;
request = $.ajax({
url: 'populate_team_city_and_name',
type: 'GET',
dataType: 'json',
data: { team_id: teamId }
});
request.done(function(data, textStatus, jqXHR) {
if (data.length > 0) {
$('#team-city').val(data.city);
$('#team-name').val(data.name);
} else {
$('#team-name').val('There is no team with entered Id');
}
console.log("Success!!")
});
request.error(function(jqXHR, textStatus, errorThrown) {
console.log("AJAX Error: #{textStatus}");
});
});
});
config/routes.rb
resources :team_games do
member do
get :populate_team_city_and_name
end
end
UPDATE: WORKING
Following are the changes I made to get the autofill to work. They may not all be necessary, but it works, so that's something. Thanks to Jeff F. for heading me down the right road.
view/team_games/_form.html.haml (revised)
= simple_form_for #team_game do |f|
= f.input :team_id, input_html: { id: 'team-id' }
#no-team-with-id-msg There is no team with entered id
= f.input :team_city, input_html: { id: 'team-city' }
= f.input :team_name, input_html: { id: 'team-name' }
controllers/team_games_controller.rb (revised)
def populate_team_city_and_name
#team = Team.where(id: params[:team_id])
respond_to do |format|
format.json { render json: #team }
end
end
private
def team_game_params
params.require(:team_game).
permit(:team_id,
:team_city,
:team_name,
:is_home_team,
:opposing_team_id,
:opposing_team_city,
:opposing_team_name,
:stadium_name
:game_date,
:game_time)
end
assets/javascripts/team_games.js (revised)
$(document).ready(function() {
$("#no-team-with-id-msg").hide();
$('#team-id').change(function(e) {
e.preventDefault();
var teamId = $('#team-id').val();
request = void 0;
request = $.ajax({
url: '/team_games/populate_team_city_and_name?team_id=' + teamId,
type: 'GET',
dataType: 'json'
});
request.done(function(data, textStatus, jqXHR) {
if (data.length > 0) {
$("#no-team-with-id-msg").hide();
$('#team-city').val(data[0].city);
$('#team-name').val(data[0].name);
} else {
$("#no-team-with-id-msg").show();
$('#team-city').val('');
$('#team-name').val('');
}
});
request.error(function(jqXHR, textStatus, errorThrown) {
console.log("AJAX Error: #{textStatus}");
});
});
});
config/routes.rb (revised)
get 'team_games/populate_team_city_and_name',
to: 'team_games#populate_team_city_and_name',
as: 'populate_team_city_and_name',
defaults: { format: 'json' }
resources :team_games
resources :teams
Part of my routing problem appears to have been that I previously had the 'get' route after the resources routes, which I'm guessing is why the javascript 'GET' was routing to the team_games#show action.
I also changed the handling of the message 'There is no team with entered id' since it seemed inappropriate for it to appear in an input.
You're using the url for that route incorrectly. Try changing your ajax to:
request = $.ajax({
url: 'team_games/' + teamId + '/populate_team_city_and_name',
type: 'GET',
dataType: 'json'
});
Then in your controller:
#team = Team.where(id: params[:id])
I edited the answer above a bit, but it was actually a little too far from correct, so I'm answering anew. Upvoted as it's on the right track, though.
Add a line to your form like:
= simple_form_for #team_game do |f|
= f.input :id, as: hidden, :input_html => { id: 'team-game-id', :value => #team_game.id }
= f.input :team_id, input_html: { id: 'team-id' }
= f.input :team_city, input_html: { id: 'team-city' }
= f.input :team_name, input_html: { id: 'team-name' }
Then in your JS:
var teamId = $('#team-id').val();
var teamGameId = $("#team-game-id").val();
{ ... }
request = $.ajax({
url: '/team_games/' + teamGameId + '/populate_team_city_and_name?team_id=' + teamId,
type: 'GET'
});
The reason for the query parameter is because you can't specify the teamId in the URL unless you make a route that allows you to. The membership is on the TeamGame model, not the team. Alternatively, you could specify a route like:
get '/team_games/:id/populate_team_city_and_name/:team_id' => 'team_games#populate_team_city_and_name'
But as-is, you don't have a matching route. However, the query parameter will show up in params.
Then in your controller:
#team = Team.find params[:team_id]
You may want to also ensure that this Team not only exists, but also that Team.team_games includes the TeamGame in question. But I'll leave that exercise to you!
I have a vote button, which simply displays the amount of votes and by clicking on it, a vote is automatically added. Now I would like to add an Ajax request, so that the page doesn't refresh. Unfortunately I have never used Ajax before and therefor have no idea how to use it with Rails. I tried going through a few tutorials, but nothing seems to help.
view:
<%= link_to vote_path(:the_id => Amplify.all.where(question_id: question.id)[0].id, :the_user_id => #current_user.id), :remote => true do %>
<button class="fn-col-2 fn-col-m-3 amplify">
<%= image_tag "logo-icon-white.png" %>
<p class="count"><%= Amplify.all.where(question_id: question.id)[0].users.count %></p>
</button>
<% end %>
controller:
def vote
#amplify = Amplify.find(params[:the_id])
#current_user = User.find(params[:the_user_id])
if #amplify.users.where(:id => #current_user.id).count != 0
#amplify.users.delete(#amplify.users.where(:id => #current_user.id).first)
else
#amplify.users << #current_user
end
#question = Question.where(id: #amplify.question_id)[0]
#amplify.update(:votes => #amplify.users.count)
#question.update_attributes(:votes => #amplify.votes)
redirect_to root_path
end
routes:
get '/vote' => "amplifies#vote", :as => "vote"
jQuery(document).ready(function($){
$('.amplify').on('click', function(){
var that = $(this);
$.ajax({
url: '/vote',
data: {id: 'your id'},
/**
* Response from your controller
*/
success: function(response) {
that.siblings('.count').first().text(response);
}
});
})
})
I like calling it like waldek does but with coffeescript.
However, in your example, you are using :remote => true which is the unobtrusive way
Basically you are then going into the controller where you will need a format.js
respond_to do |format|
format.html # if you are using html
format.js # this will call the .js.erb file
end
Then create a file vote.js.erb where you can then access your instance variables and write js
console.log('you are here');