Reputation: 920
I have my js.erb file called remotely from my Rails 5.0.4 app.
<% if @admin_invitation.present? && @admin_invitation.valid? %>
<%my_view = j (render partial: "shared/invitation_card",:user => @admin_invitation.invited_id, :email => @admin_invitation.invited_email, :type => "admin" ) %>
$(".invites.admins .row").prepend("<%=my_view %>");
<% end %>
App.refresh( "<%= j(render 'shared/alerts') %>" );
It works as expected when i hardcode the values in the partial, but when i feed them in as locals from this js.erb file, they dont come through (nil). Worth noting that I render the partial from a a non-remote haml view and it works just fine.
Here are the ways of rendering i have tried (I know some are outdated, but just for a sanity check)
render partial: "shared/invitation_card", :locals=> { :email => "[email protected]", :type => "admin" }
render partial: "shared/invitation_card", object: "[email protected]", as: "email"
render partial: "shared/invitation_card", email: "[email protected]", type:"admin"
render partial: "shared/invitation_card", locals: { email: "[email protected]", type: "admin" }
Anyone else have issues with rendering haml partials from a js.erb in rails 5?
EDIT:
Here is the controller method calling the js.erb above.
def create
if params[:invited_status] == "temporary_invitation_user"
result = CreateGroupInvitation.call(invitation_model: GroupFollowInvitation, group_id: params[:group_id], inviter: current_user, invited_email: params[:invited_email], notification: "created_group_follow_email_invitation")
else
result = CreateGroupInvitation.call(invitation_model: GroupFollowInvitation, group_id: params[:group_id], inviter: current_user, invited_id: params[:invited_id] , notification: "created_group_follow_invitation")
end
if result.success?
@invitation = result.invitation
@invited = params[:invited_id] == "null" ? params[:invited_email] : User.find(params[:invited_id])
flash.now[:success] = "Your follow invitation was sent"
else
if result.error_message.present?
flash.now[:info] = result.error_message
else
flash.now[:error] = "An error happend"
end
end
respond_to do |format|
format.js { render layout: false }
end
end
Upvotes: 1
Views: 1800
Reputation: 5942
You did not specify how you are calling js.erb
, but the best approach I suggest is the following.
STEP 1 - BUILDING THE VIEW
Either start by including a link, form or button with the remote: true
option in your view
for example
<%= form_with(model: @invitation) do |f| %>
...
<% end %>
STEP 2 - UNDERSTANDING THE ROUTING
The resources routes in route.rb
for :invitations
generates the following routes:
new_invitation GET /invitations/new(.:format) invitations#new
POST /invitations(.:format) invitations#create
and your form in your view invitations/new.html.erb
on submit will perform a POST
request to the url /invitations
.
EXPLANATION OF AJAX
In webdevelopment you can do 2 types of requests between Server and Client,
Synchronous or asynchronous requests. In a Synchronous request, the client/browser will perform an HTTP request
to the backend server, which will answer with a response
. The browser will stop and start loading until he does receive the full response.
In a asynchronous request, the request and response
cycle will not include the browser stopping to load.
Your javascript
file is just one file included in the client/browser asset pipeline, which are all the js
, css
, images
and fonts
that are downloaded by the browser when the user go to visit your page at http://yourwebsite.xyz
and performs a get
request to that url
. Your server performs a response that include all those information.
So that js
file can not find your variable which is from your server
application or from a query in your database
. The two things are separated, one is your server and the other is the client.
If you want to have that variable and add it to the client/browser dynamically and asynchronously
, then you need to use AJAX
.
A get
request needs to be performed from your client to your backend server, the response will include the @invitation
variable in the json
format
STEP 3 - BUILDING YOUR CONTROLLER
The Rails router receives a POST
request at /invitations
and redirects the request to the controller invitations
action create
. The parameters are passed through the ActionController::Parameters
object params
.
The Server running Ruby on Rails responds to the request with 3 different formats:
HTML to render the view
JS to execute script code on the front end. The JS file invitations/create.js.erb
was already downloaded by the client through the asset pipeline, this is just a command to execute it.
JSON to send the @invitation
object in json
format, it will be available at /invitations.json
# app/controllers/invitations_controller.rb
# ......
def create
@invitation = Invitation.new(params[:invitation])
respond_to do |format|
if @invitation.save
format.html { redirect_to @invitation, notice: 'Invitation was successfully created.' }
format.js
format.json { render json: @invitation, status: :created, location: @invitation }
else
format.html { render action: "new" }
format.json { render json: @invitation.errors, status: :unprocessable_entity }
end
end
end
STEP 4 - APPENDING RESULT ASYNCHRONOUSLY TO THE PAGE
In invitations/create.js.erb
you can include your js code and you can use the instance variable from the invitations#create
action
$("<%= escape_javascript(render @invitation) %>").appendTo("#invitations");
more info about working with AJAX and Rails
Upvotes: 1
Reputation: 920
I found the answer. It is simple actually
render partial 'view' locals: {k1: v1, k2:v2} does not give you a local hash. It simply defines the local variables k1 and k2 for use in your partial. You do not need to extract them from 'locals' hash.
render 'view' locals: {k1: v1, k2:v2} does give you a local hash with k1, v2 inside. They are not defined until you extract them. This inconsistency threw me off. my code was prepared for them to be inside the locals hash.
Upvotes: 1