I have a haml like ;
= form_for #company, :html => {:multipart => true}, :url => update_user_company_path do |f|
.field
Title:#{f.text_field :name}
= f.fields_for :attachments do |builder|
- if builder.object.new_record?
.field
= builder.hidden_field :name, :value => 'logo'
= builder.file_field :file
- elsif builder.object.name.eql?('logo') && !builder.object.file.url.eql?('/files/original/missing.png')
.field
%span.thumbnail
= link_to "Delete", delete_company_attachment_path(#company, builder.object), :method => :delete, :class => "remove_image"
= image_tag builder.object.file.url, :style => "height:86px;width:125px"
= f.submit 'Ok'
Chrome renders this code as intended, but in Firefox it is like;
<form method="post" id="edit_company_29" enctype="multipart/form-data" class="edit_company" action="/users/25/company" accept-charset="UTF-8"><div style="margin:0;padding:0;display:inline"><input type="hidden" value="✓" name="utf8"><input type="hidden" value="put" name="_method"><input type="hidden" value="thisismytokenvalue=" name="authenticity_token"></div>
<div class="field">
Title:<input type="text" value="sdgdfgghjh" size="30" name="company[name]" id="company_name">
</div>
<input id="company_attachments_attributes_0_id" name="company[attachments_attributes][0][id]" type="hidden" value="114" /><input id="company_attachments_attributes_1_id" name="company[attachments_attributes][1][id]" type="hidden" value="115" /><div class="field">
<input type="hidden" value="logo" name="company[attachments_attributes][2][name]" id="company_attachments_attributes_2_name">
<input type="file" name="company[attachments_attributes][2][file]" id="company_attachments_attributes_2_file">
</div>
<input type="submit" value="Ok" name="commit">
</form>
Why is an element escaped. If you check haml, you can see I didn't put them.
Where did it come from?
Why is it happening?
Wow ... I've just had a similar issue. My guess is that if you pass nil to the form_builder's fields_for the hidden_input isn't returned with html_safe. To quickly fix that add a
-else
=""
after the whole elsif block
You must return something other than nil in the fields_for block.
This issue has been fixed in Haml 4.0.4 by mattwildig, with the help of #lulalala. Here is an explanation of the problem:
The #fields_for helper generates a hidden input field for each record, which it adds to the result of capturing the block. Normally the return value of capture will be a SafeBuffer, so this won’t be escaped. However if the block doesn’t write anything to the buffer then Haml returns a literal empty String from capture. The hidden input element is concatted to this string, and when it is combined with the rest of the entries, since it is a String being added to a SafeBuffer, it gets escaped.
Related
I'm trying to write a test for a Bootstrap form using rspec. I have the following code:
<div id="published" class="col-sm-auto">
<div class="custom-control custom-radio custom-control-inline">
<%= form.radio_button :published, true, checked: page.published?, class: 'custom-control-input' %>
<%= form.label :published, 'Published', value: true, class: 'custom-control-label' %>
</div>
<div class="custom-control custom-radio custom-control-inline">
<%= form.radio_button :published, false, checked: !page.published?, class: 'custom-control-input' %>
<%= form.label :published, 'Hidden', value: false, class: 'custom-control-label' %>
</div>
</div>
which generates the following HTML:
<div id="published" class="col-sm-auto">
<div class="custom-control custom-radio custom-control-inline">
<input class="custom-control-input" type="radio" value="true" name="page[published]" id="page_published_true" />
<label class="custom-control-label" for="page_published_true">Published</label>
</div>
<div class="custom-control custom-radio custom-control-inline">
<input class="custom-control-input" type="radio" value="false" checked="checked" name="page[published]" id="page_published_false" />
<label class="custom-control-label" for="page_published_false">Hidden</label>
</div>
</div>
When I run this test, it fails with the message
it 'should default to unpublished' do
expect(page).to have_checked_field('Hidden')
end
giving the message expected to find visible field "page[published]" that is checked and not disabled but there were no matches. Also found "", "", which matched the selector but not all filters.
Looking at the HTML in the inspector, the field is visible and not disabled, and there is a label that matches the text. I have no idea what the two empty strings are about.
Please could somebody tell me why have_checked_field isn't matching? I'm really not keen on writing a more brittle test that uses has_css or has_xpath to look for an input tag with a specific ID — the whole point is that the user should see a field labelled "Hidden" and it should be checked, regardless of what's happening behind the scenes!
I'll 99.9% guarantee you the radio input isn't actually visible and has been replaced by an image using CSS (and maybe some JS). You could do
expect(page).to have_checked_field('Hidden', visible: false)
or you could do something slightly more complicated that verifies the label is actually visible like
expect(page).to have_css('input:checked + label', exact_text: 'Hidden')
Another solution if you're dealing with a lot of these radio buttons is to write a custom selector type something like
Capybara.add_selector(:bootstrap_radio) do
xpath do |locator|
XPath.descendant(:input)[XPath.attr(:type) == 'radio'].following_sibling(:label)[XPath.string.n.is(locator)]
end
filter(:checked) do |node, value|
node.sibling('input[type="radio"]', visible: :hidden).checked? == value
end
end
which would then allow you to do
expect(page).to have_selector(:bootstrap_radio, 'Hidden', checked: true)
and then you could write helper methods like have_checked_bootstrap_radio if wanted. Note: this code was off the cuff so the XPath/CSS expressions may not be 100% correct but the general idea is sound :)
I'd like to reproduce this html sequence of radio buttons with simple_form in order to make simple_form work with http://semantic-ui.com/ syntax :
<div class="grouped inline fields">
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="fruit" checked="">
<label>Apples</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="fruit">
<label>Oranges</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="fruit">
<label>Pears</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="fruit">
<label>Grapefruit</label>
</div>
</div>
</div>
So I prepared a custom wrapper :
config.wrappers :semantic_radios, tag: 'div', class: "grouped fields", error_class: 'error', hint_class: 'with_hint' do |b|
b.use :html5
b.use :label
b.use :input
end
Set some options :
config.item_wrapper_tag = :div
config.item_wrapper_class = 'ui radio checkbox'
And call this code in my form :
=f.input :child_care_type, collection: [["option 1", 1],["option 2", 2]], as: :radio_buttons, wrapper: :semantic_radios
I don't know where to customize the div.field encapsulation :
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="fruit" checked="">
<label>Apples</label>
</div>
</div>
My code only render this :
<div class="ui radio checkbox">
<input type="radio" name="fruit" checked="">
<label>Apples</label>
</div>
Can you help me ? I didn't find more wrapper's customization for collection :s
Summary:
I had done something similar to this before by creating a custom input that inherits from SimpleForm::Inputs::CollectionRadioButtonsInput and overloading just a few methods. For more on custom input components, see https://github.com/plataformatec/simple_form/wiki/Adding-custom-input-components.
In any case, the code below produces your desired html markup almost exactly using simple_form v2.1.0 and rails v3.2.15.
Code:
# File: app/inputs/semantic_ui_radio_buttons_input.rb
class SemanticUiRadioButtonsInput < SimpleForm::Inputs::CollectionRadioButtonsInput
# Creates a radio button set for use with Semantic UI
def input
label_method, value_method = detect_collection_methods
iopts = {
:checked => 1,
:item_wrapper_tag => 'div',
:item_wrapper_class => 'field',
:collection_wrapper_tag => 'div',
:collection_wrapper_class => 'grouped inline fields'
}
return #builder.send(
"collection_radio_buttons",
attribute_name,
collection,
value_method,
label_method,
iopts,
input_html_options,
&collection_block_for_nested_boolean_style
)
end # method
protected
def build_nested_boolean_style_item_tag(collection_builder)
tag = String.new
tag << '<div class="ui radio checkbox">'.html_safe
tag << collection_builder.radio_button + collection_builder.label
tag << '</div>'.html_safe
return tag.html_safe
end # method
end # class
Then, in your form, just do:
-# File: app/views/<resource>/_form.html.haml
-# Define the collection
- child_care_coll = %w( Infant Toddler Preschool Kindergarten ).map!.with_index(1).to_a
-# Render the radio inputs
= f.input :child_care_type,
:collection => child_care_coll,
:label_method => :first,
:value_method => :last,
:as => :semantic_ui_radio_buttons
Results:
<div class="input semantic_ui_radio_buttons optional childcare_child_care_type">
<label class="semantic_ui_radio_buttons optional control-label">
Child care type
</label>
<div class="grouped inline fields">
<div class="field">
<div class="ui radio checkbox">
<input checked="checked" class="semantic_ui_radio_buttons optional" id="childcare_child_care_type_1" name="childcare[child_care_type]" type="radio" value="1">
<label for="childcare_child_care_type_1">Infant</label>
</div>
</div>
...
<div class="field">
<div class="ui radio checkbox">
<input class="semantic_ui_radio_buttons optional" id="childcare_child_care_type_4" name="childcare[child_care_type]" type="radio" value="4">
<label for="childcare_child_care_type_4">Kindergarten</label>
</div>
</div>
</div>
</div>
I was having trouble with the same problem until I looked through config/initializers/simple_form.rb.
Turns out you can set the option here (line ~51):
# Define the way to render check boxes / radio buttons with labels.
# Defaults to :nested for bootstrap config.
# inline: input + label
# nested: label > input
config.boolean_style = :inline
A bit further down you can also define the default wrapper tag and wrapper tag class for the collection (line ~81):
# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
config.collection_wrapper_tag = :div
# You can define the class to use on all collection wrappers. Defaulting to none.
config.collection_wrapper_class = 'styled-radios'
Hope this helps someone :)
*using gem 'simple_form'
With updated versions 'simple_form', '~> 3.0.2' and Semantic UI 0.19.3, I achieved it with following code.
class SemanticCheckBoxesInput < SimpleForm::Inputs::CollectionCheckBoxesInput
def input
label_method, value_method = detect_collection_methods
options[:collection_wrapper_tag] = 'div'
options[:collection_wrapper_class] = 'grouped inline fields'
#builder.send("collection_check_boxes",
attribute_name, collection, value_method, label_method,
input_options, input_html_options, &collection_block_for_nested_boolean_style
)
end
protected
def item_wrapper_class
"ui checkbox field"
end
end
and in the view
= f.association :branches, as: :semantic_check_boxes
Here is the output:
So, I'm new to Ruby/Sinatra, did a bunch of codecademy lessons and the like. I decided until I actually built something, I wouldn't really understand some core concepts.
I found a tutorial for building a to-do list app in Sinatra, and all was fine and good until I got to the edit and delete functionality. I can read and write to/from the database, but whenever I try to edit or delete, it skips straight to "Note not found".
I couldn't figure this out myself, so I asked a developer friend of mine. When I sent him the code, everything worked absolutely fine for him! We tried a couple of different possible fixes, but to no end. I even downloaded the code from the guy's github, just in case there was a random mistake somewhere in my own code, to no avail.
So, I come here asking for some help! Why won't this work!
Clarification: Here's some of the code, where I think the problem may lie. In edit.erb and delete.erb, no matter what it is going to the else statement and sending me to "note not found". It's reading properly from the database, as my homepage can add and show notes.
recall.rb
get '/:id' do
#note = Note.get params[:id]
#title = "Edit note ##{params[:id]}"
erb :edit
end
put '/:id' do
n = Note.get params[:id]
n.content = params[:content]
n.complete = params[:complete] ? 1 : 0
n.updated_at = Time.now
n.save
redirect '/'
end
get '/:id/delete' do
#note = Note.get params[:id]
#title = "Confirm deletion of note ##{params[:id]}"
erb :delete
end
delete '/:id' do
n = Note.get params[:id]
n.destroy!
redirect '/'
end
edit.erb
<% if #note %>
<form action="/<%= #note.id %>" method="post" id="edit">
<input type="hidden" name="_method" value="put">
<textarea name="content"><%= #note.content %></textarea>
<input type="checkbox" name="complete" <%= "checked" if #note.complete %>>
<input type="submit">
</form>
<p>Delete</p>
<% else %>
<p>Note not found.</p>
<% end %>
delete.erb
<% if #note %>
<p>Are you sure you want to delete the following note: <em>"<%= #note.content %>"</em>?</p>
<form action="/<%= #note.id %>" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="Yes, Delete It!">
Cancel
</form>
<% else %>
<p>Note not found.</p>
<% end %>
Moving the delete-route above the other routes seemed to work for me, which would suggest that the /:id -part in the route definition catches the /delete-part as well. You can see the generated regexps with Sinatra::Application.routes.
Here's a minimal:
require 'rubygems'
require 'sinatra'
require 'dm-core'
require 'dm-migrations'
require 'dm-sweatshop'
# Models
DataMapper.setup(:default, 'sqlite::memory:')
class Note
include DataMapper::Resource
property :id, Serial
property :content, Text, :required => true
property :complete, Boolean, :default => false
property :created_at, DateTime, :default => lambda {Time.now}
property :updated_at, DateTime
before(:save) { attribute_set(:updated_at, Time.now) }
end
DataMapper.finalize.auto_migrate!
# Fixtures
Note.fix {{
:content => /\w+/.gen
}}
100.of { Note.gen }
# Routes
before("/:id") {|id| #note = Note.get id }
get("/:id/delete") {|id| #note = Note.get id; erb :delete }
get("/:id") { erb :edit }
put "/:id" do
#note.attributes = params["note"]
#note.save ? redirect(to("/")) : erb(:edit)
end
delete("/:id") { #note.destroy; redirect(to("/")) }
# Templates
template :layout do
'<!DOCTYPE html>
<html>
<body><%= yield %></body>
</html>'
end
template :edit do
'<% if #note %>
<form action="/<%= #note.id %>" method="POST">
<input type="hidden" name="_method" value="PUT">
<textarea name="note[content]"><%= #note.content %></textarea>
<input type="checkbox" name="note[complete]"
<%= #note.complete? && "checked" %> >
<input type="submit">
</form>
<p>Delete</p>
<% else %>
<p>Note not found.</p>
<% end %>'
end
template :delete do
'<% if #note %>
<p>Are you sure you want to delete the following note:
<em>"<%= #note.content %>"</em>?</p>
<form action="/<%= #note.id %>" method="POST">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="Yes, Delete it!">
Cancel
</form>
<% else %>
<p>Note not found</p>
<% end %>'
end
I don't know if this is still interesting someone, but a simple
params[:id].to_i
fixed it for me.
Using Simple_form 2.0.2
The simple form code using HAML:
= f.input :remember_me, as: :boolean, inline_label: 'Remember me'
But it renders this:
<div class="control-group boolean optional">
<label class="boolean optional control-label" for="admin_remember_me">Remember me</label>
<div class="controls">
<input name="admin[remember_me]" type="hidden" value="0" />
<label class="checkbox"><input class="boolean optional" id="admin_remember_me" name="admin[remember_me]" type="checkbox" value="1" />Remember me</label>
</div>
</div>
How do I remove that first label that's rendered, so that I only have the inline label?
You can use:
= f.input :remember_me, as: :boolean, inline_label: 'Remember me', label: false
Found a solution after much Google fu.
Use input_field instead of input which won't automatically generate a label.
= f.input_field :remember_me, as: :boolean, inline_label: 'Remember me'
For whom it doesn't work
= f.input_field ...
Use this way
= f.check_box ...
With simple_form 2.1.0 and rails 3.0.20, none of the solutions listed here worked (I don't want to use f.input_field because it's an admission of defeat).
The missing part is the boolean_style option:
options.merge!({label: false, boolean_style: :inline})
I suggest you create a custom input for this (e.g.: inline_checkbox)
boolean_style is configured as :nested by default, I think:
# Defaults to :nested for bootstrap config.
# :inline => input + label
# :nested => label > input
config.boolean_style = :nested
.control-group.error .help-inline {
display: none;
}
This should work, it works for me on rails 3.2 and simple_form 2.x+
Maybe too late, but inspired by gamov answer I have made this a custom wrapper from inline bootstrap checkbox in the initializer file 'config/simple_form_bootstrap.rb':
config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'col-sm-3 control-label'
b.use :input
b.use :error, wrap_with: { tag: 'span', class: 'help-block' }
b.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
which generates this html:
<div class="form-group boolean optional user_admin">
<label class="boolean optional col-sm-3 control-label" for="user_admin">Admin</label>
<div class="col-sm-9 checkbox-inline">
<input name="user[admin]" value="0" type="hidden">
<input class="boolean optional" id="user_admin" name="user[admin]" value="1" type="checkbox">
</div>
I have the following. Each article has a title and a body and also up to three urls. I would want to store the urls in a different table. Therefore in my form, i've a field for the urls. However they are not working, only the article fields get entered into the database. how should i specify them? Could any kind soul help me out with this?
class Article
include DataMapper::Resource
property :id, Serial
property :title, String
property :body, Text
has n, :urls, through => Resource
end
class Url
include DataMapper::Resource
property :id, Serial
property :url_01, String
property :url_02, String
property :url_03, String
belongs_to :article
end
post '/create' do
#article = Article.new(params[:article])
if #article.save
redirect "/articles"
else
redirect "/articles/new"
end
end
--------------------------------------
<form action="/create" method="post">
<p>
<label>Article Title</label>
<input type="text" name="article[title]">
</p>
<p>
<label>Article Body</label>
<input type="text" name="article[body]">
</p>
<p>
<label>Url</label>
<input type="text" name="article[url_01]">
</p>
<p>
<input type="submit">
</p>
I believe that
, through => Resource
is only needed if you are doing a many-to-many relationship. A one-to-many, which I think is what you want, does not require that. Check out the post and comment relationship shown on the associations page.
EDIT for comment:
If I were you, I would name my form fields normally and construct the database object manually, for example:
<form action="/create" method="post">
<p>
<label>Article Title</label>
<input type="text" name="title">
</p>
<p>
<label>Article Body</label>
<input type="text" name="body">
</p>
<p>
<label>Url</label>
<input type="text" name="url">
</p>
<p>
<input type="submit">
</p>
and then:
post '/create' do
#article = Article.new(
:title => params[:title],
:body => params[:body]
)
#url = url.new(
url_01 => params[:url]
)
#article.url = #url
if #article.save
redirect "/articles"
else
redirect "/articles/new"
end
end