Using Datamapper with Sinatra, it's really easy to save text properties:
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/tailor.db")
class Person
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
property :height, String, :required => false
end
DataMapper.finalize.auto_upgrade!
...
put '/:id/edit' do
p = Person.get params[:id]
p.name = params[:name]
p.height = params[:height]
p.save
end
And the view:
%form
%input{:type => 'text', :name => 'name'}
%input{:type => 'text', :name => 'height'}
%input{:type => 'submit', :value => 'Save'}
With :type => 'text' it is a breeze. However, I'm running into trouble trying to set a boolean (property :test, Boolean, :required => false) with a checkbox. This question is similar, but doesn't address how to do a simple boolean.
In addition, what is the easiest way to set up values that don't accept any string, but rather just a set of predefined options—either as a select that allows one choice, or a set of radio buttons that allows several?
EDIT:
I figured out checkboxes:
p.bool = !params[:bool].nil?
and in the view:
%input{:type => 'checkbox', :name => 'bool', :value => "#{#bool}", :checked => #p.bool}
When you submit a form with a checkbox, if you don’t specify the value form it the default is the string on. Additionally, if the checkbox isn’t selected it doesn’t get submitted at all. So the result is that in your handler, params[:test] (assuming the checkbox is named test) is either nil if it was unchecked, or the string on if it was.
A boolean property in Datamapper is expecting to be assigned either a Boolean (true or false), or one of a small set of values that it will implicitly convert. on and nil are not among those values, so when you try to assign the value to Datamapper it will fail validation and not save:
...
p.test = 'on'
if !p.save
p p.errors[:test]
end
produces:
["Test must be of type TrueClass"]
One way to fix this is to check in your handler and explicitly convert the value to a boolean when assigning to the property:
p.test = params[:test] == 'on' ? true : false
Another technique is to set the value in the HTML checkbox to one of the values that Datamapper will convert, so you can pass it directly. You can have multiple controls with the same name in a form, and they will be submitted in the order they appear in the page. Sinatra will only use the last one when creating the params hash, so if you add a hidden input with the same name as the checkbox before the checkbox, then if it is not checked the value will be that of the hidden input. If it is checked, its value will override that of the hidden input.
%input{:type => 'hidden', :name => 'foo', :value => 'f'}
%input{:type => 'checkbox', :name => 'foo', :value => 't'}
Now params[:test] will be either 'f' or 't',, both of which Datamapper will convert for you, so you can simply do
p.test = params[:test]
Related
Problem
I have a partial, which contains HTML structure for repeating elements of FAQ entry.
partials/_faqitem.haml:
.question
%a.faq-toggle{"data-toggle" => "collapse", :href => "\##{item}"}
= data.faq[item].q
%div{:class => "collapse", :id => "#{item}"}
Text directly in partial
I am using it in my faq.html.md.erb.haml template:
= partial(:"partials/faqitem", :locals => { :item => "so" })
This properly renders HTML and inserts data from data/faq.yaml:
so:
q: What is Stack Overflow?
info: Some other info
The problem arises when I try to add more text back in my template, under = partial call. I can't nail proper indentation that would allow me to render text in template inside div tag, in the same way as "Text directly in partial" is rendered.
Example of nesting in the template:
= partial(:"partials/faqitem", :locals => { :item => "so" })
Text in the template
= partial(:"partials/faqitem", :locals => { :item => "so" })
Text in the template
Depending on the level of nesting, I get either one of these errors:
syntax error, unexpected keyword_end, expecting end-of-input y in partial\n", 0, false);end;_hamlout.buffer << _hamlout.f ^
or
The line was indented 2 levels deeper than the previous line.
Is there any way to add text directly in the template in a way that will be rendered in the same way as if it would be nested inside tag from the partial?
Specific example
To better illustrate desired result, here is an example.
faq.html.md.erb.haml template:
= partial(:"partials/faqitem", :locals => { :item => "so" })
This is some text in the template.
:markdown
Now a *little* markdown to complicate things and then I will insert some information from data file, because I don't want to repeat myself.
= data.faq.so.info
This, used with partials/_faqitem.haml:
.question
%a.faq-toggle{"data-toggle" => "collapse", :href => "\##{item}"}
= spanmarkdown(data.faq[item].q)
%div{:class => "collapse", :id => "#{item}"
Should produce the same result as if the content from template was placed directly inside partial:
.question
%a.faq-toggle{"data-toggle" => "collapse", :href => "\##{item}"}
= spanmarkdown(data.faq[item].q)
%div{:class => "collapse", :id => "#{item}"}
This is some text in the template.
:markdown
Now a *little* markdown to complicate things and then I will insert some information from data file, because I don't want to repeat myself.
= data.faq.so.info
I am not sure about this syntax, I always use render to render my partial and indentation works just fine. Why not just do this:
= render "partials/faqitem", item: "so"
%div
Text in the template
I need to develop a simple File Upload
I'm using padrino and slim-templates. Also generated my views using the padrino command generator such as padrino g admin_pages modelName. Now I want to add a fileUpload field to the generated code... I'm getting this error:
undefined method `name' for nil:NilClass
My question is any way to automatically generate the admin page with this functionality or simply add the field manualy?
This is the code:
= f.text_field :newName, :class => 'form-control input-large input-with-feedback',
= f.label :content, :class => 'control-label'
= f.text_area :content, :class => 'form-control input-large input-with-feedback'
= f.file_field :fileimg
= f.submit pat(:save), :multipart => true ,:class => 'btn btn-primary'
Thanks in advance!
It seems that fileimg field is nil for the record f. Make sure that it isn't `nil, and in that case, it shell be validated by a model validation rule.
I want to add three fields that will be used as counters to the top of the screen on my app. The following code does actually work, HOWEVER, if you put the page in edit mode and then click the "Save" button, the three counter fields disappear and stay that way until you refresh the browser. I am a Haml newbie, and there isn't much documentation out there, and what little there is did not help me.
Here is the relevant Haml code to render the page in question:
- form_for [#application, #item_collection], :html => { 'data-remote' => 'json', :id => 'edit_item_collection', :class => 'show_progress inline_edit' } do |f|
= render 'edit_fields', :f => f
.submit.editing_only
%button.default_action{ :type => 'submit',:id => 'Save'} Save`
And here is the Haml code for the actual page:
.field.large
= f.label :name, nil, 'data-help-id' => 'page_name'
= f.text_field :name, disabled_if_unauthorized(#item_collection, :maxlength => 255, :title => "Edit Page")
= f.error_message_on(:name, :css_class => 'error_message')
.field
= f.label :path, nil, 'data-help-id' => 'page_path'
= f.text_field :path, disabled_if_unauthorized(#item_collection, { :maxlength => 255, :class => 'extra_margin', :title => "Edit Page" })
= f.error_message_on(:path, :css_class => 'error_message')
.field.info
= f.label 'Info'
%ul.elements_count
%li.elements_in_use{ :id => 'elements_in_use' }
%li.unused_elements{ :id => 'unused_elements'}
%li.undefined_elements{ :id => 'undefined_elements'}
When I click "Save," the elements_count unordered list disappears (but reappears if I refresh the browser). I know that the problem is that I need to put "= f." in front of these elements, but I don't see how to do that for lists.
Here is my JavaScript for populating the li elements:
function getElementCount() {
var usage_element_total = $('.usage').size();
var unused_element_total = $('.unused').size();
var undefined_element_total = $('.undefined').size();
$('#elements_in_use').html(usage_element_total + " Elements in use,");
if (unused_element_total < 1) {
$('#unused_elements').html(" 0 unused Elements,");
} else {
$('#unused_elements').html((unused_element_total-1) + " unused Elements,");
}
$('#undefined_elements').html(undefined_element_total + " undefined Elements");
}
Any help would be vastly appreciated. Thanks!
You need to trigger the Javascript that populates the element counts whenever the form is updated. Presumably you’re calling getElementCount when the page loads, you could trigger it when the form is rendered:
$("form").bind("ajax:complete", getElementCount);
But I’m guessing a bit as to how things are wired together and maybe that won’t work for your situation.
I am trying to give size properties to a drop down and select a default value. Doesn't seem to work, on the html I see length="35" but it has no effect on the actual width. How do I do this?
#roles = ['admin','user']
collection_select(:user, :title, roles.all, :id, :name, :value => "user", {:length => 35})
collection_select(:user, :title, roles.all, :id, :name, :value => "user", {:width => 35})
You should use :style => "width:200px" instead of :length.
Or much better, to use a css class for that selection box.
How can I make padrino-admin page generator produce beautiful custom pages?
By default padrino-admin generates pretty ugly admin pages, totally unmaintainable:
.group
=f.label :title
=f.error_message_on :title
=f.text_field :title, :class => :text_field
%span.description Ex: a simple text
.group
=f.label :name
=f.error_message_on :name
=f.text_field :name, :class => :text_field
%span.description Ex: a simple text
--- more annoyingly redundant frak
.group.navform.wat-cf
=f.submit pat(:save), :class => :button
=f.submit pat(:cancel), :onclick => "window.location='#{url(:pages, :index)}';return false", :class => :button
I wrote a nice AdminFormBuilder < AbstractFormBuilder, connected it with set :default_builder, 'AdminFormBuilder', it generates same admin pages from very short code:
= f.inputs :name, :surname, :email
= f.inputs :password, :password_confirmation, :as => :password
= f.input :role, :as => :select, :options => access_control.roles, :descr => 'a simple text'
= f.submits
Now I want padrino g admin_page to generate more of such pages. What should I do?
There are two ways:
1) Make your custom admin gem copying as base the actual padrino-admin
2) Fork the project (where now we support a new admin based on bootstrap) apply your changes and submit a pull request.
Btw the most interesting file for this job is this: https://github.com/padrino/padrino-framework/blob/master/padrino-admin/lib/padrino-admin/generators/admin_page.rb
Here is one-line patch for padrino-admin gem: https://github.com/ujifgc/padrino-framework/commit/b07399bdfbc15d05682237c64580e77558ac9fce
Now I can place copy of original templates folder from padrino-admin-0.10.5/lib/padrino-admin/generators to vendor/padrino-admin/generators and enjoy my own admin page templates.