I the top of my application template body I have:
<%= turbo_frame_tag :toasts %>
Which I use to display messages (https://github.com/excid3/tailwindcss-stimulus-components)
In my controllers I have an update_index patch route so that I can do some updates from the index page. The controller and corresponding turbo rendering works on the UI. In that update_index controller action I have this:
#project.broadcast_append_later_to #project, target: 'toasts', partial: 'shared/toast', locals: { user: current_user, type: :notice, message: "Project #{#project.name} Updated" }
In this action (for context is it matters this was the first model I added to my app). When I update via that action the toast is displayed as expected. The crazy part here is if I add this same logic to my other models the toast gets appended TWICE every-time.
I checked the logs and I confirmed that Sidekiq is not firing a job
twice
I tried broadcast_append_to vs later and the same result If I
comment this out both toasts are not displayed on the update so I
know it's not being triggered somewhere else
I confirmed that I don't have any broadcasts in my models the exact same line of code in the default update action only displays once
I have gone through model and controllers and they are almost identical except for the odd items here and there - nothing related to this toast (which simply displays the message passed in)
Some context to perhaps trigger ideas:
the Project model was the first model I added to the app and the rest came afterwards
all the other models are descendants of Project directly or indirectly via belong_to / has_many etc.
the issue only occurs in that update_index action
This seems like a wild goosechase and looking for help - perhaps someone may be able to point me to something obvious I am missing here.
This happens when you add a turbo_stream_from tag twice 🤦♂️
Related
I've generated a simple rails app using scaffolding. In the user model I have added a validation for the presence of name. I am using debug(#user.object_id) and showing at the top of my user edit page in edit.html.erb
I've purposefully left the name blank and tried to submit the form. The usual errors are rendered but every time I submit the form, the #user's object id changes. Could someone please explain why the object_id is changing? My assumption was that the #user is still the same object (since we're in the same page, just adding errors and re-rendering the edit.html.erb on failed update)
You probably confused by the fact, that render :edit in the update method just renders the edit template, but doesn't redirect to the edit page - that's right.
But, actually this is what happens in your scenario:
edit page is visited, #user assigned in the edit method of UsersController
form is submitted, update action is called and found #user is assigned in the update method and renders edit template
Thus, on submitting a form different method is called and the state changes
No, your assumption is not correct. HTTP calls are stateless, meaning that state does not persist between calls (i.e. every call is independent of each other). Every time your form is submitted, a new object is created and assigned to the variable #user. Since a new (and different) object is created during each call, their object_ids will be different.
My problem is very similar to Rails edit.html.erb ArgumentError
After reading through that page (and about 7 others with the same problem) multiple times and spending several hours trying to fix this, I haven't been able to. The first paragraph worth of error messages on that linked page is identical to the one I'm receiving every time I refresh a user's page.
I'm an absolute novice who's following along with Michael Hartl's Ruby on Rails tutorial and am trying to make one small change. Instead of rendering the form for new microposts on the home page, I'm trying to do so on the profile page of users who are logged in. After logging into user #1 and viewing that user's profile page at [URL]/users/1, I receive the aforementioned error message. When viewing that user's profile page while logged out, I don't receive any error messages, and the micropost form is not displayed, as intended. I apologize for asking such a stupid question when I'm sure the answer is obvious to everyone who reads this, except for me. But what am I doing wrong?
Error message details:
ArgumentError in UsersController#show
First argument in form cannot contain nil or be empty
app/views/shared/_micropost_form.html.erb:1:in _app_views_shared__micropost_form_html_erb'
app/views/users/show.html.erb:9:in _app_views_users_show_html_erb'
Parameters:
{"id"=>"1"}
config/routes.rb:
resources :microposts, only: [:create, :destroy]
views/shared/_micropost_form.html.erb: http://imgur.com/a/84nPO (Picture #1)
One of many iterations of views/users/show.html.erb that I've tried to use. I've tried so many different things that I believe it doesn't matter which attempt I screenshot; the error message is always the same: (Picture #2 in the previous album; I can only paste two links in this question.)
"show" action under controllers/users_controller.rb, if it's relevant: (Picture #3 in the previous album; I can only paste two links in this question.)
You're getting that error because #micropost is nil in your partial as it's not actually defined anywhere.
By convention, a local variable with the same name of the partial is created for each item in the partial collection. So, in this case the variable name locally scoped to your collection partial is micropost:
in _micropost.html.erb
<%= form_for micropost do |F| %>
Note that your partial will actually need to be named _micropost.html.erb
If you look at the action that originally had your form, you'll see somewhere in that controller that a variable is being set to feed your form with the needed instance variable. If you want to render the form on a different view, you need to make sure that the action that is now responsible for it has it.. (and you can take it out of the original one if you haven't already.
It's been a while since I've used Rails and I think I've gotten a little rusty. Is there a way to do this?
I'm trying to make a messaging feature that allows one user type to message another. I want the button to display on the User index page and the user show page. When the button is clicked a modal will popup with a form contained therein.
Currently I've made a Message model with three columns: user_type1_id, user_type2_id and message_body.
Should I make a distinct controller for this new model? Or should I put the logic in the controller of user_type1 (the usertype that will be messaged)?
Any other suggestions would be welcome.
Controllers are there primarily to get data from the database and get it ready for the views. So if you have user#index and user#show pages, then you should use the UsersController for all the logic associated with those views, even though it uses other modals. It really is the "Rails Way". If, however, you were to create a message#index page, then you should create the associated MessagesController.
Also, there is nothing wrong with creating a partial and sticking in the messages view directory (the filename would be, say, messages/_form.html.erb). Then, whenever you needed that form (throughout the entire site), all you would need to do was type:
<%= render 'messages/form' %>
I cannot explain why, but somehow my form_tag form is skipping streight to my view and completely bypassing my controller action.
This is my form_tag:
<%=form_tag url_for(:controller=>"orders", :action=>"finalize_order"), :method=>'post' do%>
<%=hidden_field_tag "cc_id_selection"%>
<%=hidden_field_tag "address_id_selection"%>
<%=submit_tag "Checkout", :class=>"btn btn-primary"%>
<%end%>
And here is my controller action:
def finalize_order
#selected_user_card_id = UserCard.find(params[:cc_id_selection])
begin #in case they chose pickup
#selected_address = Address.find(params[:address_id_selection])
rescue
#selected_address = params[:address_id_selection] #we can add options besides pickup if we'd like
end
end
This is what is logged in my console (with some ip stuff taken out) I tried putting a trace in the controller and don't see any sign of it:
Started POST "/finalize_order" Processing by OrdersController#finalize_order as HTML
I see at the bottom of the error page that the two params were successfully posted. Yet when I submit I get error in the next view indicating no param was instantiated in the controller. Even stranger, when I comment out the action I get the same exact results! I even tried using a route, anf get the same results. It seems I am completely skipping the action and going streight to the view. What might cause this bug?
I suspect you've accidentally made your finalize_order method private in your controller. If it's a private method, it won't be called, and the view will be rendered directly. This is a little-known feature of Rails - the action doesn't actually have to exist.
If the method is private, (the private keyword will be above it in the controller somewhere) then move the action to above the keyword to make it public.
Along the lines of what sevenseacat suggested, but the actual problem is that I accidentally had two finalize_order methods. I guess rails couldn't decide which one to run, so it ran neither.
In my current project I have a couple instances where I have a re-usable form that exists inside a rails partial. This form submits to a specific controller via ajax (:remote => true). The controller does some stuff and then returns back the appropriate js.erb to modify the page via javascript.
This works fine for when I have a single view. But the problem seems to happen when this re-usable partial exists on multiple views. In view 1 I might want to issue a completely different set of javascript commands then in view 2.
As a concrete example, say I have a comments controller that has the normal CRUD operations.
I now have partial called _comments_box.erb. This _comments_box.erb contains the ability to submit a comment via a simple line:
- form_for comment, :url => post_comments_path(post), :remote => true do |f|
This submits to a comments_controller.rb create method which looks somethings like this:
def create
... do some stuff, like create a new comments model
respond_to do |format|
# will respond with create.js.erb
format.js
end
end
The create.js.erb in turn adds a comment to the view, perhaps doing a bunch of other updates to the DOM.
Say I render the _comments_box.erb within a view called post_summary.erb. Now I have another view, post_detail.erb that requires the same _comments_box.erb. However the post_detail.erb requires me to update completely different divs on the DOM in response to a new comment.
I need to create a different JS response for each instantiation. So I can either:
Create an alternate controller method, say create_2. Pass in some parameter to the _comments_box.erb from post_detail.erb to the _comments_box.erb partial so it knows which controller method to fire. This will allow me to have a separate file _create_2.js.erb that will allow me to manipulate the post_detail.erb view independently.
Forget about using js.erb altogether and just use plain old AJAX and get back JSON, and handle the javascript manipulation completely on the client-side.
It seems option 1 allows me to continue to use the UJS supported by Rails which is nice. But also means I probably will be adding a lot of duplicate code everywhere which is annoying. Is there a way for me to do this elegantly while continuing to use UJS?
That's exactly the purpose of Apotomo: http://apotomo.de/
Here is it's own description:
Apotomo is a true MVC widget framework
for Rails. Widgets are based on Cells
and provide reuseable view components.
Having bubbling events, they know when
and how to update themselves via AJAX!
Working with Apotomo widgets almost
feels like developing GUI components –
in a Rails environment.
Have a try, it's great.
I'd not recommend using UJS for frontend apps: server shouldn't take care of client side business. I agree it's useful and clean but it lacks performance and thus should be kept for backend stuff (RJS will move into a gem, see here: http://weblog.rubyonrails.org/2011/4/21/jquery-new-default).
That said, back to the solutions you expose:
1) I think you won't need an extra controller, you'd just have to pass additional params in order to know from where to query came from. A hidden_field could do the trick. With this info, render the good js.erb file
format.js { if condition
render "create.js.erb"
else
render "create_2.js.erb"
end
}
2) I'd go for it and return json but you'll face the same problem: knowing from where the request comes from.
A better solution (than using a hidden_field) might be to check the request.referer in your controller action. This way you leverage the fact that each context has a unique URL, and don't have to explicitly specify another unique value when rendering your widget partial.