From the controller, I send json, which contains a flash message that is rendered in a custom flash via coffeescript. I want to allow <br>, <b>, etc. tags in the HTML rendered. I suppose if I don’t escape at the server, I’d have to send a flag akin to the html_safe so that the coffeescript knows if the HTML should or should not be escaped. Currently, my client code just inserts the HTML when rendering the dialog.
Here’s the coffeescript flash code I wrote below. It assumes that the msg is html (good one to refactor the name to htmlMsg)
Change param message in method postFlashMessage to htmlMsg, suggesting that caller should be sure to escape.
Change the json passing of flash_message to client to have optional param html_safe, and change the flash method in coffeescript to always escape html unless msg passed is an object composed of:
{ msg: "Something to display in <b>Bold</b>."
html_safe: true }
I think option 2 is better, as it’s very easy to forget the need to escape when rendering. Unfortunately, to change this, I’d have to scrub my code very thoroughly.
Do option 2, but don’t do any escaping by default, requiring one to pass in the message as:
{ msg: "Something to display in <b>Bold</b>."
html_safe: false }
The problem with #3 is that making html escaping the exception goes against the grain of rails.
# type: warning, danger || error, notice, success
# parentSelector: place to fill in flash, or else .flash-area
@postFlashMessage: (message, type, flashAreaSelector) ->
flashAreaSelector ||= ".flash-area"
type ||= "notice"
type = 'danger' if type == 'error'
type = 'info' if type == 'notice'
type = 'warning' if type == 'alert'
html = """
<div class="alert alert-#{type} alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
#{message}
</div>
"""
$(flashAreaSelector).html(html)
My way to send json to client is to create js.erb file. If you have a controller UsersController with the action index you can have a view users/index.js.erb. In this file you can do :
@Guirec_Corbel, in this case, I have service objects that returning status to the controller via a class I call ControllerResponse. This class encapsulates the result of the service and is invoked via a JSON ajax call, and it should send back a JSON response.
@Guirec_Corbel Thanks for the detailed response. Your technique is super if you only need to display a flash message result. In my case, I needed to pass back data values as well as set the http status on the response, so that the Coffeescript client can take the appropriate action. I ended up creating this class, ControllerResponse, which the service objects pass back to the controller.
# Return value object from Service Objects and the Models back to the controller.
# Rather than throwing an exception from the Service Objects, this class will encapsulate any
# error status to send back to the client.
class ControllerResponse
attr_reader :flash_message
attr_reader :flash_type
attr_reader :http_status_code
attr_reader :data
attr_reader :redirect_path
attr_reader :session_data
# http_status can be either symbol or number
# Pass in flash_message as a html_safe string if you do not want it escaped.
def initialize(flash_type: :notice,
flash_now: false,
flash_message: "",
http_status: :ok,
data: {},
redirect_path: nil,
session_data: {})
@flash_type = flash_type
@flash_now = flash_now
@flash_message = flash_message
@http_status_code = Rack::Utils::status_code(http_status)
@data = data
@redirect_path = redirect_path
@session_data = session_data
end
def self.symbol_to_status_code(symbol)
Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol]
end
# Currently supports applying the:
# 1. session_data
# 2. flash, flash_type, and flash_now
# 3. redirect_path
# Does not apply the http_status_code, nor the data
def apply(controller)
@session_data.each { |k, v| controller.session[k] = v }
if @flash_message.present?
if @flash_now
# NOTE: There is no need to escape, as Rails will do that unless the flash_message is html_safe
controller.flash.now[@flash_type] = @flash_message
else
# NOTE: There is no need to escape, as Rails will do that unless the flash_message is html_safe
controller.flash[@flash_type] = @flash_message
end
end
controller.redirect_to @redirect_path if @redirect_path.present?
end
def set_flash(flash)
# NOTE: There is no need to escape, as Rails will do that unless the flash_message is html_safe
flash[flash_type] = flash_message if flash_message.present?
end
# Returns the html escaped version of the flash.
# This method should be used when setting value in json.
# In the case of setting the flash on the controller, there is no need to escape, as Rails will do
# that unless the flash_message is html_safe
def flash_message_escaped
if @flash_message.html_safe?
@flash_message
else
CGI::escapeHTML(@flash_message)
end
end
end
I’m not sure to understand the problem. You have some action in a javascript file which do an ajax call. In a callback, you want to display a message which can contain html. The callback can also change some properties of the object. Am I right?
The first thing I think is to change the properties in a js.erb file like this :
// show a message
myObject.doSomething(withValue)
You have access to your object in rails callbacks.
Second thing. Why you don’t always escape text like this :
"test\<br\>".html_safe #will send "test<br>"
"test<br>".html_safe #will send the same text
@Guirec_Corbel, those will work. However, when I need data return to set values or to conditionally take some action, I prefer to have this code with the request, rather than with the js.erb file.