I've started playing a bit with the react-rails gem and am struggling to understand why I am unable to render a custom model attribute in react.js.
All database persisted attributes work fine, it's just custom getters that are returning nothing.
For the sake of simplicity, here's my User model with email, first_name and last_name as persisted attributes.
class User < ActiveRecord::Base
def full_name_and_email
"#{first_name} #{last_name} (#{email})"
end
end
When rendering my view, I initialize a react component:
<%= react_component 'Header', { data: current_user } %>
And within the Header component, if I try the following inside the render function:
var user = this.props.data;
...
return(
<p>Welcome {user.email}</p>
)
My rendered HTML is:
<p data-reactid=".0.0.2">
<span data-reactid=".0.0.2.0">Welcome </span>
<span data-reactid=".0.0.2.1">super_admin#email.com</span>
</p>
However if I try
render(
<p>Welcome {user.full_name_and_email}</p>
)
My rendered HTML results in
<p data-reactid=".0.0.2">
<span data-reactid=".0.0.2.0">Welcome </span>
</p>
I'm probably missing something pretty basic here so if anyone could please help me understand what's going on it would be great!
Thanks!
Answering my own question after re-reading the documentation for the gem:
props is either an object that responds to #to_json or an
already-stringified JSON object (eg, made with Jbuilder, see note
below).
And to add methods to to_json
current_user.to_json(methods: :full_name_and_email)
Related
I have this code which I don't really understand:
app\controllers\look_controller.rb
class LookController < ApplicationController
def at
#data_hash = params[:cruncher]
#cruncher = Cruncher.new(#data_hash[:crunch])
#data = #cruncher.crunch
end
def input
end
end
app\models\cruncher.rb
class Cruncher
attr_reader :crunch
attr_writer :crunch
def initialize(data)
#crunch = data
end
end
app\views\look\input.rhtml:
<html>
<head>
<title>Using Text Fields</title>
</head>
<body>
<h1>Working With Text Fields</h1>
This Ruby on Rails application lets you read data from text fields.
<br>
<%= start_form_tag ({:action => “at”}, {:method => “post”}) %>
Please enter your name.
<br>
<%= text_field (“cruncher”, “crunch”, {“size” => 30}) %>
<br>
<br>
<input type=”submit”/>
<%= end_form_tag %>
</body>
</html>
I do not understand what is the relationship between <%= text_field (“cruncher”, “crunch”, {“size” => 30}) %> and the model. What do text_fields attributes cruncher and crunch have to do with the model?
As I understand the params is a special hash that stores the data from the user, and by using #data_hash = params[:hash] inside the controller we store that data.
But what about this #cruncher = Cruncher.new(#data_hash[:crunch]), why do we now use #data_hash[:crunch]?
Why not just #data_hash?
Thanks.
if you look at he html produced by the input view, you'll see something like this for the text field:
<input type="text" name="cruncher_crunch" value="cruncher[crunch]" size="30" />
this means that the params hash, created when the form is submitted, and sent to the LookController#at method will be formatted like this:
{cruncher: {crunch: 'somevalue'}}
which is exactly the format that the Cruncher.new(#data_hash[:cruncher]) expects.
Its not that strange that you don't understand it.
This code is probably ludicrously old (.rhtml and start_form_tag put it at Rails 1 or 2) and really bad, it does not even run as there are two syntax errors as well as the quotes that look like an artifact from pasting the code into MS Word
# don't put a space before parens when calling methods in Ruby!
text_field (“cruncher”, “crunch”, {“size” => 30})
It would also give NoMethodError on #data = #cruncher.crunch.
In Rails 5 the same example can be written as:
class Cruncher
include ActiveModel::Model
attr_accessor :crunch
def crunch
# have no idea what this was supposed to do
end
end
class LookController < ApplicationController
def at
#cruncher = Cruncher.new(cruncher_params)
#data = #cruncher.crunch
end
private
def cruncher_params
params.fetch(:cruncher).permit(:crunch)
end
end
# I really have no idea what the actual routes are supposed to be
<%= form_for(#cruncher, url: '/look/at') do %>
<%= f.text_field(:crunch size: 30) %>
<% end %>
Its still just a strange and non RESTful example though. Sometimes garbage code is best left buried.
I do not understand what is the relationship between <%= text_field
(“cruncher”, “crunch”, {“size” => 30}) %> and the model. What do
text_fields attributes cruncher and crunch have to do with the model?
Nothing. There is no data binding. Its just a plain-jane text input.
But what about this #cruncher = Cruncher.new(#data_hash[:crunch]), why
do we now use #data_hash[:crunch]?
Because the author didn't know what they where doing. And probably had not figured out that you can pass hashes to your methods.
I have an API that returns info straight from the database and noticed Rails is storing a utf8 check mark and the authenticity token. What is the best way to remove this before it is saved?
{"achievements":[{"utf8":"✓","authenticity_token":"XDzrmNqNyt6OfhWpAzu3j7LhMd/sRcr/4oLsPxRMrDY=","achievement":"Big Guns",...
An example of the controller code:
def achievements
if params.include?(:ach)
#achievement = Achievement.new(params[:ach])
if #achievement.save
redirect_to action: :achievements and return
end
end
end
Example of the view:
<%= form_tag '', multipart: true do %>
<div class="four columns">Name <input name="ach[achievement]" type="text" ></div>
<div class="five columns">Description <input name="ach[description]" type="text" ></div>
<% end %>
Rather than try to write up a long post, I'll just note 3 things that should get you headed in the right direction. If it's confusing, comment and I'll explain further:
The utf8=✓ is inserted into your HTML when you use the form helpers in Rails.
You should not be handing the entire params object to your model.
You should always build your JSON API responses from known-safe attributes.
You can remove the key-pair from the params hash, by using in the reject method in the Hash class.
Example:
hash.reject! {|k,_| k.to_s.eql?("utf8") || k.to_s.eql?("authenticity_token") }
Dynamic fields must be enabled in the configuration.
As mentioned by coreyward, params should be selectively added to model, in this case.
This is what ended working best for me:
achievement_params = %w[achievement description]
#achievement = Achievement.new(params.select { |key,_| achievement_params.include? key })
I've a text attribute for a model named :settore_scientifico_progetto and three string attributes, :macrocat, :cat, :microcat:
class Modulo1 < ActiveRecord::Base
before_create :set_settore_scientifico_progetto
before_update :update_settore_scientifico_progetto
private
def set_settore_scientifico_progetto
self.settore_scientifico_progetto = "#{macrocat}\n#{cat}\n#{microcat}"
end
def update_settore_scientifico_progetto
self.settore_scientifico_progetto = "#{macrocat}\n#{cat}\n#{microcat}"
end
I'd like to put a new line where I typed \n but the code I posted gives me the output
macrocat cat microcat.
I would like it as follows:
macrocat
cat
microcat
The output is shown in show.html.erb:
<div class="form-field">
<h3>Settore scientifico:</h3>
<p><%= #modulo1.settore_scientifico_progetto %></p>
</div>
Rails has a helper specifically for this purpose called simple_format.
<%= simple_format #modulo1.settore_scientifico_progetto %>
This will output the following HTML:
<p>macrocat<br/>
cat<br/>
microcat
</p>
Which is rendered by your browser like this:
macrocat
cat
microcat
That seems to be exactly what you're looking for, and it takes care of sanitizing your HTML for you. (Options for customizing the output, e.g. changing the wrapping tag or HTML attributes, are listed in the docs.)
P.S. Using the gsub...html_safe method advocated above is very risky. If your app accepts user input for any of the attributes you're printing, calling html_safe on those values means they won't be sanitized by ActionView and a malicious user could inject code into the view that makes your app vulnerable to cross-site scripting (XSS) attacks. Here's a good primer on the ins and outs of html_safe in Rails.
I know views shouldn't be rendered from a model, but I cannot make sense of a better way to handle this problem.
I have a simple_form form that should show a list of object as checkboxes:
= f.association :results, collection: #search.results.all(order: 'cost'), as: :check_boxes
Like this it would only show the object next to a checkbox, but I need to display a bunch of more detailed information, so I'm defining the to_label method in my model like this:
class Result < ActiveRecord::Base
belongs_to :search
def to_label
"<div class='row'>
<div class='col-md-2'>#{supplier}</br><span class='label label-default'>#{brand}</span></div>
<div class='col-md-2'>#{desc}</div>
<div class='col-md-2'>#{model}</div>
<div class='col-md-1'>#{season}</div>
<div class='col-md-1'>#{qty}</div>
<div class='col-md-1'>#{expected_delivery}</div>
<div class='col-md-1'>#{msrp}</div>
<div class='col-md-1'>#{cost}</div>
</div>".html_safe
end
end
This shows some better formatted information, but still it's becoming very complicate to mantain and I will need at some point to add information like images etc. Of course it is also very wrong from a MVC point of view.
My idea is to create a partial in app/views, using haml as for the rest of my application and make my to_label method look like this:
def to_label
render "results/label"
end
Of course rails won't allow me to do that, but I have no better idea on how to implement it.
You should not access views from a model. Instead, create a helper with a function that receives the result object as a parameter (code not tested):
helpers/results_helper.rb
result_to_label(result)
render(:template =>"results/label", :layout => nil , :locals => { :result => result }).to_s
end
Then, from your view you call result_to_label(the_result) directly.
Update
In your simple_form, you can add the label_method with a lambda:
= f.association :results, collection: #search.results.all(order: 'cost'), label_method: lambda { |res| result_to_label(res) }
Bending rails that way will get you somewhere you don't wanna be. First of all you trying to render complex html layout by making simple_form thinking it's label for form control. Building custom form without use of simple_form makes more sense. Because rendering simple controls with automatic label generation is kinda simple form thing. And you are way beyond that.
So solution to your problem as it seems for me is defining to_partial_path on your class:
class Result < ActiveRecord::Base
def to_partial_path
'results/form'
end
end
and render your form with:
= render #results
it may be rendered inside of simple form you just need to build form controls with rails or simple form helpers.
What I'm trying to do
I am doing a search across several different models using Sphinx in my rails application, and it successfully returns the weighted results (including several different types of objects, let's say posts and users). I currently am testing this with (many.each { |one| one.inspect }), and that works fine.
I am now trying to write a method for each model with the same name that will output my objects in the format that I choose. For example, instead of one.inspect, I'd like to put one.print_pretty and have it output html that neatly prints out the information specific to that type of object. Using the user example, I'd like it to output something like:
<div class="small-2 columns">
<%= image_tag(one.userpic.avatar.url) %>
</div>
<div class="small-10 columns">
<div class="row">
<h3 class="subheader"><%= one.first + " " + one.last %></h3>
</div>
<div class="row">
<%= one.tagline %>
</div>
</div>
By contrast, on my post model, it should output something different that's applicable to the post attributes and priorities.
Question
How do I write a method for a Rails model that will output an html-styled chunk to code or otherwise style output that's of an unknown object type (from a limited pool)?
Would something like this work for you? You could create a separate partial that would show a different output for each object class. In your view, pass the object into a helper method function. For example:
<%= render_partial_for_this_type_of_object(your_object) %>
Then in a helper method, have something like:
def render_partial_for_this_type_of_object(object)
if object.class.name == "ClassA"
render 'class_a_partial', :object => object
elsif object.class.name == "ClassB"
render 'class_b_partial', :object => object
end
end
So to summarize, you are passing the object from the view to the helper, identifying what class of object it is, then passing that object to the appropriate partial based on its class that will then be rendered in the view.
I'm sure there are other ways to do this but this worked well for me in an app I just built.