ActiveAdmin Select2 not working - ruby-on-rails

I am using ActiveAdmin 1.0 (current master updated today). And have migrated from Chosen to Select2. I followed the directions at https://github.com/mfairburn/activeadmin-select2 but am noticing issues. If I navigate pages on the site, the css doesn't seem to be working. However if I am on a page and select the refresh button, the Select2 css shows up.
active_admin.js.coffee
#= require active_admin/base
#= require chosen-jquery
#= require_tree ./active_admin
#= require_tree .
#= require active_admin/select2
active_admin.css.scss
// SASS variable overrides must be declared before loading up Active Admin's styles.
//
// To view the variables that Active Admin provides, take a look at
// `app/assets/stylesheets/active_admin/mixins/_variables.css.scss` in the
// Active Admin source.
//
// For example, to change the sidebar width:
// $sidebar-width: 242px;
// Active Admin's got SASS!
#import "active_admin/mixins";
#import "active_admin/base";
//#import "chosen";
//#import "active_admin/chosen";
//body.active_admin {
#import "active_admin/select2/base";
//}
// Overriding any non-variable SASS must be done after the fact.
// For example, to change the default status-tag color:
//
// .status_tag { background: #6090DB; }
Any ideas why I have to refresh every page for the css to load? I have cleared cache and precompiled assets.

