In my rails app, I have a html template located in public/templates/_hero-1.html.erb
In my application.js.erb file, I'm trying to load that template into a div I created dynamically, before I render the entire div.
// Append component to text editor when clicked
$('.js-TemplateThumbnail').click(function() {
// Define new template container
var newTemplateContainer = $('<div class="position-relative hideChild js-TemplateContainer">');
// Insert selected template into new template container
newTemplateContainer.load("<%= 'public/templates/hero-1' %>");
// Build new template
$('.js-Canvas .js-TemplateContainer').last().after(newTemplateContainer);
});
But I'm getting a console error GET http://localhost:3000/public/templates/hero-1 404 (Not Found)
Quick answer/related SO answer: Rendering partial in js.erb file
Longer answer: There are a couple ways of doing this. You could try:
newTemplateContainer.load("<%= 'render partial: public/templates/hero-1' %>");
Not positive that will work. This feels hacky to me and also leaves you with highly coupled code. If the name of that template changes, you will have to update it in n places.
I would decouple the JS and create an internal endpoint in config/routes.rb; eg:
get templates/:id
then,
class TemplatesController < ApplicationController
def show
#template = Template.find(params[:id])
end
end
Then in your JS inside the click handler you can:
$.get('/templates/hero-1', function(data) {
// do stuff with returned data
$('.js-Canvas').html(data)
});
Very simplistic, but more so trying to outline a different approach. This also seems like something that handlebars could help with.
Related
In my search controller I have an instance variable, #results. I set it to return a dummy result, 'foo'.
In my search.html.erb view... I set window.results = <%= raw #results.to_json %>.
At the top of my search.js asset file, I console.log(results) and get undefined, but when I get into the devtools after the page load, results is populated with 'foo'. The console.log statement is also wrapped in a jquery ready function.
What gives?
Rails assets are precompiled so they don't have access to instance variables coming from the controller. Embed the Javascript directly into your view using a script tag and access the #results just like you did. You could also initiate a call to an external Javascript file.
I'm trying to implement the top solution here, the one that only uses javascript:
Django: how to change the choices of AdminTimeWidget
It basically uses regex to create the different time choices by overriding the time options.
The only problem I have is that my script loads before DateTimeShortcuts.js, so I get an Uncaught ReferenceError: DateTimeShortcuts is not defined. Does anyone know how I can force the DateTimeShortcuts.js file to load after my js file that references it?
If I create a second reference to DateTimeShortcuts.js, it'll work properly, but I'll have two clocks up there, only the 2nd one will modify, because it's loading after the 2nd DateTimeShortcuts.js
I'm calling my file like this, where admin_clock.js references DateTimeShortcuts.js and has the override code:
class EventAdmin(admin.ModelAdmin):
list_filter = ('film', 'partner',)
list_display = ('id', partner', 'film', 'date_time', 'venue_name', 'city')
class Media:
js = ('tiny_mce/tiny_mce.js', 'tiny_mce/textareas.js', 'admin_clock.js',)
Apologies for not commenting on the original answer, I need more points to comment there.
The way I solved this was to add the javascript directly to the extrahead block of my version of the change_form template, overriding that block in the template: https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#overriding-admin-templates
I have a variable in rails controller like
def index
#approveflag = true
end
I need to access this variable in my javascript code, I used the code given below
in index.html.erb
<script>
alert("<%=#approveflag%>")
<script>
It works fine with result "true".But when I moved this code to its corresponding .js file it gives an alert with result string ""<%=#approveflag%>"".
What is the reason. How can I solve this?
Thanks in advance.
I personally don't like mixing js with erb so I would do something like this:
in index.html.erb
<%= content_tag :div, '', id: 'mycontainer', data: { source: #approveflag } %>
in js
$('#mycontainer').data('source')
alert($('#mycontainer').data('source'))
And you have the value from that instance variable, you can add a data attribute in a link_to if you want.
You cannot use the js files for this, they are part of the asset pipeline, they are compiled/compressed, and preferably only downloaded once by your client (browser). So they should not change. So at the time the js-files are precompiled, the instance variable is not set and would not make any sense anyway.
But there are a few options.
You can declare a javascript variable in your view, which your javascript can use (for global data)
Code (I use haml):
:javascript
var approveFlag = #{#approveflag} ;
You can declare data-tags on elements, if the data belongs to a specific element. But for instance, you could also a data-tag on body element
For instance
%body{:'data-approveflag' => #approveflag}
Or something like
= link_to 'Collapse', '#', 'data-collapse-div' => 'class-to-collapse'
Alternatively you can use ajax/json to download the data. This is clean, but adds an extra delay, so only do this is if the data is not required immediately.
I recommend that you to use the 'gon' gem
I'm usually more oriented to straight options that don't imply installing, but trust me using 'gon' is very direct and clean
In your Gemfile add this lines
# To use controller variables as javascript variables
gem 'gon'
Do a bundle install to get/install the gem
In your app/views/layouts/application.html.erb add the following line
<%= include_gon %>
Then in your controller pass your variable to gon as simple as this
gon.your_variable = #controller_variable
And in your javascripts retrieve your variable (in this case to show an alert)
alert (gon.your_variable) ;
And That's it !
For more examples and details see the Wiki page - https://github.com/gazay/gon/wiki
In my project I need sometimes user language related translations. So what I did is, I created a simple js file to store the localized text:
var TRANSLATION_ARRAY = []
function store_translation(key, value) {
TRANSLATION_ARRAY[key] = value
}
function get_translation(key) {
return TRANSLATION_ARRAY[key]
}
In den index.html.erb or other components I do this to store the localized text:
store_translation('text_id', "<%= translate_with_user_language('text_id') %>")
In my plain js file located in app/assets/javascripts i get the localized text by:
get_translation('text_id')
Works very well. You can use this approach also for other purposes not only translations.
I'm a big fan of ICanHaz, and I'm trying to directly intregrate it into a new Marionette application I'm building. However, going off this post, I have written this that reaches into the render method and changes it in Marionette:
// Set up Initalizer
APP.addInitializer(function() {
//Reach into Marionette and switch out templating system to ICH
Backbone.Marionette.Renderer.render = function(template, data){
return ich[template](data);
}
//Create Router
new APP.Routers.GlobalRouter();
//Start Backbone History
Backbone.history.start();
});
If I walk through this function, all the data seems to work fine. However, when put into use and trying to use it for layouts and Item Views, nothing gets appended or inserted. This is from my GlobalRouter:
//Grab the main Layout
var layout = new APP.Views.LayoutView();
//Render that layout
layout.render();
//Make the model
var userModel = new APP.Models.UserModel({
"user_name" : "nweingartner#awesome.com",
"tenant" : "Ginger Ale is Great"
});
//Make the Header Region
var headerRegion = new APP.Views.HeaderView({model: userModel});
layout.header.show(headerRegion);
This all happens in a method that gets called when index is hit. There are no JS errors so I have nothing to go on. However, it in the render function I append the data to the body, it will add (however ruining my layout and region structure).
I am storing my templates in index.html.
Can anyone help with this?
Okay, I couldn't find an easy way to do this using ICH. However, due to another SO I found, very similar functionality can be found just using Mustache.
Using this code:
Backbone.Marionette.TemplateCache.prototype.compileTemplate = function(rawTemplate) {
return Mustache.compile(rawTemplate);
}
Lets you change the renderer so you can pull mustache templates from index.html using Marionette's template call. A mustache template looks like this:
<script id="headerTemplate" type="text/template">
<p>{{user_name}}</p>
<p>{{tenant}}</p>
</script>
The difference is that the type is 'text/template' as opposed to 'text/html'. Otherwise it acts very similar.
Hope this helps someone else.
Can anyone explain how i can access the rails routes/names routes in javascript ?
The following are some of the things i tried
http://github.com/jsierles/js_named_routes.
but no luck.
I was researching this question for a while and didn't found any implementation compatible with Rails 3.
So decided to write my own:
https://github.com/railsware/js-routes
kishore I think this is the simpliest way, just call:
Rails.application.routes.url_helpers.*_path
So, if you have in your routes.rb, let's say:
resources :users
then you want to call the index action from a javascript file, you do:
$.get('<%= Rails.application.routes.url_helpers.users_path %>', function(data){ ...
Take into account that the js file should have a .erb extension (or *.js.erb) so rails knows that must be preprocessed. Otherwise, the file will be served as is.
If I understand your requirement correctly you want Javascript code in your views to have access to the named routes. If so, then one simple way to do this is to render your Javascript code through a template (ERB, Haml, etc) and then to expand the routes something like the following:
ERB:
<script>
$.get('<%= some_named_route_path %>', function(data) {
});
</script>
Haml:
:javascript
$.get('#{ some_named_route_path }', function(data) {
});
UPDATE: Adding suggestions for public/javascripts/ case
Accessing named routes from the public folder is a bit trickier but there at least 2 ways that come to mind, neither of which is entirely satisfactory but, one or either of which might suit your application
Create javascript templates in (say) lib/javascripts using ERB named like '*.js.erb' and a rake task to expand those templates into public/javascripts before deploying (say as a step in your Capistrano recipe for example). Downside of that is that your changes are not made available live on the site until the templates are expanded and you might not get the right syntax highlighting of your javascript with an .erb extension file
Create per-view javascript helper functions in a content_for :head block which expand the named routes and make those available to code from your public javascript files. If you're careful with namespacing and conventions this might work out well. The downside is that the code calling these javascript helpers is decoupled from the code that defines them which could be confusing for maintainers or prone to abuse.
In some view:
<% content_for :head do %>
<script>
SomeNameSpace.helper_to_some_named_route = function() {
return '%<= some_named_route_path %>
};
</script>
<% end %>
Then in public/application.js (say)
$.get(SomeNameSpace.helper_to_some_named_route(), function(data) {
});
bjg really answered this, but I thought I'd extract the relevant part and amplify with an example.
You simply provide your view a string variable whose value is a named Rails path, and then use that value in the Javascript of your form. An example that illustrates how a controller method can specify the path for another method, to be opened by the script on the press of a button:
File config/routes.rb:
...
resource :foo, :only => [:show, :reset]
...
match 'foo_reset_path' => 'foo#reset'
Commanding rake routes will now produce, among other output, this:
foo GET /foo(.:format) foo#show
foo_reset_path /foo_reset_path(.:format) foo#reset
foo_reset_path is what we're going to use here, but you can of course use this method with any named Rails path.
File app/controllers/foo_controller.rb:
...
def show
#reset_path = "foo_reset_path" # simply the string you'd use in the
# Rails code for opening the path
...
end
...
def reset
... # reset some variables to their default values
redirect_to foo_path # causes the show method to be called, and the HTML
# page to be redisplayed
end
File app/views/foo/show.html.erb:
...
<input type="hidden" id="reset_path" name="reset_path" value="<%= #reset_path %>">
...
<script>
$(document).ready(function() {
...
/* Hang functionality on the "Reset form" button. */
$('#reset_button').click(function () {
var reset_path = $('#reset_path').val();
window.open(reset_path, "_self")
});
...
})
</script>
I'm using JQuery here, but the basic idea should be clear. The script adds a hook to the button element whose id is reset_button, so that clicking on the button causes the reset method of foo_controller to be called.
What I did based on Teemu's great answer:
In your controller:
def show
#section = Section.find(params[:id])
respond_to do |format|
format.html
format.json { render json: #section}
end
end
In your view:
<input type="hidden" id="section_path" data-path="<%= #section.id %>" name="section_path" value="foo">
In your js:
var id = $('#section_path').attr('data-path');
$.ajax({
url:'/sections/'+ id +'.json',
type:"GET",
success: function (data){
console.info(data);
},
error: function (xhr, status){
console.info(xhr.error);
}
});
If you are using a JS pipeline that supports import (ES6 + webpacker), you might want to check out js_from_routes, a code-generation library that works seamlessly with Rails reloader.
For each endpoint that you'd like to access from JS, it will generate a method that can help you build a URL or make a request.
resources :video_clips, only: [:update], export: true
By using it in combination with axios, these generated helpers can be convenient and easy to use:
import VideoClipsRequests from '#requests/VideoClipsRequests'
VideoClipsRequests.update({ data: video })
.then(onVideoUpdate)
Have in mind that you can adjust the generated code by providing a custom template, so it can adapt to any technology you are using, even plain jQuery.
Benefits:
No need to manually specify the URL, preventing mistakes and saving development time.
If an action is renamed or removed, the helper ceases to exist, which causes an error that is easier to detect than a 404.
See http://tore.darell.no/pages/javascript_routes:
JavascriptRoutes converts your Rails routes into JavaScript. You can then access (generate) them in the browser (or any other JS environment). It supports both normal and named routes, and creates helper functions for the latter.
Update: It looks as though someone has forked this if you prefer jQuery: https://github.com/ajnsit/javascript_routes
gem install the "angular_rails_templates"
Create a file called angular_rails_templates.rb in the config/initializers folder
copy following code in the file and restart server. (including "module CustomERBEngine" for it cannot be added to code block by 4 space)
module CustomERBEngine
class ERBTemplate < Tilt::ERBTemplate
def evaluate(scope, locals, &block)
scope.class_eval do
include Rails.application.routes.url_helpers
include Rails.application.routes.mounted_helpers
include ActionView::Helpers
end
super
end
end
end
Tilt.register CustomERBEngine::ERBTemplate, '.erb'