Using Rails data as the defaultProps in react component - ruby-on-rails

I'm using react-rails (with sprockets rather than webpack, if it turns out to be relevant), trying to make a reusable component for a sprawling rails application. It's basically a navbar, and we have a list of links we usually want to display, but we also need to be able to override that list on some pages.
So far so good. However!
We have the array of links saved as a constant in our rails app, and ideally, the default links would be the defaultProps for the react component. We don't want to duplicate that information in the react component, because the whole point of making this component is that we're trying to move toward consistency, and if we need to make updates hopefully we'll only make them in one place.
So this is an option:
<%= react_component "Navbar", links: navbar_default_links, other_settings: true, etc: false %>
But having to do that on every page seems inelegant when really we just want to call out when the links are different than the usual ones.
Does anyone know a way to pull information from Rails into the definition of the react component using react-rails? Or another solution?
(Edit: to be clear, this doesn't need to be information from the rails database)

Here are the ways I'm aware of to communicate to share constants between Rails and React.
Option #1: Wrap react_component
def render_navbar(props = {}, options = {})
react_component 'Navbar', {links: navbar_default_links}.merge(props), options
end
Option #2: (If you use Webpack) Declare the links in a JSON or YAML that both Rails and your bundler can consume.
# The file that used to define the constant
def navbar_default_links
JSON.load(File.read(Rails.root.join('path/to/your/file.json')))
end
// In `Navbar.js`, using json-loader
import navbarDefaultLinks from './path/to/navbar_default_links.json';
Option #3 (If you use the Asset pipeline) Add .erb to your JS file and use the constant
// Navbar.js.erb
const defaultProps = <%= MyModule::NAVBAR_DEFAULT_LINKS %>
Options #4 Communicate through a global object like window. It breaks encapsulation but hey 🤷‍♂️
In application.html.erb
<script>
window.navbar_default_links = <%= MyModule::NAVBAR_DEFAULT_LINKS %>
</script>
<%= `window.navbar_default_links` must be above this line %>
<%= javascript_pack_tag 'application' %>
// In `navbar.js`
const navbarDefaultLinks = window.navbar_default_links

Related

How use react components from npm in rails

I am new in react and I follow this article https://www.airpair.com/reactjs/posts/reactjs-a-guide-for-rails-developers
to use react in rails app and everything was fine. But later I wanted include in records.html page summernote (WYSIWYG html editor). I found this npm module react-summernote and can't make editor works.
This is my code (try 1):
application.js
var ReactSummernote= window.ReactSummernote = global.ReactSummernote = require('react-summernote');
index.html.erb
<%= react_component 'ReactSummernote'%>
This is my code (try 2):
summernote.js.coffee
#RichTextEditor = React.createClass
render: ->
React.createElement ReactSummernote
index.html.erb
<%= react_component 'RichTextEditor'%>
and got this error
react.self-e3251ec….js?body=1:1285 Warning: React.createElement: type should not be null, undefined, boolean, or number. It should be a string (for DOM elements) or a ReactClass (for composite components). Check the render method of Constructor.
Some things worth checking;
I'm assuming your node packages are being installed to /node_modules/*. Make sure those packages are being added to your asset pipeline. I've used browserify to accomplish this.
Check in your browser console to see if the variable is being set (just type ReactSummernote. The result should be a function if it is imported correctly, or undefined if there is some problem).
If you've gotten this far and are still stuck, I would recommend triggering the render without using react-rails. It's easy enough to render the component onload of your page through vanilla javascript, and if it's the react-rails gem that is causing you problems, this will navigate around it easily with something like this;
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);

Ruby on Rails 5 - Turbolink 3 did not work correctly