Sounds like this isn't a CSS issue, but an issue with your JS initialization and turbolinks. The problem lies on this line of code: https://github.com/mfairburn/activeadmin-select2/blob/master/app/assets/javascripts/active_admin/select2/select2.js.coffee#L15 which isn't compatible with Turbolinks, as the document.ready event isn't called when making page transitions - you can read about Turbolink's event lifecycle here: https://github.com/rails/turbolinks#events
My suggestion would be to probably override the file I linked to above by creating a assets/javascripts/active_admin/select2/select.coffee in your own app tree containing this code:
'use strict';
initSelect2 = (inputs, extra = {}) ->
inputs.each ->
item = $(this)
# reading from data allows <input data-select2='{"tags": ['some']}'> to be passed to select2
options = $.extend(allowClear: true, extra, item.data('select2'))
# because select2 reads from input.data to check if it is select2 already
item.data('select2', null)
item.select2(options)
$(document).on 'has_many_add:after', '.has_many_container', (e, fieldset) ->
initSelect2(fieldset.find('.select2-input'))
$(document).on 'ready page:load', ->
initSelect2($(".select2-input"), placeholder: "")
return
(notice the use of Turbolink's page:load event)

Related

specific issue getting component- chip's jQuery to function properly in rails app

I have been beating my head against this for a day and a half now. I think I'm close though! I have two issues that I've searched high and low for. Broadly, I am trying to use Materialize within rails to use their "chips" component. It's seemingly most advanced option uses jQuery to auto-complete a search... once enter is hit it becomes a "chip." http://materializecss.com/chips.html
I finally got jQuery working by creating a users.js file which is supposed to allow me to just toss in jQuery calls to be reflected in my html view. Once I got this working, I tried to delete the portions of the code I didn't need to better identify what's doing what... I did this in codepen. https://codepen.io/robmatthews/pen/XgyERE
HTML
<div class="testing">
<div class="chips chips-initial"></div>
<div class="chips chips-placeholder"></div>
<div class="chips chips-autocomplete"></div>
</div>
jQuery
$(document).ready(function(){
$('.chips-initial').material_chip({
data: [{
tag: 'Apple',
}, {
tag: 'Microsoft',
}, {
tag: 'Google',
}],
});
$('.chips-placeholder').material_chip({
placeholder: 'Enter a tag',
secondaryPlaceholder: '+Tag',
});
$('.chips-autocomplete').material_chip({
autocompleteOptions: {
data: {
'Apple': null,
'Microsoft': null,
'Google': null
},
limit: Infinity,
minLength: 1
}
});
});
Once I got this working (note its the code from the materialize docs plus the document initialization that I assume you need... maybe you don't? The only part that has only worked for me in within the doc's example though is, of course, the one I want to use. On the doc's site, the auto-complete jQuery works... it doesn't in codepen... I have no idea why. I assume I need to add dropdown somewhere because I see that in inspect?? No idea.
Can anyone see what I am doing wrong? Here's what my appplication.js file includes with the users folder being positioned here: assets/javascripts/users
//= require jquery
//= require turbolinks
//= require materialize-sprockets
//= require_tree ./users/
This is only part of my problem. My next part will surely be when I am trying to modify the data object to serve up dynamic auto-fill options based on all of the existing users names being searched for! but I'll hold off on that until I get there. Thanks in advance! can't wait to figure out what my issue is...

Making jQuery works with Turbolinks

I have a Rails 4 app, which uses Turbolinks. My understanding is that Turbolinks breaks jQuery code, as Turbolinks does not load new page, but only get new elements.
Therefore, navigating to new page may not trigger .ready, although it always triggers .page:load, and thus new jQuery code won't initialize.
I have a lot of jQuery code, so I don't want to modify my jQuery code to be compatible with Turbolinks.
Is it possible to add a javascript code to my application.js that overwrites .ready event to include page:load as well? How should I do it?
Rather than wait for $(document).ready to fire for your jQuery, just use page:load instead:
$(document).on 'page:load' ->
<your code>
Alternatively, you can set up the jquery.turbolinks gem: https://github.com/kossnocorp/jquery.turbolinks
With turbolinks 5.0.0, the events changed to turbolinks:load. See full list of turbolinks events.
The documentation recommends following code:
document.addEventListener("turbolinks:load", function() {
// ...
})
The jquery.turbolinks fork located at https://github.com/dalpo/jquery.turbolinks already reflects these changes and allows for a seamless drop-in of turbolinks. Nevertheless, I would go for the turbolinks:load event to have full control and not require another library.
i had to use the page:change event:
js:
$(document).on('page:change', function () {
<code here>
});
coffee script:
$(document).on 'page:change' ->
<code here>
With TurboLinks 5 / Rails 5 ... I would recommend instantiating DataTables like this.
It will prevent the heading and footer paging from showing up multiple times when the back button is used.
$(document).on 'turbolinks:load', ->
tableElementIds = [
'### TABLE ID HERE ###'
]
i = 0
while i < tableElementIds.length
tableElementId = tableElementIds[i]
if $.isEmptyObject($.find(tableElementId))
i++
continue
table = undefined
if $.fn.DataTable.isDataTable(tableElementId)
table = $(tableElementId).DataTable()
else
table = $(tableElementId).DataTable(### OPTIONS HERE ###)
document.addEventListener 'turbolinks:before-cache', ->
table.destroy()
return
i++
return
TLDR; Here is how the conventional approach works
$(document).ready(function() {
$("#tbl-account").tableSorter();
});
It uses jQuery to initialize a table-sorting plugin once the document finishes loading. One noticeable thing here is not teardown and re-run of this js when page component switches by Turbolink. There isn't any. There didn't need to be back in the day because the browser handled the cleanup. However, in a single-page application like Turbolinks, the browser doesn't handle it. You, the developer, have to manage the initialization and cleanup of your JavaScript behaviors.
When people try to port traditional web apps to Turbolinks, they often run into problems because their JS never cleans up after itself.
All Turbolinks-friendly JavaScript needs to:
Initialize itself when a page is displayed
Clean up after itself before Turbolinks navigates to a new page.
Capturing Events
Turbolinks provides its own events that you can capture to set up and tear down your JavaScript. Let's start with the tear-down:
```js
document.addEventListener('turbolinks:before-render', () => {
Components.unloadAll();
});
```
The `turbolinks:before-render` event fires before each pageview except the very first one. That's perfect because on the first pageview there's nothing to tear down.
The events for initialization are a little more complicated. We want our event handler to runs:
On the initial page load
On any subsequent visit to a new page
Here's how we capture those events:
// Called once after the initial page has loaded
document.addEventListener(
'turbolinks:load',
() => Components.loadAll(),
{
once: true,
},
);
// Called after every non-initial page load
document.addEventListener('turbolinks:render', () =>
Components.loadAll(),
);
Thanks to Starr Horne for writing the article on migrating from jquery/pjax to turbolinks

How to resize images client side using jquery file upload

I am using blueimp jquery-file-upload in a Rails 3.2 app, via the jquery-fileupload-rails gem.
I am trying to resize images on the client side prior to uploading, but am having trouble following the documentation. My code is below. Currently uploading works perfectly, but the images are not resized.
What is the correct syntax to resize images via jquery-file-upload.
(Two approaches shown in the coffeescript based on this and this documentation. Neither works for me.)
#Coffeescript
jQuery ->
if $("#new_asset").length
$("#new_asset").fileupload
dataType: "script"
add: (e, data) ->
types = /(\.|\/)(jpe?g|png)$/i
file = data.files[0]
if types.test(file.type) || types.test(file.name)
data.context = $(tmpl("template-upload", file))
$('#progress-container').append(data.context)
jqXHR = data.submit()
$("button.cancel").click (e) ->
jqXHR.abort()
else
alert("#{file.name} is not a jpeg or png image file")
progress: (e, data) ->
if data.context
progress = parseInt(data.loaded / data.total * 100, 10)
data.context.find('.bar').css('width', progress + '%')
stop: (e, data) ->
$('.upload').hide()
process: [
action: "load"
fileTypes: /^image\/(gif|jp?g)$/
maxFileSize: 20000000 # 20MB
,
action: "resize"
imageMaxWidth: 1500
imageMaxHeight: 1500
,
action: "save"
]
dropZone: $(".dropzone")
sequentialUploads: true
disableImageResize: /Android(?!.*Chrome)|Opera/.test(window.navigator and navigator.userAgent)
imageMaxWidth: 1500
imageMaxHeight: 1500
downloadTemplateId: null
#application.js
//= require jquery-fileupload
EDIT
According to Matanza's answer, the add callback in my code prevents any processing functions from being called automatically. So I assume I need to do something like
...
add: (e, data) ->
$.each data.result, (index, file) ->
// processing code goes here
But I'm having a lot of trouble working out the correct syntax or making sense of the guides that are available.
How do I apply the resize processing to each file in the add callback?
I solved it by calling the process within the add callback like so:
add: (e, data) ->
current_data = $(this)
data.process(->
return current_data.fileupload('process', data);
).done(->
data.submit();
)
also remember to load your JS files in the right order in application.js....(this wasted several hours of my life):
//= require jquery-fileupload/vendor/jquery.ui.widget
//= require jquery-fileupload/vendor/load-image
//= require jquery-fileupload/vendor/canvas-to-blob
//= require jquery-fileupload/jquery.iframe-transport
//= require jquery-fileupload/jquery.fileupload
//= require jquery-fileupload/jquery.fileupload-ui
//= require jquery-fileupload/jquery.fileupload-process
//= require jquery-fileupload/jquery.fileupload-validate
//= require jquery-fileupload/jquery.fileupload-image
If that is still relevant-
I found out that once you use the add callback, it is your responsibility to add whatever processing stages that you require. So for that matter if you remove the add callback it will resize the image with your settings.
what you should do is register the resize processing settings to each file in the add function
hope that helps
I had trouble getting Image resize to work. In the end I started again. I had added image resize to an existing working file upload code. For me, it was the custom add that was hindering the resize. Once I removed the custom add, it was hunky-dorey. Just wanted to put it out there for the benefit of other strugglers.

javascript in rails not recognized

This is a fairly simple question but I can't seem to figure out why it's not working
I have this code:
$(".positive").numeric({ negative: false }, function() { alert("No negative values"); this.value = ""; this.focus(); })
which works when I put it in my view between <script> and </script>
However, when I copy and paste that formula to app->assets->javascripts-> page.js.coffee it doesn't work. I have even tried copy and pasting that code to JS2Coffee converter and pasting the coffee version of the code. Still no luck.
What am I doing wrong that my view does not recognize the javascript in my assets?
Here is the code in coffescript:
$(".positive").numeric
negative: false
, ->
alert "No negative values"
#value = ""
#focus()
my application.js has
//= require jquery
//= require jquery_ujs
//= require jquery.numeric
//= require_tree .
Your CoffeeScript generates the following JavaScript:
$(".positive").numeric({
negative: false
}, function() {}, alert("No negative values"), this.value = "", this.focus());
As you can see, it's not the same.
Please note some things:
You do not have to use CoffeeScript. You can use plain JavaScript if you want to.
If you want to use CoffeeScript, please read up on its syntax. Most importantly: in CoffeeScript, whitespace is significant.
Your codesnippet could look like this, in correct CoffeeScript:
$(".positive").numeric
negative: false
->
alert "No negative values"
#value = ""
#focus()
The reason that your code isn't executed when you put it through the asset pipeline, is that it will end up in the head of the document, instead of the body, and hence it will be executed before the browser has finished loading the DOM.
In other words - there isn't any element to fetch yet.
To solve this, wrap you code with JQuery:
$ ->
# Your code goes here
JQuery will make sure that your code is executed only once the DOM is ready for manipulation.

Jquery calls not working in $viewContentLoaded of Angular

Unable to call jquery functions in $viewContentLoaded event of Angular controller, here is the code for the same.
$scope.$on('$viewContentLoaded', function() {
jQuery.growlUI('Growl Notification', 'Saved Succesfully');
jQuery('#category').tree()
});
Is any configuration required here?? I tried even noConflict(); var $jq = jQuery.noConflict();
Does it require any other configuration?
Thanks,
Abdul
First thing first, don't do DOM manipulation from controller. Instead do it from directives.
You can do same thing in directive link method. You can access the element on which directive is applied.
Make sure you load jquery before angularjs scripts, then grawlUI, three, angularJS and finally your application script. Below is directive sample
var app = angular.module("someModule", []);
app.directive("myDirective", function () {
return function (scope, element, attrs) {
$.growlUI('Growl Notification', 'Saved Succesfully');
element.tree();
};
});
angularjs has built in jQuery lite.
if you load full jquery after angular, since jQuery is already defined, the full jquery script will skip execution.
==Update after your comment==
I reviewed again your question after comment and realised that content which is loaded trough ajax is appended to some div in your angular view. Then you want to apply element.tree() jquery plugin to that content. Unfortunately example above will not work since it is fired on linking which happened before your content from ajax response is appended to element with directive I showed to you. But don't worry, there is a way :) tho it is quick and dirty but it is just for demo.
Let's say this is your controller
function ContentCtrl($scope, $http){
$scope.trees=[];
$scope.submitSomethingToServer=function(something){
$http.post("/article/1.html", something)
.success(function(response,status){
// don't forget to set correct order of jquery, angular javascript lib load
$.growlUI('Growl Notification', 'Saved Succesfully');
$scope.trees.push(response); // append response, I hope it is HTML
});
}
}
Now, directive which is in controller scope (it uses same scope as controller)
var app = angular.module("someModule", []);
app.directive("myDirective", function () {
return function (scope, element, attrs) {
scope.$watch("trees", function(){
var newParagraph=$("<p>" + scope.trees[scope.trees.length-1] + "</p>" ); // I hope this is ul>li>ul>li...or what ever you want to make as tree
element.append(newParagraph);
newParagraph.tree(); //it will apply tree plugin after content is appended to DOM in view
});
};
});
The second approach would be to $broadcast or $emit event from controller (depends where directive is, out or in scope of controller) after your ajax completes and you get content from server. Then directive should be subscribed to this event and handle it by receiving passed data (data=content as string) and do the rest as I showed you above.
The thing is, threat that content from ajax as data all the way it comes to directive, then inject it to element in which you want to render it and apply tree plugin to that content.

Resources