Dealing with 2 models in 1 view - ruby-on-rails

I have two models I'm trying to interact with in 1 view. The first model is Room and the second is Availability. 1 Room has many Availabilities.
On 1 Rooms page, I render out availabilities through a partial like this:
<% #room.availabilities.where("booking_id is NULL").order("start_time").each_with_index do |a|%>
Beside each availability I have a button to delete and update. The delete worked out fine since it was in my loop so I could do something like this.
<%= link_to("Delete", availability_path(a.id), method: :delete, remote: true) %>
But I'm having trouble with edit. I'm trying to do it through a modal which doesn't have access to the 'a' variable from the loop. So I'm not sure how to pass in the unique availability to the form
Button:
<!-- Edit button -->
<button type="button" class="btn btn-xs btn-primary" data-toggle="modal" data-target="#editAvailability"><i class='fa fa-pencil'></i></button>
<!-- Edit Availability Form -->
<%= simple_form_for #facility.availabilities.find(???), method: :put, remote: true do |f| %>

You should be able to do this with AJAX. I have an app that has a modal dropdown that lets me toggle pieces of equipment in or out of service with a button in the dropdown. You can use a route that points towards the Availabilities controller that renders a form in the modal. Mine is simple in that it just toggles, but I don't see why you can't use a form. I would move your query out of your view and make a helper that gives you the results of your query in a variable.
I can provide more detail but need to see a lot more of your current code. If you can post your controller and all of your view code for the modal. I don't know understand why you don't have access to the variable you need in the modal? If the modal has an AJAX call you should be able to populate it with any data available to your controllers.
edit
Take a look at this: https://coderwall.com/p/ej0mhg/open-a-rails-form-with-twitter-bootstrap-modals . Be sure and read the links at the end of this article, it has some StackOverflow examples that are spot on. I'm thinking link_to is the way to go:
<%= link_to 'Update, availabilities_edit_path(a.id), {:remote => true, 'data-controls-modal' => "modal-window", 'data-backdrop' => true, 'data-keyboard' => true} %>
This should open a modal and ask the availabilities#edit controller for a JS response. Make sure you have an edit action, I don't see one in your controller:
availabilities.rb
def edit
#availability = Availability.find_by(id: params[:id])
respond_to do |format|
format.html
format.js
end
So the JS call will cause it to look in the /views/availabilities/ folder for a file called edit.js.erb with content like:
$('#editAvailability').html('<%= escape_javascript(render :partial => 'editForm') %>');
In your edit form you you now have the #availability instance variable to use in your form:
<%= simple_form_for #availability, method: :put, remote: true do |f| %>
...
So your modal gets built using an AJAX lookup that returns a form built using the needed instance variable. I'm putting this together from stuff I've written and other stuff I've read, so there will probably be some errors and tweaking to get your code working. Let me know how far this gets you.

You can store all the information necessary in your update button within your loop like this:
<%= link_to("Update", method: :put, remote: true, data: { attr1: a.attr1, attr2: a.attr2 }) %>
Then every button knows what entity they are operating for.

Related

Rails 4 - link back to form without reloading