I am using the Ruby on Rails 5 and use turbo-link as shown below:
<%= javascript_include_tag 'custom-plugins', 'data-turbolinks-track' => true %>
It is working fine with Ruby On Rails 4 but have issue with Ruby on Rails version 5. Once I click on back the js/css do not loaded correctly.
Any help will be appreciated.
As Refereed to Rails 5 Awesome features
Turbolinks has been part of Rails since version 4, probably one of the features that people hate it or love; there is no middle ground here.
With Rails 5 we will be receiving a new version that, with the help of HTML5 custom data attributes, we will expect better speed and rendering in our Rails applications.
The most significance change in this new version is the Partial Replacement feature. From the client side, we will be able to tell Turbolinks what content do we need to change/replace and what we don’t.
Turbolinks will look for HTML5 custom attributes and to decide the replacement strategy in our .
To trigger a replacement in the client side we could use or to update our . The difference between and is that the first one will issue a to the server to obtain the HTML that must be used to replace our while expects from us the HTML that should be used for its operation.
With both functions, we can pass a hash with an or an array of of HTML elements to or.
Action Result
Turbolinks.visit(url, { change: ['entries'] }) Will replace any element with custom attribute and any element with its id listed in change.
Turbolinks.visit(url) Will keep only elements with custom attribute and replace everything.
Turbolinks.visit(url, { keep: ['flash'] }) Will keep only elements with custom attribute and any element with its id listed in keep, everything else will be replaced.
Turbolinks.visit(url, { flush: true }) Will replace everything
We can trigger the same functionality from the server-side with and , both can receive , and as options but can also receive with or to force a redirect with or without Turbolinks.
Whether you like Turbolinks or not, this might be a good time to try out and find out if it could be a good fit somewhere in your application.
It is a common occurrence in for turbolinks with js. Turbolinks helps load a particular page a lot faster. But what it also does is stops the js from functioning sometimes. So while loading this particular page use this line
<%= link_to "example_page", example_page_path, :"data-no-turbolink" => true %>
or
write this in your layout
<body <%= "data-no-turbolinks='true'".html_safe if controller_name=="example_controller" && action_name=="example_page" %>>
to stop turbolink from working in this particular page.

Call a Ruby on Rails helper method from a JavaScript js.erb file

Is it possible to call a ruby helper method from within a js.erb file?
I have a helper in application_helper.rb called solve which makes a API call to a third party service, and they only have a ruby client.
The js.erb file isn't being run on the client side, it is being called from within a controller method and run server side as it is PhantomJS.
I call the JS file from the controller
Phantomjs.run('phantom.js.erb', url)
Then within the PhantomJS js.erb file I have tried
var response = '<%= solve(variable) %>'
which just sets the variable as the string <%= solve(variable) %>
I have also tried
var response = <%= solve(variable) %>
but that just seems to make the application hang and become unresponsive.
I have seen other questions similar to this. In those questions they are asking if it is possible to call it from client side JS which I know you need to use an ajax request to do so.
Is this possible?
Try this:
var content = '#{solve()}'
Need a bit more context for this question, but I'll try my best to answer:
Essentially, you wouldn't be able to access your application_helper methods outside of any .erb files. (ie. if you have application.js or any other js file in your pipeline and you are trying to <%= solve %> from there it wouldn't work - mainly because it isn't an .erb file)
There are a lot of ways and architecture to go about solving this, but here are two simple ones:
If you put the JS you want to evaluate inline on the same page as your partial/html.erb page by using <script> //JS ERB CODE GOES HERE </script> It will actually evaluate properly since it is inside of an erb file. However, this is generally looked upon as unclean...
What you probably want to do is pass the value (presumably) you want that comes from the "solve" application_helper in a 'data' attribute on the html element that it affects. By utilizing "unobtrusive javascript" in this way, you simply pass the value through markup and then in your JS you can get the variable by using jQuery code like this. Here's an example:
<%= link_to "test_link", root_path, :data => {:solve => solve } %>
Of course it doesn't have to be a link, any HTML element will do, and then in your jQuery:
$("#test_link").data("solve");
will return to you whatever output comes out of your "solve" method in the application_helper.
it can possible but there are different ways to do it. one way is define the method in helper_method in your controller and call it from your view. and another way is use the gon gem to access some controller value in your javascript. please check what is best for you please check the below links for more help
http://apidock.com/rails/ActionController/Helpers/ClassMethods/helper_method
https://github.com/gazay/gon
http://railscasts.com/episodes/324-passing-data-to-javascript

Rails asset pipeline: How to prevent caching of a specific asset

