I have a vote model with "like", "dislike" actions. I have a route for each of these actions. When I call the actions, I'm returning a json response. My problem is that first, I need to figure out how to send the query to my like/dislike actions. I need to access ruby/rails variables from my javascript (I'm sending an ajax request using jquery's $.getJSON), so that for example I can create the request for the correct item. Help much appreciated.
A popular technique is to attach data to the dom. For example (pseudocode follows):
<% items.each do |item| %>
<div class="like_button" data-item-id="<%= item.id %>">Like</div>
<% end %>
and in the JavaScript:
$(".like_button").on("click", function() {
var item_id = $(this).data('item-id'); // from the dom
// construct Ajax request for item_id
});
Related
I have a form I want to submit automatically whenever any input field is changed. I am using Turbo Streams, and if I use onchange: "this.form.submit()" it isn't captured by Turbo Streams and Rails uses a standard HTML response. It works fine when clicking the submit button. How can I work around this?
There is a discussion on the hotwire forum, where Mark Godwin figured out why form.submit() isn't working with turbo:
Turbo intercepts form submission events, but weirdly, the JS formElement.submit() method does not trigger the submit event.
And Jacob Daddario figures out that you can use form.requestSubmit() instead:
It turns out that the turbo-stream mechanism listens for form submission events, and for some reason the submit() function does not emit a form submission event. That means that it’ll bring back a normal HTML response. That said, it looks like there’s another method, requestSubmit() which does issue a submit event.
So you can change your code slightly, and use requestSubmit() if a browser supports it, and use submit() if not:
onchange: "this.form.requestSubmit ? this.form.requestSubmit() : this.form.submit()"
Update:
As BenKoshy pointed out, in Turbo 7.1.0, a polyfill was added so you can use form.requestSubmit() without checking for browser support, so you can add this to your input field:
onchange: "this.form.requestSubmit()"
I need to implement this for an app with lots of forms. I wound up using Stimulus. Below is the whole controller:
import { Controller } from "stimulus"
const _ = require("lodash")
export default class extends Controller {
connect() {
let that = this;
that.element.addEventListener('change', _.debounce(that.handleChange, 500))
}
handleChange(event) {
event.preventDefault()
// event.target.name // => "user[answer]"
// event.target.value // => <user input string>
event.target.form.requestSubmit()
}
}
and here it's used in a form with a single text input. NOTE the controller is attached to the form, not to the inputs.
<%= turbo_frame_tag dom_id(form_model) do %>
<%= form_with model: form_model,
format: :turbo_stream,
html: { data: { controller: "buttonless-form" } } do |f| %>
<%= f.hidden_field :question_id, value: question.id %>
<%= f.text_field :answer_value, class: "input shadow wide", placeholder: "Enter your answer here" %>
<% end %>
<div id=<%= "question_#{question.id}_output" %>>
<p> <!-- feedback to the user shows up here via Turbo -->
</div>
<% end %> <!-- end turbo frame -->
I would like to automatically click the submit button of an Ajax enabled form, so that the user does not have to click the button (but can optionally).
Right now, I'm working on the first boundary, which is to call the form from Javascript, so that at the very least, once i build my timer, I will have this part figured out.
I've tried many ways to do this, and NONE work. Please keep in mind that this is an ASP.NET MVC 4 Mobile application (which uses jquery.mobile) but I do have the jquery.mobile ajax disabled so that my button works at all (creating manual ajax based forms with updating divs, does not work in a jquery.mobile app because it hooks on the submit of all ajax forms).
So my current button works fine, I just can't seem to fire it programmatically.
I have my form:
<% using (Ajax.BeginForm("SendLocation", null, new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "result", HttpMethod = "POST" }, new { #id = "locationForm" }))
{ %>
<ul data-role="listview" data-inset="true">
<li data-role="list-divider">Navigation</li>
<li><%: Html.ActionLink("About", "About", "Home")%></li>
<li><%: Html.ActionLink("Support", "Support", "Home")%></li>
<li data-role="list-divider">Location</li>
<%: Html.HiddenFor(model => model.GPSLongitude)%>
<%: Html.HiddenFor(model => model.GPSLatitude)%>
<li><input type="submit" id="submitButton" value="Send" /></li>
</ul>
<% } %>
I have tried to do this in javascript:
$.ajax({
type: "POST",
url: action,
success: function () {
alert('success');
}
});
And I do get the server code firing that normally would. However, the DIV is not updated and also, the model was not intact either (it existed with all internal values null, so i assume newly instantiated).
I have also tried different ways to fire the form:
var form = $('#locationForm', $('#myForm'));
if (form == null) {
alert('could not find form');
} else {
alert('firigin on form');
form.submit(function (event) { eval($(this).attr("onsubmit")); return false; });
form.submit();
}
This did not work either:
var f = $('#locationForm', $('#myForm'));
var action = f.attr("action");
var data = f.attr("data");
$.post(action, data, function() { alert('back'); });
Which were all ways to do this that I found throughout the web.
None of them worked to fire the form and have it work the way it would normally as if a user had pressed the submit button themselves. Of course, once this fails, if I hit my submit button, it works perfectly...
Using Chrome Developer Tools, I found that the $.ajax call needs to have valid data before it will even attempt to function.
I was getting a silent Internal 500 Error on the post. But of course because of AJAX it was silent and the controller was not firing because it didn't get past IIS.
So I found out that the data I was sending, saying its JSON, was not and the .serialize() does not use JSON formatting. I tried to incorporate the JSON Javascript libraries to convert the object into JavaScript, however, this does not work either, because the Data Model object (or the form object) seems to not be compatible with those libraries. I would get errors in the JavaScript console and those libraries would crash when trying.
I decided to actually just pass the object I want manually:
var encoded = '{ GPSLongitude: ' + $('#GPSLongitude', $('#myForm')).val() + ',GPSLatitude: ' + $('#GPSLatitude', $('#myForm')).val() + '}';
Which passed the hidden fields i wanted to send (GPS LON/LAT) to the controller, and the model was intact in the controller call!
Now, for anyone that is reading this answer. the actual AJAX update process that is supposed to update the view, failed to work. Although for my purpose, I did not actually need the view to update correctly. Eventhough a partial view is returned, the special AJAX call seems to break the linkage between the form's div to update.
However, since the data was passed to the controller intact, this basically passed the GPS data that I needed to the server which was my ultimate goal.
make sure you are including the proper js libraries.
you need. jquery.js, jquery.unobtrusive-ajax.js
make sure unobtrusivejavascriptenabled = true in the web.confg
<appSettings>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
please try $('#locationForm').submit();
does it give error message?
if you're using i.e. you can look use the develper tools to look at network traffic to make sure nothing is sent.
I am using Rails + Backbone + Faye to make a sample chat application.
I'm currently able to use Faye's messaging capabilities to write to the DOM on a create event, though I'm not actually instantiating a backbone model. Ala Ryan Bates' tutorial I'm just calling inside of
create.js.erb
<% broadcast "/messages/new" do %>
$("#chats-table").append("<%= escape_javascript render :partial => "chat", :locals => { :chat => #chat } %>");
<% end %>
And publishing it in another javascript:
faye.subscribe("/messages/new", function(data) {
eval(data);
});
I'd like to refactor this a bit and leverage backbone's models. A good use case would be the delete method.
My chat model is bound to a click event, delete which calls:
model.destroy();
this.remove();
Backbone will call the delete method and put a delete request to /entity/id
That also dispatches rails' /views/delete.js.erb'.
In there I call a helper method which publishes a message with Ruby code.
<% broadcast "/messages/delete" do %>
<%= #chat.to_json.html_safe; %>
<% end %>
listener
var faye = new Faye.Client('http://0.0.0.0:9292/faye');
faye.subscribe("/messages/delete", function(data) {
});
Here, I was wondering if i could instantiate the deleted backbone model somehow so I could push that event onto everybody's screen and remove it from the DOM. Basically, I would like to call this.remove(); inside the faye client instead of in the chat model. Is this even possible?
Well, you should do remove on the model and let the UI listen for the event and refresh itself. Once the UI reflects the model changes you are golden.
The problem you have here is that Backbone collections/models are not an identity map. So the model object you are dealing with in the view is not the same you will instantiate and remove from the faye callback. If your messages collection is globally accessible, then I suggest you get the instance from there are remove it.
So I have a simple ajax call to a page:
= link_to 'click me', my_path, :onclick => "if ($('#go').val() == "ok") { alert('going'); } else { return false; }", :remote => true do
This works just fine, I see the alert only when my field with id "go" has ok in there... but the issue is that the remote action triggers every time no matter what.
If this was not a remote link it would work just fine, not going through with the link, but it does not seem to behave the same way with a :remote => true ?
How can I achieve the expected result ?
Thanks,
ALex
The issue here is that the Rails UJS driver will see the data-remote and then perform the action because of a function like this in rails.js so perhaps try setting the property om your link inside the JS and remove the :remote => true. That might work however I dont know if rails.js would bind to that correctly or not.
Also, consider placing this JS in the application.js once you're done debugging just so you dont have inline JS all over your controllers.
All.
A Rails n00b here...
I'm writing an application that reports the status of a transaction.
Some of the content in the rendered HTML comes from instance variables
initialized in the controller, while other content comes from text files
(e.g., log files) that I want to render in the HTML using <pre> tags.
What is the "Rails Way" to do this?
Thank you for your time...
<pre>
<%= render :file => '/tmp/test.log' %>
</pre>
Here you go: http://edgeguides.rubyonrails.org/layouts_and_rendering.html
In some cases (when the file is not small and loading it is connected with a delay), I prefer to load the page content and then to use jQuery ajax request to load the file content.
For example, let's say I have a model with file path attribute. In the view layout I am doing something like this:
<pre data-source=" <%= (#file.path) %>"></pre>
Then in the corresponding js file I am loading the context like this:
$(document).ready ->
$.ajax(
url: $("pre").data("source")
context: document.body
).done (response) ->
$("pre").html response
return
return
Of course you can check the jQuery ajax documentation for more options. For example, you can render the pre tag with loading like this:
<pre data-source=" <%= (#file.path) %>"><div class="loading"></pre>
or use other jQuery animations as well.