I have a Product (imovel) controller where the users can create his own Products (imovels). I am using devise for the authentication and the CRUD (create, Update, Delete) needs login. So for the clients be able to see the products and doesn't need a user, I created the Welcome controller where so it is the root.
In the Index of the Products I use Ransack to do the research in the table and works just fine. (very happy about it).
On the welcome controller I try to do the same thing, but when I submit the search the page gets redirect to the imovels#index.
Controller:
class WelcomeController < ApplicationController
def index
#q = Imovel.ransack(params[:q])
#imovel = #q.result(distinct: true)
end
end
View:
<div class="container text-center">
<%= search_form_for #q do |f| %>
# Search if the name field contains...
<%= f.label :descricao_cont %>
<%= f.search_field :descricao_cont %>
<%= f.submit "Pesquisar", class: "btn btn-primary" %>
<% end %>
</div>
Another thing that can be important in the index (imovels#index) there is a for in a tr and the information is filtered there:
Imovels Index
<tbody>
<% #imovels.each do |imovel| %>
<tr>
<td><%= imovel.id %></td>
<td><%= imovel.descricao %></td>
<% end %>
And in the welcome controller where I need the search, I used Divs:
Welcome Index
<% #imovels.each do |imovel| %>
<div class="card">
<div class="containerImovel">
<h4><b><%= imovel.descricao %></b></h4>
<p><%= imovel.cidade %> - <%= imovel.bairro.nome %> </p>
</div>
</div>
<% end %>
How can I do the search on the divs of welcome controller? Ransack is the better option for it? It is possible to search in the has_many :through association?
Add a path for it in your routes.rb
resources :imovel do
collection do
match 'search' => 'welcome#search', via: [:get, :post], as: :search
end
end
and then add a new controller action to WelcomeController
def search
index
render :index
end
and afterwards modify the search form like so
<%= search_form_for #q, url: search_imovel_path, html: { method: :post } do |f| %>
Be sure to recheck the naming/variables as I'm not completely familiar with your app
Related
I have found some related questions but they don't seem to work for my case, they are either for older versions or don't fit my approach.
I have set up a User model with devise and it has two related models, it has one Contact Detail and many Addresses. I also have Petergate to get the roles.
I want any user to edit their registration (I have added contact details and addresses at the app/views/devise/registrations/edit.html.erb form) and admin can edit all.
The button in the contact details show page is implemented like this:
<%= link_to 'Edit Profile', edit_user_registration_path(#contact_detail.user) if logged_in?(:admin) || current_user.id == #contact_detail.user_id %>
and the edit form is the following
h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= form_for(resource, as: resource_name, url: user_registration_path(resource_name), html: { method: :put }) do |f| %>
...
...
<div>
<%= f.fields_for :contact_detail do |contact_detail|%>
<%= render "contact_detail_fields", f: contact_detail %>
<% end %>
</div>
<div>
<%= f.fields_for :addresses do |address_form| %>
<%= render "address_fields", f: address_form %>
<% end %>
</div>
<div class="form-group">
<%= f.submit "Update", class: "btn btn-primary" %>
</div>
But when I click the button on a user's contact details page, I always redirect to the admins edit page (/users/edit.1) and not to the user's edit page.
How can edit I the correct user's registration path?
My routes.rb file has the following:
resources :addresses
resources :contact_details
devise_for :users, controllers: {
registrations: 'my_registrations'
}
I haven't made a users controller override and my models don't contain anything relevant.
Thanks in advance!
An admin section where you can CRUD other users is not actually a feature of Devise.
The Devise RegistrationsController is built from the ground up to only work for the current user. The routes are actually singular and don't contain an id segment. You would have to override pretty much everything to get it working and in the process you're steamrolling the Single Responsibility Principle.
If you want to CRUD other users just create a seperate route and controller just like you would for any other resource. You can just run rails g scaffold_controller users to scaffold one.
devise_for :users, controllers: {
registrations: 'my_registrations'
}
resources :users
If you want to share view code between registrations and your users controller don't try to recycle your app/views/devise/registrations/edit.html.erb view. Instead spit the view into partials that can be resused.
# app/views/users/_fields.html.erb
<div>
<%= f.fields_for :contact_detail do |contact_detail|%>
<%= render "contact_detail_fields", f: contact_detail %>
<% end %>
</div>
<div>
<%= f.fields_for :addresses do |address_form| %>
<%= render "address_fields", f: address_form %>
<% end %>
</div>
# ...
# app/views/users/_form.html.erb
<%= form_for(user) do |f| >
<%= render "users/fields", f: f %>
<%= f.submit, class: "btn btn-primary" %>
<% end %>
# app/views/users/new/html.erb
<%= render "form", user: #user %>
# app/views/users/new/edit.erb
<%= render "form", user: #user %>
# app/views/devise/registrations/edit.html.erb
# This might be broken due to your earlier modifications so might want to
# regenerate the views
<h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= form_for(resource, as: resource_name, method: :put do|f| %>
<%= render "users/fields", f: f %>
<div class="form-group">
<%= f.submit "Update", class: "btn btn-primary" %>
</div>
<% end %>
While you can explicitly render another view by passing a path in the controller:
class UsersController < ApplicationController
def edit
#user = User.find(params[:id])
render 'devise/registrations/edit',
locals: { resource: #user, resource_name: :user }
end
end
This really just pushes the complexity down into the view layer. Editing your own account and someone else account is not the same thing and the view will end up full of conditionals sooner or later.
I'm a real newbie at Ruby and Rails, and I've been looking for the solution for two days. I need to submit data from form_tag to action 'create' in my controller to add new entries to database, but looks like I'm doing something terribly wrong, because absolutely nothing happens, and it seems that form_tag doesn't even redirect to needed action.
Here's the page code:
<h1>Todos</h1>
<% #projects.each do |project| %>
<tr>
<h2><%= project.title %></h2>
<% project.todos.each do |todo| %>
<ul style="list-style-type:disc">
<li><%= todo.text %></li>
</ul>
<% end %>
</tr>
<% end %>
<%= form_tag({controller: "mega", action: "create"}, method: "get", remote: true) do %>
<h2>New todo</h2>
<p>
<%= text_field_tag 'text' %>
</p>
<p>
<%= select_tag 'title', options_from_collection_for_select(#projects, 'id', 'title') %>
</p>
<p>
<%= link_to 'CANCEL' %>
<%= link_to 'OK', "", :onclick => "$('#form_id').submit()" %>
</p>
<% end %>
And the controller:
class MegaController < ApplicationController
def index
#projects = Project.all
#todos = Todo.all
end
def update
end
def create
#newTodo = Todo.create(text: params[:text])
#newProject = Project.find_by(title: params[:title])
#newProject.todos << #todo
#newTodo.save
end
end
My routes file. I seriously don't know how it works:
Rails.application.routes.draw do
get 'mega/index'
root 'mega#index'
get 'mega/update'
post 'mega/create'
resources :todos
resources :projects
end
You create resources with a POST request. Never GET.
GET requests should be idempotent - they should not update or alter resources on the server. One very important reason is that they are stored in the browser's history, so pressing the back button will cause unintended consequences for the user.
In Rails flavor MVC instead of tacking the action name on the path of the route you use the HTTP verb to create routes to the correct action:
GET /things things#index
POST /things things#create
I'm not going to attempt to salvage your code (it's deeply flawed) and instead show you how you would solve this the rails way as it is much simpler:
<%= form_for(Todo.new) do |f| %>
<h2>New todo</h2>
<%= f.text_field :text %>
<%= f.collection_select(:project_id, #projects, :id, :title, prompt: true) %>
<%= f.submit %>
<% end %>
This would submit to todos#create - if you want to route it to an unconventional action you can use the url option:
<%= form_for(Todo.new, url: polymorphic_path(controller: 'foo', action: 'bar')) do |f| %>
It's best to learn the rules before you break them.
I am using Ransack to add a simple search form on my homepage. I would like the results of the search to show on a different page, instead of on the homepage.
The HomeController has an index action with the #search variable set as follows
def index
#search = User.search(params[:q])
#users = #search.result
end
The view contains
<%= search_form_for #search do |f| %>
<fieldset>
<legend>User</legend>
<ul>
<li>
<%= f.label :first_name_or_last_name_cont %>
<%= f.text_field :first_name_or_last_name_cont %>
</li>
<li>
<%= f.label :email_cont %>
<%= f.text_field :email_cont %>
</li>
</ul>
</fieldset>
<fieldset>
<legend>User's Posts</legend>
<ul>
<li>
<%= f.label :posts_title_cont %>
<%= f.text_field :posts_title_cont %>
</li>
</ul>
</fieldset>
<%= f.submit %>
<% end %>
<%= render 'results' %>
How can I set up the controller so that I can use <%= render 'results' %> in a different view for a different action, say a search action? How can I do this so that when I submit the search form I am directed to a new page for the search action which displays the search results?
Great question! To answer your question, you can create a private method with a redirect_to a different page (that has <%=render 'results' %>) IF search params are passed in your HomeController.
class HomeController < ApplicationController
before_action :search
def index
#search = User.search(params[:q])
#users = #search.result
end
private
def search
if params[:q]
search_params = CGI::escapeHTML(params[:q])
redirect_to (url --> see below how to get the url)
end
end
end
However, if you want to start building out your app, you want your search results to display on that dedicated page, no matter where you are at in the app. I am pasting in a full answer from a small rails app. The code is only slightly different (form_tag instead of search_form_for), but I know it works, so hopefully it will help you.
Below, is a nav bar partial that is displayed across the app and then the relevant code for the home page and the ListingController index action. If search params are passed, then index.html.erb renders the #listings partial (_listing.html.erb) and nothing below the <% else %> tag on the home page.
_navigation.html.erb
<%= form_tag search_path, :method => :get do %>
<div class="form-group">
<%= text_field_tag :search, params[:search], class: "form-control", placeholder: "Search" %>
</div>
<%= submit_tag "Submit", :name => nil, :class => "btn btn-primary" %>
<% end %>
index.html.erb
<% if params[:search] %>
<h2>Search Results</h2>
<%= render #listings %>
<% else %>
...what usually shows up on my home page with no search results.
<% end %>
listings_controller
def index
#listings = Listing.search(params[:search])
end
routes.rb
get 'search' => "listings#search"
This works great. However, if I am in a different view/controller, like the one showing all the categories, and try to search, then it basically searches the current page. So, I added the following to the categories controller:
categories_controller
before_action :search
......
private
def search
if params[:search]
search_params = CGI::escapeHTML(params[:search])
redirect_to ("/listings?utf8=%E2%9C%93&search=#{search_params}")
end
end
BUT, for your specific app, to get the search to redirect to the home page and display the search results, first do a search on your home page and see what is generated in the url. Let's say I typed 'cheese' (/listings?utf8=%E2%9C%93&search=cheese). Notice the %E2%9C%93...you may not see this b/c this normally displays as a check in the url on your browser (http://unicode-search.net/unicode-namesearch.pl?term=mark)...so just paste it into text wrangler or stackoverflow text area to get the 'full url' like above. Then at the end of the url, just replace what you typed into the search box with #{search_params}.
This passes whatever was typed into the search box to your dedicated search results page (in my case index.html.erb)!
Here is some documentation on CGI escapeHTML (for security reasons): http://ruby-doc.org/stdlib-2.0/libdoc/cgi/rdoc/CGI.html#method-c-escapeHTML
I am trying to build a gallery with commenting function. Consider there are 3 classes: Album, Photo, Comment. The relation of these objects are Album has many Photos, Photo has many Comments.
Each Comment is left by registered user, thus in the comment object there are comment_text, user_id, and photo_id (Referring to the photo which the comment belongs).
I am trying to create a comment form under each photo's "show" view. Here is some of the Controller and the Show view:
controllers/photos_controller.rb
class PhotosController < ApplicationController
...
def show
#photo = Photo.find(params[:id])
#new_comment = #photo.comments.build if user_signed_in?
#comment_items = #photo.comments.paginate(page: params[:page])
end
...
end
views/photos/show.html.erb
...
<%= form_for #new_comment, html: {method: :post} do |c| %>
<div class="row">
<div class="span">
<%= image_tag current_user.avatar.url(:thumb) %>
</div>
<div class="span">
<p><%= c.label "Enter your comment here:" %></p>
<p><%= c.text_area :comment_text %></p>
<p><%= c.submit "Submit Comment", class: "btn btn-tiny" %></p>
</div>
</div>
<% end %>
So the problem is when I am writing the CommentsController, I do not know how to pass the photo's id (photo_id) from the previous "show" page to the create method. Is it only possible if a pass it thru via a hidden field in the form? Are there any more elegant way in Rails to do so? Besides, a point on security, if I retrieve the photo_id via a hidden field, will someone be able to write a specific "post" request so that it post comments to all photos in the album? (like spammer in the php's forum long time ago..)
Thank you for reading this and thanks in advance for answering!
take a look at the rails guides on nested resources http://guides.rubyonrails.org/routing.html#nested-resources
your routes would have something like this.
resources :photos do
resources :comments
end
your photo controller would stay the same, but your comments controller would need to lookup the photo from the url
class CommentsController < ApplicationController
before_action :get_photo
def create
#photo.comments.create(params.require(:comment).permit(:comment_text))
....
end
...
def get_photo
#photo = Photo.find(params[:photo_id])
end
end
and the photo view would have
<%= form_for [#photo,Comment.new], html: {method: :post} do |c| %>
<div class="row">
<div class="span">
<%= image_tag current_user.avatar.url(:thumb) %>
</div>
<div class="span">
<p><%= c.label "Enter your comment here:" %></p>
<p><%= c.text_area :comment_text %></p>
<p><%= c.submit "Submit Comment", class: "btn btn-tiny" %></p>
</div>
</div>
<% end %>
which should invoke the new_photo_comment_path
as for the user_id i would just grabbed that from the user_signed_in helper. Since you already have them logged in, no sense in passing it in from the web.
I have two model less views.
An index view:
<% #icd1.each do |f| %>
<%= link_to "#{f.von} #{f.bis} #{f.bezeichnung}", icd_show_path(f) %>
</p>
<% end %>
And an show view:
<% #icd1.each do |f| %>
<%= link_to "#{f.von} #{f.bis} #{f.bezeichnung}", icd_show_path(f) %>
</p>
<% f.icd2.each do |s| %>
<%= s.von %><%= s.bis %><%= s.bezeichnung %>
</p>
<% end %>
<% end %>
My controller:
class IcdController < ApplicationController
def index
#icd1 = Icd1.all
end
def show
#icd1 = Icd1.find(params[:id])
end
end
But somehow the link in the index view, wont work:
<%= link_to "#{f.von} #{f.bis} #{f.bezeichnung}", icd_show_path(f) %>
When i try to access the show page i get the error:
Couldn't find Icd1 without an ID
and the url only shows
http://localhost:3000/icd/show
without an id!
My routes:
get "icd/index"
get "icd/show"
1st: Very confusing naming: controller icd, model icd1..
2nd:
get "icd/show/:id", to: "icd#show", as: "icd_show"
or
get "icd/:id/show", to: "icd#show", as: "icd_show"
depends what url you want to get. It is confusing.
but I think this is what you need in your url;
<%= link_to "#{f.von} #{f.bis} #{f.bezeichnung}", icd_show_path(f) %>
and also the routes:
get "icd/:id", to: "icd#show", as: "icd_show"
after this next url will be available:
../icd/1 that will call action show from icd controller