As stated in the title I want to prevent caching of a specific asset, namely a javascript file something.js.erb. The situation is like as follows:
Content of something.js.erb:
...
var something = <%= SomethingHelper.get_something.to_json %>;
...
It binds the value from SomethingHelper successfully but only once and unless the javascript file is edited by hand the value of var something is never assigned again.
This might be somehow expected but clearly doesn't meet my needs. Output of SomethingHelper.get_something changes according to call time. So I need to see up-to-date data in my compiled something.js file.
My exact need:
I don't want to disable asset pipeline caching as a whole
I only want something.js.erb to be rendered every time it is requested.
is this even possible?
Environment info:
Rails 4
Development mode
Rails' own server but will be on nginx on prod
Thanks
I can suggest 2 options:
1)Use inline js to set variable:
<%= javascript_tag do %>
window.something = '<%= j SomethingHelper.get_something.to_json %>';
<% end %>
2)Store the variable in your html and call it from your js:
#html
<body data-something="<%= j SomethingHelper.get_something.to_json %>">
</body>
#js
$("body").data("something");
You're marrying front-end business logic with data. This is inadvisable, and one of the reasons I don't use or recommend using ERB + JS for most scenarios (especially triggering behavior on response like Rails tutorials and guides are keen on doing). You are better off either…
Firing a request off to fetch the data from your JavaScript.
Provided the variable is going to be used on every page (or close to it) and is relatively brief, non-binary data, you can embed a meta tag in your layout with the relevant information.
For example:
# /app/views/layouts/application.html.erb
<%= tag :meta, name: 'something', content: #something %>
# /app/assets/javascripts/application.js
$('meta[name="something"]').attr('content');

How to avoid embedding javascript in views/show

I have a Rails app that uses javascript (Backbone) to show user specific data on each users profile page /views/users/show.html.erb. I do this by passing <%= #user.id %> as a data parameter to Backbone's fetch function, however, the only way I know how to get the <%= #user.id %> into Backbone's fetch function is by embedding the javascript in each views/users/show.html.erb page, which therefore allows Backbone to load different user specific info for each views/users/show.html.erb page. Although this works, it seems like the wrong way to do it, as I have read that I should not embed javascript like this. Furthermore, I am going to have to do it a lot, because I wish to display a lot of different kinds of data, more than you see below. So the show.html.erb page will be filled with javascript to make the app work the way I wish.
Question: how might I get #user.id into Backbone's fetch function for each user's show page without embedding javascript in the way that I've done. In the comments, someone suggest I use app/assets/javascripts/user.js, but I don't know how to get <%= #user.id %> into that file. #user.id is readily available in show.html.erb
<script type='text/javascript'>
$(document).ready(function() {
app.collections.awardCollection.fetch({ data: $.param({ user_id: <%= #user.id %> }) }).complete(function(){
app.views.awardCollection = new app.Views.awardCollection({ collection : app.collections.awardCollection});
app.views.awardCollection.render()
});
});
</script>
In order to understand how the views works, is that you can add as many extensions to a view as you want, and they will be parsed by the right library.
If you create a view like
my_view.haml.erb
It will be first parsed with ruby (erb), and then with haml, and will end in a html page.
You can create many views for js, usually you want to archive that when you do ajax, so you can end having a js view like:
my_view.js.erb
First ruby will be parsed (all the <% %> tags), that will end as plain text, and then the server will serve the .js file. But that's usually a common task for ajax.
If you have to render a html page where you want to put some js and you need some ruby code on it, what I usually do is to put the data in the html content with a hidden div.
You can put in any view (even on your layout if you want it to be globally available), something like:
<div id="user_id" style="display: none;"><%= #user.id %></div>
And then on a js (or coffeescript or whatever) you can just check the content of that div:
<script type="text/javascript">
var user_id = $("#user_id").html();
</div>
that's really useful when you want to debug or create tests for your js files, since its plain js and won't throw syntax errors.
I see the comment of LuĂ­s Ramalho and Gon is a good option, but I recommend use the following approaches:
If the from the variable is not going to change, print it with <%= %> under .js.erb files located in app/assets/javascripts (note that it will be cached until you restart your app)
If you need server variables the best way is to use Ajax
You can define functions on .js files on app/assets/javascripts and call those functions from the views
If you really don't want any Javascript code in the view, you can create the functions on a .js on app/assets/javascripts (corresponding to the view, for order), and use events and/or store the variables in hidden fields (or even use the data attribute from HTML5)

Resources