How should I update the pagination links in an AJAX call? - ruby-on-rails

So I've implemented AJAX pagination. The problem is that since the <%= paginate #videos %> code is not inside the partial that I render, the pagination links are not updated. What jQuery code should I use to update the pagination links?
Btw I tried $(".pagination").replaceWith('escape_javascript(<%= paginate #videos %>)');;
but i get this error: Uncaught SyntaxError: Unexpected token ILLEGAL
$(".pagination").replaceWith("escape_javascript(<%= paginate #videos %>)");; throws this error: Uncaught SyntaxError: Unexpected identifier
Here is the JS code the browser sees:
$(".pagination").replaceWith(" <nav class="pagination">
<span class="prev">
« Prev
</span>
<span class="page first">
1
</span>
<span class="page current">2</span>
<span class="page last">
3
</span>
<span class="next">
Next »
</span>
</nav>
");

Ideally, the new pagination links should be included in the HTML response of the ajax call. If that is not possible - presumably because the links reside elsewhere in the document, then I would suggest creating a context/link/URI/action/whatever (disclaimer: I'm not a ROR guy) which returns a JSON string, structured something like:
[{"data": "The HTML output"}, {"pageLinks": "pagination HTML"}]
and replace your ajax call with one which expects JSON as the return dataType. Then it should be easy, e.g.:
$.ajax({
url: 'theURL',
dataType: 'json',
success: function(json) {
$('.pagination').html(json.pageLinks);
$('#someDiv').html(json.data);
}
});

The problem looks to be the quotes in your use of replaceWith. You need to escape the characters of that string before trying to use it.
You have double quotes starting and ending your string argument in the replaceWith function but the string you are feeding it also has double quotes throughout that don't look to be escaped. Every time a double quote is encountered it is terminating the string and trying to parse the rest as javascript statements and not a string.

I know this is an old question, but I faced a simmilar problem using will_paginate gem, and I tryed a lot of complicated stuffs to update the pagination. However, I did it with a simple solution, that maybe can help someone else.
In summary, create a partial where you put the call to will_paginate helper inside a div, and after your ajax request, just render this partial again. For instance:
The _pagination.html.erb:
<div id="paginate">
<%= will_paginate #results %>
<div>
The your_template.js.erb (where #results should be available):
$('#paginate').html('j(render "pagination.html.erb")');
That should be enough.

Related

Capybara - usage of applying has_xpath? on an apath with variable

There is the structure like:
<div class="parent">
<div>
<div class="fieldRow">...</div>
</div>
<div>
<div class="fieldRow">
<div class="CheckBox">
</div>
</div>
<div>
<div class="fieldRow">...</div>
</div>
<div>
<div class="fieldRow">...</div>
</div>
</div>
In my script I am writing a loop for each of the 4 div's under div[#class='parent'] and aiming to click the checkbox if there is, i.e.
members = page.all(:xpath, '//div[#class='parent'])
members.each do |a|
if **page.has_xpath?(a).find(:xpath, "div[#class='fieldRow']/div[#class='CheckBox']")**
a.find(:xpath, "div[#class='fieldRow']/div[#class='CheckBox']").click
end
end
However I can't look for the correct usage of has_xpath? with xpath including variable.
Please advice? Thank you!
has_xpath? takes an XPath expression (not an element) and returns a boolean (true/false) based on whether there are any elements that match that expression within the current scope - http://www.rubydoc.info/gems/capybara/Capybara/Node/Matchers#has_xpath%3F-instance_method. Since it returns true/false you can't then call find on it. For the example you posted there's no need for XPath or checking for the existence of the elements, just find all the matching elements and call click on them. Something like
page.all('div.parent div.fieldRow div.Checkbox').each { |cb| cb.click }
or
page.all('div.parent div.Checkbox').each { |cb| cb.click }
if the fieldRow class isn't something you really need to check.
Note: this assumes clicking the elements doesn't invalidate any of the other matched elements/change the page.
If you REALLY need to do it with the whole members and looping on them , using XPath, and checking for presence then it would be something like
members = page.all(:xpath, './/div[#class='parent'])
members.each do |a|
if a.has_xpath?(:xpath, ".//div[#class='fieldRow']/div[#class='CheckBox']")
a.find(:xpath, ".//div[#class='fieldRow']/div[#class='CheckBox']").click
end
end
Note: the .// at the beginning of the XPath expressions is needed for scoping to work correctly - see https://github.com/teamcapybara/capybara#beware-the-xpath--trap - which is an issue using CSS selectors doesn't have, so you should really prefer CSS selectors whenever possible.

Ruby on Rails script not being loaded

In my ruby on rails application, I'm generating a view via an ajax call.
I'm using following piece of code.
$("#a_div_id").html("<%= escape_javascript(render 'index')%>");
And the view I'm trying to render is _index.html.erb:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
First Name: <input type="text" ng-model="firstName"><br>
Last Name: <input type="text" ng-model="lastName"><br>
<br>
Full Name: {{firstName + " " + lastName}}
</div>
<script>
alert('first');
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
alert('second');
$scope.firstName = "John";
$scope.lastName = "Doe";
});
alert('third');
</script>
When I render the view, I'm getting only first and third messages. However, when I add this piece of code into dashboard.html.erb rather than rendering it via the ajax code, it perfectly works.
In the first case, I'm getting the following error.
angular.min.js:6 Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.4.8/$injector/modulerr?p0=myApp&p1=Error%3A%2…ogleapis.com%2Fajax%2Flibs%2Fangularjs%2F1.4.8%2Fangular.min.js%3A20%3A274)
at angular.min.js:6
at angular.min.js:38
at n (angular.min.js:7)
at g (angular.min.js:37)
at eb (angular.min.js:41)
at c (angular.min.js:19)
at yc (angular.min.js:20)
at Zd (angular.min.js:19)
at HTMLDocument.<anonymous> (angular.min.js:294)
at fire (jquery.self-bd7ddd3….js?body=1:3233)
I'm be at my wits' end, I couln't decide what I'm missing,
Any suggestions,
Thanks.
​
Can you try without escape_javascript.
Also put a debugger like this, this will put a break point when chrome executes the js.
debugger;
$("#a_div_id").html("<%= render 'index' %>");
That would help you see what exactly jquery is trying to add as HTML.
If this doesn't work,
Probably you should take the js in script tag and put it in some method and call that method after you have added index to dom.
$("#a_div_id").html("<%= render 'index' %>");
myMethodToRenderAngular();

Rails 4 - Moving js inline script inside View to JS file

I would like to move some js inline scripts I have on my homepage to a javascript file (in Assets) but there is some complexity due to variables.
home.html.erb
<div>
this is the homepage
</div>
<script>
<% #deal.deal_details.each_with_index do |popin, index| %>
<% index_plus_one = index + 1 %>
function loadInfoPopin() {
var msg;
msg = Messenger().post({
message: '<%= j render partial: "deals/info_popin/info_popin#{ popin['popin_id'] }",
locals: { popin: popin, index: index_plus_one } %>'
});
}
<% end %>
</script>
For the sake of information here is the format of the Deal's column/attribute 'deal_details' (it's a json attribute):
[{"popin_id":"4","text1":"qqq","text2":"sqsq","image1":"sqqs"},{"popin_id":"5","text1":"sqqs","video1":"s"}]
This is an example and you can have as many json block inside the array as possible.
deals/info_popin/info_popin5.html.erb (it's an example)
<div>
<p>cool image</p>
</div>
</div>
Now, how can I move the whole script or at least the function loadInfoPopin() to a javascript file (that is to say away from the view home.html.erb) ?
Thanks
How about moving the loadInfoPopin() function into a separate JS file and altering the function slightly to take in an argument for the html message?
function loadInfoPopin(html_message) {
Messenger().post({
message: html_message
});
Setup the html for the message within the loop before passing into and calling loadInfoPopin.
.js.erb is an option. This way the js files will be parsed before compiling. Not sure though it will work the way you want.
I would recommend to leave the variables in the layout as vars and moving only the static JS parts to the assets.

AngularJS Filter in rails

I'm trying to make a searchable list of posts on a ruby on rails application that I made. I have AngularJS working on the application. All of the posts are saved on rails in #posts. How would I make AngularJS filter over that?
Here is the relevant view:
<h1>Posts</h1>
<div ng-app>
<div ng-controller = "PostCtrl">
<input placeholder = "Search Post Titles", ng-model="searchText">
<div ng-repeat="post in posts | filter:searchText">
<h2>{{post.title}}</h2>
<p>{{post.text}}</p>
</div>
</div>
</div>
I'm not sure how to fill the angular array posts with the objects in #posts.
It appears that your code works as it is. Here is a plunker that seems to recreate your code.
If you would like to filter inside the post object, you can use this syntax:
ng-repeat="post in posts | filter:{ text: searchText }"
The above will only search the values of the text property of post.
Continuing the answer of Davin Tryon, you can try as this.
First use ng-init="init()" in your Controller:
<div ng-controller = "PostCtrl" ng-init="init( <%= #posts.to_json %> )">
Then in your controller you can do:
$scope.init = (posts) ->
$scope.posts = angular.fromJson(posts)
Then posts will be a JavaScript Object which can be accessed in your scope as if you have query it using angular resource.
If you want to include the associations of posts (lets say comments i.e) you can check rails docs for to_json (or "as_json") and the :include option

Rails: Rendering Multiple index.html.erb on a Single Page

I am using tabs and want to load multiple index pages into tabs. For instance:
class AnimalsController < ApplicationController
def index
#dogs = Dog.all
#cats = Cat.all
end
end
Then in my views/animals/index.html.erb
<ul class="tabs">
<li>Dogs</li>
<li>Cats</li>
</ul>
<div id="#dogs">
<%= render #dogs %>
</div>
<div id="#cats">
<%= render #cats %>
</div>
Is refactoring out into a partial the only way to achieve this?
I'd like to have them loaded statically at once and not have to resort to doing an Ajax.load() when the tab is clicked.
You have your answer in the question itself. Why don't you just use javascript to hide the two partials and call them when their respective tab is clicked? You don't need ajax for this at all :)
Since you did not mention the javascript library that you use, I will give a generic solution using jquery:
Also you need not add a # to your respective div's ids. Change it to this:
<ul class="tabs">
<li id="dogs">Dogs</li>
<li id="cats">Cats</li>
</ul>
<div id="dogs" class="subTabs">
<%= render #dogs %><hr />
</div>
<div id="cats" class="subTabs">
<%= render #cats %><hr />
</div>
$(document).ready(function() {
$('.subTabs').hide(); //Hide the subTabs as soon as the DOM is loaded.
$('li').live('click', function(e) {
$('.subTabs').hide(); //Calling this again so as to remove an already loaded
tab, if any. You can refactor this part to make it
even simpler.
$('body').find('.subTabs').attr('id',$(this).attr('id')).show();
//This finds the ".subTabs" whose id is the same as the "li" id that
was clicked and shows it. Ofcourse, even this can be made even more
compact had i known your entire DOM structure.
});
});
Edit:
You also have to make sure that you style it using CSS to make it look more like tabs if you haven't already. :)
Hope this helps. :)
You typically only want to use a partial if you are using the same or almost the same code in more than one place.
When you say "index page", do you really want to use the same code that is used in an index of another controller? If so, then a partial is the best strategy.
Don't use JavaScript to solve a layout / code organization problem.

Resources