How can I call and run a predefined config-block with formtastic or simple_form elements: What I did so far:
Create the form-config (in this case with formtastic):
AppmeConfig.register App do
form do |f|
f.inputs "Details" do
f.input :category
f.input :slug
end
f.action :submit
end
end
Assign the Block to the template:
def form &block
#form = block
end
In the template i do the following:
=semantic_form_for(#resource, &#form)
Everything fine so far and the source runs ... but it only outputs the last element (in this case the action button). Can you give me a hint, what's wrong?
More Information: the output is exactly the same for formtastic and simple_form, it doesn't work as well with a normal .html.erb-template instead of .haml
Not that nice as expected, but for the first I came up this solution (ok sorry, it's a dirty hack):
=simple_form_for #resource do |f|
- #form.to_source(:strip_enclosure => true).to_s.split("\n").each do |line|
= eval(line)
Explanation of the code above: I used sourcify to parse the whole block and eval it in the context of the form.
The main-problem I found is with the build in form_for-helper of rails, that doesn't accept block input. I'm wrong?
Related
I have added a custom field in my spree_orders table (let's call it custom_attribute).
I have added Spree::PermittedAttributes.checkout_attributes << [:custom_attribute] to my spree.rb initializer.
In my checkout process I have a custom form with the following code (html formatting has been removed):
<%= form_for #order do |alt_form| %>
<%= alt_form.label :custom_attribute, "Custom Attribute" %><span class="required">*</span><br />
<%= alt_form.text_field :custom_attribute, :class => 'form-control required', maxlength: 11 %>
<% end %>
This form successfully submits the field in the post request (full dump below) to http://localhost:3000/checkout/update/address as order[custom_attribute] xyz, however, the information is not saved to the model.
_method=patch
_method=patch
authenticity_token=Y+ATRotWKfI57f+b0/YGwIw9Bg6mADHBDmeEOHYzLPnB6Vbydya4ITDTopcX65EG+TiL7bwyJKQPpBU9bQTaUg==
authenticity_token=Y+ATRotWKfI57f+b0/YGwIw9Bg6mADHBDmeEOHYzLPnB6Vbydya4ITDTopcX65EG+TiL7bwyJKQPpBU9bQTaUg==
commit=Save and Continue
order[bill_address_attributes][address1]=123 Test
order[bill_address_attributes][address2]=
order[bill_address_attributes][city]=Test
order[bill_address_attributes][country_id]=232
order[bill_address_attributes][firstname]=Test
order[bill_address_attributes][id]=3
order[bill_address_attributes][lastname]=Test
order[bill_address_attributes][phone]=555555555
order[bill_address_attributes][state_id]=3535
order[bill_address_attributes][zipcode]=30024
order[email]=spree#example.com
order[custom_attribute]=2414
order[state_lock_version]=32
utf8=✓
utf8=✓
I've inserted #order.inspect on the following (payment) page to can see at that point that #order.custom_attribute is still nil.
Does anyone have any idea about what I need to do in order to get the custom_attribute value sent in the post request saved to the model with the other attributes sent?
-------------------edit-------------------
Default spree permitted attributes are defined here https://github.com/spree/spree/blob/3-0-stable/core/lib/spree/core/controller_helpers/strong_parameters.rb and are added on by the strong_paramaters helper here (don't have the rep to post a third link):
module Spree
module Core
module ControllerHelpers
module StrongParameters
def permitted_attributes
Spree::PermittedAttributes
end
delegate *Spree::PermittedAttributes::ATTRIBUTES,
to: :permitted_attributes,
prefix: :permitted
def permitted_payment_attributes
permitted_attributes.payment_attributes + [
source_attributes: permitted_source_attributes
]
end
def permitted_checkout_attributes
permitted_attributes.checkout_attributes + [
bill_address_attributes: permitted_address_attributes,
ship_address_attributes: permitted_address_attributes,
payments_attributes: permitted_payment_attributes,
shipments_attributes: permitted_shipment_attributes
]
end
def permitted_order_attributes
permitted_checkout_attributes + [
line_items_attributes: permitted_line_item_attributes
]
end
def permitted_product_attributes
permitted_attributes.product_attributes + [
product_properties_attributes: permitted_product_properties_attributes
]
end
end
end
end
end
which can be at found spree/core/lib/spree/core/controller_helpers/strong_parameters.rb in the spree github repo.
-------------------final edit-------------------
If anyone finds this in the future and is trying to troubleshoot a similar issue, my code above is actually correct; I had (stupidly) placed it in an if Rails.env.production? block.
I will give you an example, maybe you can translate it into your code.
OPTIONAL
Imagine that I have a custom action, called "custom" on my users controller, defined this way in my routes:
resources :users do
collection do
get 'custom'
post 'custom'
end
end
This way I can call it by using custom_users_path.
Next, I want a form that submits to that function, to do that you need to specify an additional parameter in your form_for called :url, in this example I call it using custom_users_path, once I submit the form, It will run my custom action.
form_for would look like this:
<%= form_for :user, :url => custom_users_path do |f| %>
<%= f.text_field :random %>
<%= f.submit "Submit" %>
<% end %>
Then, I want to be able to access some :random parameter in my users controller. Let's suppose that I have a text_field which I want store the value on my :random parameter (see above). First, you need to permit that parameter to be accessible in your controller, in this example, in users controller. This way:
params.require(:user).permit(YOUR PARAMETER HERE, {:random => []})
So, every time I submit the form, I can access the :submit parameter value, by doing this params["controller-name"]["parameter-name"], translated into this example, would look like:
params["user"]["random"]
You can then convert it into string using to_s if you want.
Output (Supposing that I wrote "444" on my text_field):
444
I hope this helps you.
I am relatively new to rails but having a real problem with something that I know should be really simple. I have a model called channel, in it I have a simple new method, in the view I have form but every time I try and load it, I get an error to say:
undefined method `channels_path'
My view (new.html.erb) is really simple, for the minute it just has a button in it with a name and a value, it just looks like this:
<%= simple_form_for #channel do |f| %>
<%= f.error_notification %>
<%= f.button :submit, 'Free Plan', name: 'plan', value: 'free' %>
<% end %>
My Controller has:
def new
#channel = Channel.new
end
And in my routes I have:
resources :channel
Output form a rake routes is:
channel_index GET /channel(.:format) channel#index
POST /channel(.:format) channel#create
new_channel GET /channel/new(.:format) channel#new
edit_channel GET /channel/:id/edit(.:format) channel#edit
channel GET /channel/:id(.:format) channel#show
PATCH /channel/:id(.:format) channel#update
PUT /channel/:id(.:format) channel#update
DELETE /channel/:id(.:format) channel#destroy
Which all looks how I expect. But as the error says there is no channels_path, but as far as I am aware, there shouldn't be.
I am sure this is supposed to be really simple but I just cannot see what I am doing wrong. Can anybody help?
Many thanks
David
EDIT
I have updated the route to be:
resources :channels
I can now load the form, however I now get the error when trying to submit it:
param is missing or the value is empty: channel
Being caused by:
# only allow specific params
def channel_params
params.require(:channel).permit(:name,
:slug,
:description,
:plan,
:subscription_ends
)
end
I am assuming singular is correct here based on the model, but have tried plural too with no luck. Any more thoughts?
Many thanks
Edit
Got it working in the end, it appears you have to have at least one input in your form. I added an input for the name field and it started working.
Many thanks to everyone that commented
According to your rake task, the path should be
channel_path
If it's not working with the simple_form_for helper, it's probably because you should have set up your routes as resources: channels
UPDATE
The new bug is coming from nothing being received by the controller for :channel
Try adding a field like so
f.hidden_field :plan, :value => "free"
When I create a form using simple_form_for #model, upon submit the post params has all the attributes grouped under params[model]. How do I get simple_form to drop this grouping and instead send it directly (under params root)?
<%= simple_form_for #user, do |f| %>
<%= f.input :name %>
<%= f.input :password %>
<%= f.submit %>
Now the name and password attributes would normally be sent under params[:user][:name], params[:user][:password] etc. How do I get simple_form to post these as params[:name], params[:password] etc.?
Thanks!
Ramkumar
ps: In case you are wondering why I need this, the bulk of my app is to serve as an API and I have built a set of methods to validate a request which expect some attributes to be in root. In a rare instance (forgot password), I actually need to present a form and I am looking for a way to use these methods.
you can explicitly define the name for an input by passing input_html to it:
input_html: { name: :name }
(needed this myself for sending an resource to a thirdparty endpoint with redirect to my side which relied on the plain attribute names, but i actually wanted not to built up label and input via the tags ;) )
also see simple form builder impl
Two ways I can think of:
The first is, don't use simple_form to build your form, but do it by hand or with the form_tag and *_tag methods. These will allow you to more closely specify what parameters are used in your form.
If you want to keep simple_form, though, then have it call a different controller action. Refactor the controllers to strip out the logic into a separate method. Something like:
class UsersController
def create_from_api
controller_logic(params)
end
def create_from_form
controller_logic(params[:user])
end
def controller_logic(params)
[actual work happens here]
end
end
I'm creating a helper to be used by Formtastic but I get the undefined local variable or method error. I don't know where to put it so it can work.
I already tried in the application_helper.rb and in app/helpers/active_admin/view_helpers.rb
You can define them in app/helpers/ as you tried but you need to include them trough the active admin's initializer like this:
# in config/initializers/active_admin.rb
ActiveAdmin.setup do |config|
....
end
module ActiveAdmin::ViewHelpers
include ApplicationHelper
end
You need to put your helper functions in app/helpers/active_admin/views_helper.rb file
Example:
module ActiveAdmin::ViewsHelper #camelized file name
def my_helper
# do something
end
end
What I have found using ActiveAdmin 0.6.1 is that ActiveAdmin will look for helpers in app/helpers/active_admin/*_helper.rb, but the name doesn't really matter.
What does matter is:
the filename must end in "_helper.rb"
the module name must be the camel-case of the file name
the file must be in app/helpers/active_admin/ directory.
If anyone knows where this is officially documented, that would be awesome.
Here is an example: https://gist.github.com/afred/7035a657e8ec5ec08d3b
app/helpers/active_admin/view_helpers.rb
didn't help me
EDITED: i changed it to views_helper.rb & ViewsHelper accordingly and it worked
*but if you want to define it only for certain resource, you can do it in my way
i had to define
#app/helpers/active_admin/categories_helper.rb
module ActiveAdmin::CategoriesHelper
def helper_method
end
end
for my active_admin resource app/admin/categories.rb
Another way to do this is to make the specific ActiveAdmin controller generated behind-the-scenes include the helper. This method will allow making the inclusion of the helpers explicit per file rather than global.
ActiveAdmin.register MyModel do
controller do
include MyHelper
end
end
I can make it work in ActiveAdmin 0.6.1 (finally!). The solution is to create a helper module as following:
# app/helpers/active_admin_helpers.rb
module ActiveAdminHelpers
# make this method public (compulsory)
def self.included(dsl)
# nothing ...
end
# define helper methods here ...
def helper_method
...
end
end
then include this module this way:
# app/admin/[resource].rb
include ActiveAdminHelpers
ActiveAdmin.register [Resource] do
...
end
Actually, it's not a nice solution but it's DRY and working good. I have already read and tried a lot of methods and solutions such as ViewHelpers module (put under 'app/helpers' or 'app/admin/active_admin'), ActiveAdmin::DSL monkey patching, ... but those never worked in version 0.6.1 (I don't have any ideas about other versions) :(
Defining ActiveAdmin::ViewHelpers in app/admin/active_admin/view_helpers.rb works for me with activeadmin 0.3.4 and 0.5.0.
Using activeadmin 1.0.0.pre1 from git://github.com/activeadmin/activeadmin.git
Rails 4.2.1
This worked for me...
my_app/app/helpers/active_admin/resources_helper.rb
module ActiveAdmin
module ResourcesHelper
def resource_form_for(_resource, _params, _options = {}, &_block)
url = if _resource.new_record?
UrlBuilder.resources_path(_resource.class, _params)
else
UrlBuilder.resource_path(_resource.class, _params)
end
method = _resource.new_record? ? :post : :put
options = { url: url, method: method, builder: ActiveAdmin::FormBuilder }
options.merge!(_options)
semantic_form_for([:admin, _resource], options) do |f|
_block.call(f)
end
end
end
end
my_app/app/admin/balance_sheets.rb
ActiveAdmin.register BalanceSheet do
form partial: 'form'
end
my_app/app/views/admin/balance_sheets/_form.html.erb
<%= resource_form_for(resource, params) do |f| %>
<%= f.inputs "Fields" do %>
<%= f.input :progress_status %>
<%= f.input :crew %>
<%= f.input :shift %>
<%= f.input :expected_progress %>
<%= f.input :real_progress %>
<%= f.input :analyst, collection: User.analysts %>
<%= f.input :activity_ids, as: :check_boxes, collection: Activity.balance_sheet_activities %>
<%= f.input :worker_ids, as: :check_boxes, collection: Worker.all %>
<% end %>
<%= f.actions %>
<% end %>
You can also use ActiveAdmin partials :
render partial: 'admin/my_partial', locals: { var: my_var }
And inside app/views/admin/_my_partial.html.arb your active_admin ruby code.
What worked for me with Rails 3.2.11 and and gem activeadmin (0.5.1) was not adding the app/active_admin/view_helpers.rb file, or declaring any modules in config/initializers/active_admin.rb
I put my helpers logically, by model, into the app/*_helpers.rb files. Then inside the app/admin/model.rb file I used:
# app/admin/[resource].rb
ActiveAdmin.register [Resource] do
...
filter :gender, as: :select, collection: proc{genders}
...
end
To use the helper in filters, to display a drop down list of genders to filter on, in the list view. For the corresponding create form fields, I used:
# app/admin/[resource].rb
ActiveAdmin.register [Resource] do
form do |f|
f.inputs "Case Manager" do
...
f.input :gender, as: :radio, collection: genders
...
f.buttons
end
end
end
To display radio buttons for the input form.
Not sure why the proc{} is required outside of the form do |f| block, but if anyone can explain why it's a bad idea, I'll find a different way.
I have the following model:
class Activity < ActiveRecord::Base
has_many :clientships, :dependent => :destroy, :after_add => :default_client_info
accepts_nested_attributes_for :clientships, :allow_destroy => true
end
In my controller, if I perform the following
def new
#activity = IndividualActivity.new(params[:activity])
#activity.clientships.build(:client => Client.first)
...
end
and then save the form, it creates the relevant params and submits successfully.
However, if I chose to call the following through a remote link
#activity.clientships.build(:client => Client.last)
the view is updated with the new clientship record but when I submit the form, the params[:activity] is not created for the second nested attribute. (Why not!?)
This is the view:
%h1 Create a new Activity
- form_for #activity do |f|
%div
= render "activities/client_selector", :f => f
%div
= f.submit "Save!"
Here is the remote_link's controller action
def add_client
#activity = IndividualActivity.new(session[:individual_activity])
# Refresh client
#activity.clientships.build(:client => Client.find(params[:client_id]))
respond_to do |format|
format.js
end
end
This is the add_client.html.js:
page.replace_html "selected_clients", :partial => 'activities/clients'
This is the activities/clients partial:
- form_for #activity do |f|
- f.fields_for :clientships do |client_f|
%tr
%td= client_f.hidden_field :client_id
%td= client_f.object.client.full_name
Does anyone know how I can troubleshoot this further? I seem to have come to a dead-end with my debugging... One thing to note, there is a double use of the following form_for used in new.html.haml and the activities/clients partial (is this problematic?)
- form_for #activity do |f|
I am on rails v2.3.5
Thanks
You ask about debugging, so the first step may be looking at the server log (log/development.log).
There you should see the "params" hash.
Maybe your params contain "activity"=>{"client_id"=>..} instead of "client_id"=>.. ?
Also look at the generated HTML page - use a Firebug or just use a "view source" method of your browser. Look, especially, for input names.
If everything looks OK, put a few debug calls in your action, and look at the development.log for some database activity - do the SQL queries look like they are doing what you want?
In your question there is no 'save' method. The 'build' method does NOT save the created record. Maybe this is your problem?
def add_client
logger.debug "Creating Activity"
#activity = IndividualActivity.new(session[:individual_activity])
logger.debug "Building clientship"
# Refresh client
#activity.clientships.build(:client => Client.find(params[:client_id]))
logger.debug "#activity = #{#activity.inspect}"
# Maybe you were missing this part of code?
logger.debug "Saving #activity"
#activity.save! # use a ! to easily see any problems with saving.
# Remove in production and add a proper if
logger.debug "Saved. #activity = #{#activity.inspect}"
respond_to do |format|
format.js
end
end
You should create a functional test (in case you haven't already) and ensure that if you send proper parameters, your action works as intended.
The test will narrow your search. If the test fails, you know you have a problem in the action. If the test is OK, you need to ensure the parameters are sent properly, and you probably have the problem in your view.
UPDATE:
You said you have TWO forms on the page. This may be the problem, since only one form may be sent at a time. Otherwise it would need to work in a way which can send two requests in one request.
First thing (useful in all similar problems): validate whether your page has correct HTML structure - for example http://validator.w3.org would be a good start. Try to make the code validate. I know that some people treat a "green" status as a unachievable mastery, but just it's really not so hard. With valid code you may be sure that the browser really understands what you mean.
Second: Place all your inputs in a single form. You have problems with nested attributes. For start, try to manually insert inputs with name like <input name="activity[clientship_attributes][0][name]" value="John"/>, and for existing clientships ensure that there is an input with name = activity[clientship_attributes][0][id].
This is the way nested attributes are handled.
Your view may create such fields automagically. This construction should be what you need: (it worked in one of my old project in rails 2.x, I have just replaced the names with ones you use)
<% form_for(#activity) do |f| %>
<p><%= f.text_field :activity_something %></p>
<% #activity.clientships.each do |clientship| %>
<% f.fields_for :clientships, clientship do |cform| %>
<p><%= cform.text_field :name %></p>
<p><%= cform.text_fiels :something %></p>
<% end %>
<% end %>
<% end %>
If you really want to use a partial there, don't create a new form in the partial. Use only the parts of above code.
To pass a variable to the partial, use :locals attribute in the place where you call render :partial:
<%= render :partial => 'clientship', :locals => {:form => f} %>
Then, in your partial, you may use a local variable form where you would use f outside of the partial. You may, of course, map the variables to the same name: :locals => {:f => f}