some symbols in URL are changed hex during page loading - ruby-on-rails

When I click href, the URL is changed during page load.
I click href, www.test.com/main#/?arg1=1&arg2=2
, then it is displayed in URL bar.
But during page loading URL is suddenly changed to www.test.com/main#!#%2F%3Farg1=1&arg2=2
I am not sure why #/? characters are changed to #!#%2F%3F
This issue has happened since I update Angularjs version.

Rails is not able to work with urls with # in the middle. Only when # is the anchor of the URL.
When .html.erb is loaded, Rails encode this symbols inside the URLs. You can do something like this.
.html.erb
<%= link_to 'angular link','www.test.com/main#/?arg1=1&arg2=2', class: 'angular-link', data: { url: 'www.test.com/main#/?arg1=1&arg2=2' } %>
javascript
document.addEventListener('DOMContentLoaded', () => {
const angularLinks = document.getElementsByClassName('angular-link');
for(let angularLink of angularLinks) {
setTimeout(function(){
var angularURL = angularLink.getAttribute('data-url');
angularLink.setAttribute('href', angularURL);
}, 1000);
}
});
Is not the best solution, but it works 🤷🏻‍♂️

The root cause was the hashbang of angularjs.
angularjs now requires Exclamation mark.
I puts ! symbol like this and it works.
!/?

Related

Rails 4: How to upload files with AJAX

