Wondering how to do this.
I have a month helper and want to render a list of each month.
If you click on the month it would then pass the number value . I want to produce a list like this
Choose a month
January - February - March - April - May.....
That are links. My helper looks like this.
MONTHLIST = [
[ "January", "1" ],
[ "February", "2" ],
[ "March", "3" ]
].freeze
How do I use link_to to get the names and link be the value. i.e #3 for march.
Here's a simple approach: pass the view to the helper
in your view
<%= your_helper(self, other_params) %>
.
.
in your helper
def your_helper(view, other_params)
do_something_with_params
view.raw view.link_to(.... link to params)
end
If I understand you completely, you mean that you want to call the link_to method in your helper module, right? Then the helper method, when called, will return the result of the link_to method to whichever view called it.
If that's what you want, I'm butting my head against a similar problem because the helper module cannot access the link_to method except through the ActionView::Helpers::URLHelper instance that was created in response to your service request. I assume that this object can be accessed through a Rails system variable, but I am at a loss as to what that might be. Thoughts?
For the interrim, my answer is to use the link_to method only within your view file. Your helper file would look something like this:
module MyHelper
def MyHelper.my_helper_method
MONTHLIST = [ [ "January", "1" ], [ "February", "2" ], [ "March", "3" ] ].freeze
end
end
Then your view file would call the helper method thus:
<% require '[path to helper file]' %>
<% months = MyHelper.my_helper_method %>
<% months.each do |month_name, month_num| %>
<%= link_to(month_name, month_num) %>
<% end %>
(Yes, that was probably a more verbose code than you needed. It could have been abbreviated.)
An alternative! Here goes:
Make a Proc object out of the link_to method, then pass it from your view to the helper as an argument. Here's how it will look...
Your view file:
<% require 'app/helpers/myhelper.rb' %>
<% proc_object = Proc.new( { |anchor_text, args| link_to(anchor_text, args) } )
<%= MyHelper.my_helper_method(proc_object) %>
Your helper file:
module MyHelper
def my_helper_method(proc_object)
months.each do |name, num|
proc.call(name, :action => 'show', :id => num)
end
end
end
For the second arg in your proc.call statement, use what you'd normally use to set the url in your link_to method, e.g. :controller => [x], :action => [y].
To recap, what you're doing is making a Proc that wraps your method, then sending that wrapped method to your helper, which will return html code for your hyperlink, which your .html.erb file will print to your html page.
<% monthlist.each do |month| %>
<%= link_to month[0], your_route(month[1]) %>
<% end %>
Related
I would like to recognize in template form.html.eex which action from new.html.eex or edit.html.eex.
I would like to change button label.
<% if #action == :create do %>
<%= submit "NEW" %>
<% else %>
<%= submit "UPDATE" %>
<% end %>
You're looking for Phoenix.Controller.action_name(#conn).
Of course, you can pass it to the template as #action with, in your controller:
conn
|> assign(:action, action_name(conn))
# ...
|> render(...)
But if you use it often, you can override the action function in lib/<your_app>_web.ex:
def controller do
quote do
# ...
def action(conn, _) do
action = action_name(conn)
conn = conn
|> assign(:action, action)
apply(__MODULE__, action, [conn, conn.params])
end
end
end
You pass it in from the templates. You can actually pass as many key/value pairs as you like.
For example you might have a templates/user/edit.html.eex like this:
<h2><%= gettext("Edit user") %></h2>
<%= render "form.html", changeset: #changeset,
is_admin?: #is_admin?, user: #user, lang_select: #lang_select,
action: user_path(#conn, :update, #user) %>
<%= link gettext("Back"), to: user_path(#conn, :show, #user) %>
You would just pass in one more key/value pair. Since #action is created by Phoenix generators to be the form action, you could just name yours submit_text or whatever makes sense to you. From inside the form template, it would be available like this:
<%= submit #submit_text %>
I'm building a Todo list in rails trying to learn how it all works. I'm having an issue with calling the action of a controller from the view of another controller.
I have a TodoList has_many TodoItem and TodoItem belongs_to TodoList.
TodoList#Model
has_many :todo_items
def has_completed_items?
todo_items.complete.size > 0
end
def has_incompleted_items?
todo_items.incomplete.size > 0
end
TodoItem#Model
belongs_to :todo_list
scope :complete, -> { where("completed_at is not null") }
scope :incomplete, -> { where(completed_at: nil) }
def completed?
!completed_at.blank?
end
Routes
resources :todo_lists do
resources :todo_items do
member do
patch :complete
end
end
end
Patch generates this route:
complete_todo_list_todo_item PATCH
/todo_lists/:todo_list_id/todo_items/:id/complete(.:format)
todo_items#complete
Now I can call this path from my TodoItem views without a problem like this: <%= link_to "Mark", complete_todo_list_todo_item_path(todo_item), method: :patch %>
After adding complete/incomplete scope to my TodoItem I added the following to my TodoList show view:
TodoList#Show
<% #todo_list.has_incompleted_items? %>
<% #todo_list.todo_items.incomplete.each do |item| %>
<li><%= item.content %></li>
<% end %>
This is properly displaying the incomplete items, so I tried to put a link to mark the item complete from within this view like this:
<% #todo_list.has_incompleted_items? %>
<% #todo_list.todo_items.incomplete.each do |item| %>
<li><%= item.content %>
<%= link_to "Mark", complete_todo_list_todo_item_path(item), method: :patch %></li>
<% end %>
Now this is where I get the following error:
NoMethodError in TodoLists#show undefined method
`complete_todo_list_todo_items_path' for #<#:0x000001071cf540>
Why is this complete#action undefined from my TodoList#show view?
What is the proper convention to share actions between controllers?
Thank you for your help.
I was able to solve this problem by removing the member block from the routes file:
resources :todo_lists do
resources :todo_items do
patch :complete
end
end
Now my action route was updated to: todo_list_todo_item_complete
So from my view I had to call the new route with it's nested resource:
link_to "Mark", todo_list_todo_item_complete_path(#todo_list, item), method: :patch
Lastly I had to update my controller to locate the proper todo_item#id in the complete block:
#todo_item = #todo_list.todo_items.find(params[:todo_item_id])
In order to remove (paperclip) images from my objects, I have a custom callback (and route) defined:
ActiveAdmin.register Camping do
#...
member_action :destroy_image, :method => :delete do
camping = Camping.find(params[:id])
camping.image.destroy
redirect_to({:action => :show}, :notice => "Image deleted")
end
end
This works as expected; trough a named route destroy_image_admin_camping => /admin/campings/:id/destroy_image.
The problem is that I cannot find how to add this to the form:
ActiveAdmin.register Camping do
form do |f|
f.inputs "Camping" do
f.input :name
f.input :image
f.action :delete_image, :url => destroy_image_admin_camping_path(#camping.id), :button_html => { :method => :delete }
f.input :description
end
f.actions
end
#...
end
More detailed: I don't know how to pass the "id of the current item we are editing" into destroy_image_admin_camping_path; #camping is nil, f.camping not defined and so I don't know how to pass the item in there.
Is this the right approach? I prefer this "ajax-ish" interface over a more common checkbox-that-deletes-images-on-update, but I am not sure if this will work at all.
There's a few questions here, I'll try to address them all.
How to access the "id of the current item we are editing"
You are pretty close in looking for f.camping. What you want is f.object, so:
destroy_image_admin_camping_path(f.object.id).
NOTE: f.object.id will be nil when it's a new form (as opposed to an edit form), you'll want to check for that with unless f.object.new_record?
"Is this the right approach?"
I'm not sure, really. To me it seems like making requests without actually saving the currently rendered form could create complications, but it might turn out to be a better interface. Anyway, if you want to do the checkbox-that-deletes-images-on-update, this should help you out: Rails Paperclip how to delete attachment?.
However, if you want the ajax-ish approach I think you'll want an <a> tag styled as a button. The problem with actually using a button is that you don't want to submit the form.
Here's an example:
f.inputs do
link_to 'Delete Image', delete_image_admin_camping_path(f.object.id), class: 'button', remote: true, method: :delete
end
remote: true will make it an ajax request and ActiveAdmin gives you a pretty reasonable button class for <a> tags. Updating the interface based on success / failure is left as an exercise to the reader.
Also, you'll probably want to use erb templates for this instead of the Active Admin DSL (see http://activeadmin.info/docs/5-forms.html at the bottom).
Like this:
# app/views/admin/campings/_form.html.erb
<%= semantic_form_for [:admin, #post] do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<%= f.input :image %>
<%# Actually, you might want to check for presence of the image, I don't know Paperclip well enough to demonstrate that though %>
<% unless f.object.new_record? %>
<%= image_tag f.object.image_url %>
<%= link_to 'Delete Image', delete_image_admin_camping_path(f.object.id), class: 'button', remote: true, method: :delete %>
<% end %>
...
<% end %>
<%= f.actions %>
<% end %>
I am relative newbie to all this, so sorry if this sounds mad!
I have used this tutorial: http://www.railsmine.net/2010/03/rails-3-action-mailer-example.html
And I have a new contact form working great.
The controller is at app/controllers/support_controller.rb
class SupportsController < ApplicationController
def new
# id is required to deal with form
#support = Support.new(:id => 1)
end
def create
#support = Support.new(params[:support])
if #support.save
redirect_to('/', :notice => "Support was successfully sent.")
else
flash[:alert] = "You must fill all fields."
render 'new'
end
end
end
And the model at /app/models/support.rb
class Support
include ActiveModel::Validations
validates_presence_of :email, :sender_name, :support_type, :content
# to deal with form, you must have an id attribute
attr_accessor :id, :email, :sender_name, :support_type, :content
def initialize(attributes = {})
attributes.each do |key, value|
self.send("#{key}=", value)
end
#attributes = attributes
end
def read_attribute_for_validation(key)
#attributes[key]
end
def to_key
end
def save
if self.valid?
Notifier.support_notification(self).deliver!
return true
end
return false
end
end
The views however only work in views/supports/new.html.rb (rendered - views/supports/_form.html.erb)
So I can call the Model / Controller from localhost:3000/support/new but if I try and render the same form in another view from the root directory e.g. app/view/contact.html.erb I get:
undefined method `model_name' for NilClass:Class
I think this is because it is calling the support model away from the supports directory.
Do I have to create an instance on #support so it can be called? If so what is the best way of doing that? I think I am nearly there. I just want the contact form on multiple pages not just in suppport/new
Thanks
Charlie
Yes, you would need to create a #support variable in each action you wish to render your form.
Another option would be to refactor the form to take a parameter, that way you're a bit more flexible. For example, from your view:
<%= render :partial => "supports/form", :locals => {:support => #support} %>
Now, instead of referring to #support in your _form.html.erb, you'd refer to simply support as it's a local_assign.
Yet another option would be to refactor the form a little further, and worry about creating the actual form tag outside of the partial.
Such as:
app/views/supports/new.html.erb
<%= form_for #support do |form| %>
<%= render :partial => "suppports/form", :object => form %>
<% end %>
app/views/supports/_form.html.erb
<%= form.text_field :foo %>
<%= form.text_field :bar %>
...
In this case, when you render a partial with the object option, you will get a local variable in your partial with the same name as the partial. You maintain a little bit more flexibility in the path of your form, but can still render the meat of what a Support object is inside of the form while remaining consistent across your app.
To clarify, you could use this somewhere else by doing something like:
app/views/foos/_create_foo_support.html.erb
<%= form_for #foo.support do |form| %>
<%= render :partial => "supports/form", :object => form %>
<% end %>
You have to pass #support object wherever you use your contact form. It's working in SupportsController#new because you initialize the variable there. In all other places where you want to use the form, you'll have to do the same.
I would like to enable edit-in-place functionality in a View that displays values from different models:
This is what I use currently and it does NOT work, but would like some alternatives:
I have a model called Campaign. In the controller, I do the following to list, in order, the three Models that belong_to a Campaign:
<% #campaign_events = campaign_events %>
<% #campaign_events.each do |campaign_event| %>
<% model_name = campaign_event.class.name.tableize.singularize %>
<p>
<%= link_to campaign_event.title, send("#{model_name}_path", campaign_event) %>
<span class='model_name'>(<%= model_name.capitalize %>)</span>
<%= campaign_event.days %> Days
</p>
<% end %>
campaign_event is a campaign_helper defined as:
module CampaignsHelper
def campaign_events
return (#campaign.calls + #campaign.emails + #campaign.letters).sort{|a,b| a.days <=> b.days}
end
end
I want to be able to click on the numerical value for Days when looking at the view/campaign/show and edit the value for :days (in this case, displayed as campaign_event.days
I'm not really sure about it, but I'll try to help... I believe something like the following may work for you:
# calls controller
in_place_edit_for :call, :days
# emails controller
in_place_edit_for :email, :days
# letters controller
in_place_edit_for :letter, :days
# campaign view
<% #campaign_events = campaign_events %>
<% #campaign_events.each do |campaign_event| %>
<% controller_name = campaign_event.class.name.tableize %>
<% model_name = controller_name.singularize %>
<p>
<%= link_to campaign_event.title,
send("#{model_name}_path", campaign_event) %>
<span class='model_name'>(<%= model_name.capitalize %>)</span>
<%= in_place_editor_field model_name, :days, {},
:url => {
:controller => controller_name,
:action => 'set_#{model_name}_title',
:id => campaign_event.id} %> Days
</p>
<% end %>
There's somethings I don't know exactly how to do:
1) in_place_editor_field model_name
I believe this won't work, but I don't know how to pass the model_name.
2) :action => 'set_#{controller_name}_title'
Not sure about it also. Just doesn't look right.
Anyway, hope it helps you... and forgive me if this is completely stupid.