Rails newbie here...please be kind...
I have an app that generates a unique paragraph of text (sampled from arrays and made unique based on criteria entered in _form.html.erb, rendered from the criteria/new page). On the criteria/show page, I have 2 links, one to generate a new unique paragraph using the same criteria, and one to return to the form and edit the criteria.
For the second link, I want to retain the criteria previously entered, as the user may want to only change one entry and not have to re-enter all of it. I've found plenty of information regarding ajax calls, respond_to, remote: true, but haven't found this exact answer or if I have, my brain is TIRED and failed to comprehend.
I've seen the suggestion in a similar question (How to come back to form without reset the values?) which talks about repopulating the data: "Just add the parameters (/myForm?param1=1&param2=2&param3=3) to the button url that leads back to the search form. Then populate the fields using the parameters. No need to use session variables." Sadly, I'm unclear about how to do implement this.
Would someone please be so kind as to walk me through either (preferably the simplest!) way of doing this?
Current links on show page (commented things are those that I tried unsuccessfully:
<%= link_to 'Generate Another!', criteria_path %> #regenerates text with current criteria
<%= link_to 'Enter New Information', new_criterium_path %> #this is the link I'm trying to change
<%#= link_to 'Enter New Information', new_criterium_path, :remote => true %>
<%#= link_to 'Enter New Information', { :controller => 'Criteria', :action => "new" } :remote => true %>
Controller new action (commented until I can make it work):
def new
#criterium = Criterium.new
#testing ajax stuff
# respond_to do |format|
# format.html
# format.json
# end
end
There's lots of talk about needing a new.js.erb file (or would that be show.js.erb?) and a div tag in my show(?) page but again, brain TIRED. I've attempted some of these things, then deleted from the code so as not to gak things up too much and be unable to remember what I did.
Please tell me what else you need from me in order to answer. Many thanks in advance!
Looks like you need to pass back params to your 'new' action, and then use them when instantiating Criterium:
<%= link_to 'Enter New Information', new_criterium_path(criteria: params[:criteria]) %>
def new
#criterium = Criterium.new(criteria: params[:criteria])
end

Fire up custom action when user clicks 'Search" on RAILS 3.2 form?

Is it possible to fire up an custom action when user clicks 'Search' button on search form?
There is an mechanism in our app to save every URL the app has hit. In our search form, when clicking 'Search' button, there will bring up the search result page. The problem is that the URL for the search result form was not saved. The Back button brings back the search page (for setup search params) instead of the search result page (because its URL was not saved).
Here is the search form for model configs:
<h4>Search Form></h4>
<%= simple_form_for #config, :method => :get, :url => search_result_configs_path do |f| %>
<%=render :partial => 'search_params', :locals => {f: f} %>
<%= f.button :submit, t('Search') %>
<% end %>
The URL for the search result looks like this (with the search params set by user) after user clicks Search button:
http://localhost:3000/configs/search_results?utf8=%E2%9C%93&engine_config[start_date_s]=&engine_config[end_date_s]=&engine_config[engine_id_s]=1&engine_config[argument_name_s]=&engine_config[commissioned_s]=&commit=%E6%90%9C%E7%B4%A2
This is the URL we would like the app to remember. We figure we need custom action triggered when a user clicks 'Search' button. Is it possible?
Route
Firstly, calling a custom application is actually quite a simple process - you just need to call its route:
#config/routes.rb
resources :search do
collection do
get :custom_action
end
end
This will allow you to use the likes of form_tag to call the custom route:
#app/views/your_controller/view.html.erb
<%= form_tag search_custom_action_path, method: :get do %>
...
<% end %>
--
Form
Secondly, you're using simple_form for your search form.
This is completely fine, but the problem you have here is that when you use this, it has to have a ActiveRecord object to populate the form with. This is probably where you're getting confused, as to do this, you need ot make sure #config is available every time you load that form, which I imagine can be a lot.
We've created a search form here:
Although in Rails 4, we used a form_tag for this form, as it allowed us to create & display the form where-ever we need in the app. This allows us to pass the required params through the form & access them on the other side
--
Params
You mention you want to "save the URL" - what do you mean by this?
Surely you'd prefer to save the params?
If this is true, the way to do this is actually relatively simple - you'll get access to the params hash in your controller when you send the request through:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
def custom_action
params[:your_param] #-> this is accessible here
end
end
The bottom line is if you wanted to save the query strings, you'll have to create a model called Search or similar, allowing you to pass the params through when you process the custom action in your controller, just like you would any other ActiveRecord object

Button "New Item" in Rails 4

I have a page that works with various models.
One of the items is "Language", where I select the language and level. But I can only insert a single language. I wonder what the best way to insert a "New" button to add another language, even if the edit page (because even though I need to include in the edit page too)
I'm using accepts_nested_attributes_for and simple_form.
Have tried several solutions but could not find any similar tutorial with what I need.
When you mention you have a page which works with various models, you need to remember views != models. In rails, views are used to show data you have defined in your controllers.
When you ask about inserting a new button to add a new language, this will be entirely dependent on your interface, and how you wish it to work. A good example would be this:
#app/views/languages/_new_lang.html.erb
<%= form_for Language.new, url: language_path do |f| %>
<%= f.text_field :name %>
<%= f.submit "Create" %>
<% end %>
A better way to do this will be to use ajax & render a layout-less element on your page:
#app/views/controller/your_view.html.erb
<%= button_to "Test", new_language_path, remote: true, id: "test" %>
#app/controllers/languages_controller.rb
Class LanaguageController < ActiveRecord::Base
layout Proc.new { |controller| controller.request.xhr? ? false : "application" }
end
#app/assets/javascripts/application.js.erb
$(document).on("ajax:success", "#test", function(data) {
$(data).appendTo("body");
});
This will allow you to send the required data through to your system to create a new language. Can be improved, so if you want to use it please let me know in the comments

How do I create a button that increments an attribute of a model and can be pressed once per browser?

An apartment_listing has many reviews, and a review belongs to an apartment_listing.
In the file views/apartment_listings/show.html.erb, I show a list of reviews for that particular apartment_listing. These reviews are generated with the partial view apartment_listings/_review.html.erb like so:
<%= render :partial => "review", :collection => #apartment_listing.reviews %>
In _review, I want to have a button that, when pressed:
Increments that review's helpful_count attribute.
Makes it so that it cannot be pressed again while in the same browser - probably using cookies.
I feel like the former shouldn't be too hard to figure out, but it's got me beat. I'm really not sure where to start with the second goal.
EDIT: I managed to update the review's helpful_count attribute with this code in apartment_listings/_review.html.erb:
<%= form_for review, :method => :put, :remote => true do |f| %>
<%= f.hidden_field :helpful_count, value: (review.helpful_count + 1) % >
<%= f.submit 'Helpful?' %>
<% end %>
However, I'm not sure if this is the best way to do it, and I'd like to be able to disable the button after it is clicked.
Your code for updating helpful_count has the potential for problems. Imagine two users have loaded an apartment on their web page. One of them marks it helpful, and the next one does as well. Since when they initially loaded the page, helpful_count was the same, after both of them click helpful, the count will only be incremented by one: it would be updated twice to the same value.
Really, you want to create a new action, probably under the reviews resource for an apartment. That action could use ActiveRecord's increment method to update the helpful_count (technically there's still a race condition in increment!, you'd encounter it much less often) http://apidock.com/rails/ActiveRecord/Persistence/increment%21
Cookies seem like a reasonable solution for the latter problem. Simply bind to submit on the form with jQuery, and create the cookie in the handler.
What does the code look like in your reviews controller? More experienced RESTful coders might be able to speak more coherently on this, but the way I see it, incrementing the helpful_count attribute should be an action sent to the reviews controller. That way, you can create a link that performs the action asynchronously.
For example, inside _review.html.erb:
<% collection.each do |review| %>
<%= link_to "Mark as Helpful", "/apartment_listing/#{#apartment_listing.id}/reviews/#{#review.id}/incHelpful?nonce=#{SecureRandom.rand(16)}", :remote => true, :method => :put %>
# ... Do something cool with your review content ...
<% end %>
Inside your ReviewsController class:
def incHelpful
unless params[:nonce] == session[:nonce][params[:id]]
#review = Review.find(params[:id])
#review.helpful_count += 1
#review.update_attributes(:helpful_count)
session[:nonce][params[:id]] = params[:nonce]
end
render :nothing
# Optionally return some javascript or JSON back to the browser on success/error
end
Inside /config/routes.rb:
put "apartment_listing/:apart_id/reviews/:id/incHelpful" => "reviews#incHelpful"
The main idea here is that actions that edit a resource should use the PUT http method, and that change should be handled by that resource's controller. Rails' built-in AJAX functions are engaged by setting :remote => true inside the link_to helper. The second concept is that of a nonce, a random value that is only valid once. Once this value is set in the user's session, subsequent requests to incHelpful will do nothing.

Rails: Multi-submit buttons in one Form

Say I have an Article model, and in the article 'new' view I have two buttons, "Publish" and "Save Draft".
My question is how can I know which button is clicked in the controller.
I already have a solution but I think there must be a better way.
What I currently used in the view is:
<div class="actions">
<%= f.submit "Publish" %>
<%= f.submit "Save Draft", :name => "commit" %>
</div>
So in the controller, I can use the params[:commit] string to handle that action.
def create
#article = Article.new(params[:article])
if params[:commit] == "Publish"
#article.status = 'publish'
// detail omitted
end
#article.save
end
But I think using the view related string is not good. Could you tell me another way to accomplish this?
UPDATE: Since these buttons are in the same form, they're all going to the 'create' action, and that's OK for me. What I want is to handle that within the create action, such as give the Article model a 'status' column and holds 'public' or 'draft'.
This was covered in Railscast episode 38. Using the params hash to detect which button was clicked is the correct approach:
View:
<%= submit_tag 'Create' %>
<%= submit_tag 'Create and Add Another', name: 'create_and_add' %>
Controller:
if params[:create_and_add]
# Redirect to new form, for example.
else
# Redirect to show the newly created record, for example.
end
it can also be done on the form_for helper like this
<%= f.submit "Publish",name: "publish", class: "tiny button radius success" %>
<%= f.submit 'Mark as Draft', name: "draft", class: "tiny button radius " %>
and the logic is the same on the controller
if params[:publish]
// your code
elsif params[:draft]
// your code
end
We solved using advanced constraints in rails.
The idea is to have the same path (and hence the same named route & action) but with constraints routing to different actions.
resources :plan do
post :save, constraints: CommitParamRouting.new("Propose"), action: :propose
post :save, constraints: CommitParamRouting.new("Finalize"), action: :finalize
end
CommitParamRouting is a simple class that has a method matches? which returns true if the commit param matches the given instance attr. value.
This available as a gem commit_param_matching.
I remember coming across this problem once. You cannot keep two buttons and then call some action based on the params[:commit]. the submit button onclick is going to call the url the form refers to. There are certain bad ways to get the desired behavior. Keep a button to call the action the form refers to and to get another button to call a action, I used a link_to and then changed the styles to match a button. Also, alternatively you can use jQuery to change the url the form would call, hence deciding what action is invoked at run-time. Hope this helps.
You could also set some data attributes on the submit buttons and use JavaScript to change out the form action on click of one of the buttons
usually i using the suggestion given by John Topley (see answer above).
another way is using JQuery /JS changing the form action attribute- upon clicking the submit button
example:
form_tag({} ,:method => 'post', :id => 'reports_action') do
.......
.......
submit_tag 'submit', :onclick => "return changeAction();"
end
and then .....
function changeAction(){
$('#reports_action').attr('action','my_new_action');
}

Resources