I want to upload files with AJAX. In the past I accomplished this by using the magical jQuery form plugin and it worked great. Currently I'm building a Rails app and trying to do things "The Rails Way" so I'm using the Form Helper and the paperclip gem to add file attachments.
The rails docs warn that the Form Helper does not work for AJAX file uploads:
Unlike other forms making an asynchronous file upload form is not as
simple as providing form_for with remote: true. With an Ajax form the
serialization is done by JavaScript running inside the browser and
since JavaScript cannot read files from your hard drive the file
cannot be uploaded. The most common workaround is to use an invisible
iframe that serves as the target for the form submission.
It seems clear there's no off-the-shelf solution. So I'm wondering what's the smartest thing to do. Seems like I have several options:
Use the form helper and the iframe trick.
Use the form helper + load jQuery form plugin to submit the file (not sure if this will play nice with Rails's authenticity token, etc)
Use the form helper + paperclip + [some other gem] to extend it's functionality to allow AJAX form submission.
All three seem possible. I know the least about #3, specifically the [some other gem] part. I found two similar questions (this and this) which mention a branch of Pic-Upload called Uploadify but those are both 2 years old and deal with Rails 2 and 3 (and Uploadify hasn't been updated in years). So given how much has changed, I think this is really a whole new question:
What's the best way to upload files with AJAX in Rails 4?
Have a look into the remotipart gem: https://github.com/JangoSteve/remotipart -- may get you all of the way there with very little work!
Using #rails/ujs.
view (.html.erb):
<%= file_field_tag :file, { id: "ajax_file_upload"} %>
controller(_controller.rb):
def update
#record = YourModel.find(params[:id])
respond_to do |format|
if #record.update_attributes(params[:your_model])
format.json { render json: { success: true } }
else
error_messages = #record.errors.messages.values.flatten
format.json { render json: { success: false, errors: error_messages } }
end
end
end
javascript(.js)
const uploadFile = element => {
const formData = new FormData();
formData.append("your_model[attribute_name]", element.target.files[0]);
Rails.ajax({
url: "your_model/:id",
type: "PUT",
beforeSend(xhr, options) {
options.data = formData;
return true;
},
success: response => {
if (response.success) {
alert("File uploaded successfully");
}
else {
alert(response.errors.join("<br>"));
}
},
error: () => {
alert("ajax send error");
}
});
};
const documentOnReady = () => {
const fileField = document.getElementById("ajax_file_upload");
if (fileField) {
fileField.addEventListener("change", uploadFile);
}
}
document.addEventListener("turbolinks:load", documentOnReady);
Note: No need to setRequestHeader in ajax while using FormData.
FormData uses the same format a form would use if the encoding type were set to "multipart/form-data"
IMHO Rails is not perfect when dealing with upload files using AJAX, especially if you want a progress bar. My suggestion is to use Javascript for the form submission over an AJAX request like you suggested in (2). If you are comfortable with Javascript you will not have many problems.
I recently used the same approach by using this very simple JS library https://github.com/hayageek/jquery-upload-file and I wrote more details here http://www.alfredo.motta.name/upload-video-files-with-rails-paperclip-and-jquery-upload-file/
For an application with a form to upload a movie with title and description the JS code looks like follow:
$(document).ready(function() {
var uploadObj = $("#movie_video").uploadFile({
url: "/movies",
multiple: false,
fileName: "movie[video]",
autoSubmit: false,
formData: {
"movie[title]": $('#movie_title').text(),
"movie[description]": $('#movie_description').text()
},
onSuccess:function(files,data,xhr)
{
window.location.href = data.to;
}
});
$("#fileUpload").click(function(e) {
e.preventDefault();
$.rails.disableFormElements($($.rails.formSubmitSelector));
uploadObj.startUpload();
});
});
Far from perfect, but gives you flexibility on your frontend.

Rails hide #anchor tag in URL

I am using JavaScript to navigate to anchors on the main page. From within the main page, when I click an anchor link, the url remains the same: localhost/ (using javascript ScrollTo), but when I try to link to that anchor tag from a separate page it displays the anchor tag in the url. localhost/#anchor.
How can I mask the url to not display the anchor tag?
<li><%= link_to "Anchor", root_path(:anchor => 'anchor') %></li>
jquery:
$('a[href*=#]:not([href=#])').click(function() {
if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'')
|| location.hostname == this.hostname) {
var target = $(this.hash);
target = target.length ? target : $('[name=' + this.hash.slice(1) +']');
if (target.length) {
$('html,body').animate({
scrollTop: target.offset().top
}, 1000);
return false;
}
}
When you click on the anchor on the main page you're not actually submitting a request to Rails, you're just executing a click handler. But when you are coming from another page then you are submitting a request to Rails and the #fragment is the only way for anyone to know where on the page to scroll to.
Best you can do is detect the #fragment in JS when you load the page, scroll to the right place, and remove the fragment with
history.replaceState({}, '', window.location.href.substring(0, window.location.href.indexOf('#')))
assuming you have a browser that supports replaceState. You can use History.js if you want to be nice to older browsers.
You can do window.location.hash.replace('#', '');

Rails 3 - Link to :remote, updating the address URL

I'm currently toying with updating page content via the following:
<%= link_to(content_tag(:span, 'Settings'), edit_admin_store_path, :remote => true)%>
With the javascript as such:
$('nav li a').bind("ajax:success", function(event, data){
console.log(event + data);
$('div#loading').hide();
$('div#container div#content').html(data).hide().fadeIn('100');
});
And was wondering if there is a 'rails way' to also update the address url as well?
Thanks a plenty for any help/advice!
Basically, you are asking for the HTML5 history.pushState method. Great documentation can be found here, at the mozilla developers network.
To be simple, you would push into the history by doing something like this:
var stateObj = { foo: "bar" };
history.pushState( stateObj, "new page title", "forbear.html" )
This will cause the URL to display http://www.yoursite.html/whatever/foobar.html depending on what the URL currently looks like.
Cheers!

Broken relative Url in jQuery getJSON Ajax Method

The Url for my development environment is:
http://localhost/mysite/blah...
I am using jQuery & getJSON to perform some ajax actions on my site, which work fine all the time I specify the url as:
/mysite/controller/action
..but this is not ideal as I don't want to hardcode my development url into my seperate jQuery include files.
When the site goes live, it'll be fine to have controller/action or /controller/action as the url as that will resolve ok, but for the development site, it's no go.
I've tried:
controller/action
..but this returns a 404, which suprised me as I thought the lack of / at the front of the url would prevent from looking at the website root.
There must be a neat solution to this?
I would do this by inserting a global constant in my HTML header:
<script type="text/javascript">
var BASE_URL = '/mysite/';
</script>
That would be inserted from your server so it can be dynamically changed.
Later in your script, you'll be able to make AJAX requests with (jQuery style here):
$.ajax( BASE_URL + '/controller/action', ...);
If you're in
/mysite/controller/action
then the correct relative path to
/mysite/some_other_controller/some_other_action
is
../../some_other_controller/some_other/action
You can use this code to get the current path of the .js script and use it for calculate your relative path.
var url;
$("script").each(function () {
if ($(this).attr("src").indexOf("[YOUR_SCRIPT_NAME.JS]") > 0) {
url = $(this).attr("src");
url = url.substr(0, url.lastIndexOf("/"));
return false;
}
});
var final_url = url + "/your_target_script.js"
Replace YOUR_SCRIPT_NAME with the unique name of your script.

Detecting if this is an iframe load or direct

I am looking to only show a form if it is pulled on a page within an iframe. How do I do that? Is there a server side solution?
If you are using JQuery... (installation instructions here: http://jquery.com/ )
$(document).ready(function(){
if( window == window.top) { $('form#myform').hide(); }
});
Which just hides the form with id "myform" if the window is not the topmost window.
I can't think of purely serverside way, but you could use a bit of hybrid javascript/rails.
assuming that you have a dedicated iframe layout template e.g. 'layouts/iframe.erb'
you could put some javascript in the head to check if it is being loaded as an iframe, and if it is not, redirect to an action and maybe display a flash msg "can only load this page inside application"
The javascript/rails for the head
<script type="text/javascript">
function parentExists()
{
return (parent.location == window.location)? true : false;
};
function check_modal(){
if (parentExists()) {
window.location = '<%= url_for( :controller => "home", :action => 'iframe_action', :iframe_fail => 'true')%>'}
}
check_modal()
</script>
notice the param :iframe_fail which you could check for in a controller and do whatever you please if that param is present e.g. display flash msg or redirect
example controller
def iframe_action
if params[:iframe_fail]
flash[:notice] = 'can only load inside app'
else
#do something else
end
end
Not real pretty but might help you get the job done.
My iframe tag was like
%iframe{:height => "98%", :width => "98%",:"id" => "profileIframe"}
I wanted to hide header of my webpage within this iframe hence I used code as:
var $frame = $(window.parent.frames["profileIframe"]).contents();
$frame.find('.header-ui').hide();
If you observe then contents() returns a element as "#document", which is html of iframe, hence calling a javascript without this will try to access your actual webpage rendered in background of iframe.
You can only check it on the client side via JavaScript.
However: DO NOT DO THAT. There are plenty of legitimate uses of putting a site in a (i)frame. Breaking out of such iframe or changing your site in any way in such circumstances them will only make your users pissed unhappy.

Resources