Rails 3 - Referencing a CSS Class to Form Elements in an Object Dot Style

Rails 3 - Referencing a CSS class to form elements in an Object Dot Style

<%= f.label :password, :class => "myCSSclass" %>

You could do something like the above. hope it helps.

And place the CSS file in the app/assets/stylesheets/<cssFileName.css>

How to add a class to a erb ruby element?

<%= form.submit "Save Item", :class => 'class' %>

How to add a class to an erb statement (in a form, not a do statement)

classes are attributes of DOM elments. Strings don't have classes.

You could wrap it in a span and assign the class to the span... do something like this...

<%= tag.span f.summary, class: 'class-name' %>

or to use p tag...

<%= tag.p f.summary, class: 'class-name' %>

Simple_form class form-horizontal with bootstrap 3 not working in rails 4

I ran into a similar issue and found a pretty quick fix here http://www.iconoclastlabs.com/blog/using-twitter-bootstrap-3-with-simple_form

The simple fix is (after doing the normal simple form bootstrap init stuff -see below), you add the following code to an initializer (like config/initializers/simple_form_bootstrap.rb)

inputs = %w[
CollectionSelectInput
DateTimeInput
FileInput
GroupedCollectionSelectInput
NumericInput
PasswordInput
RangeInput
StringInput
TextInput
]

inputs.each do |input_type|
superclass = "SimpleForm::Inputs::#{input_type}".constantize

new_class = Class.new(superclass) do
def input_html_classes
super.push('form-control')
end
end

Object.const_set(input_type, new_class)
end

and you are off to the races.

The "normal simple form bootstrap init stuff" goes something like:

rails generate simple_form:install --bootstrap

If you want horizontal forms, add this to your simple_form config

config.form_class = "simple_form form-horizontal"

CAVEAT: Based on some recent comments, it appears this many not work for more recent versions of simple_form. I wrote the post some time ago and the project that was using this code (for me) is no longer using simple_form so it's hard to track down the exact version number. I believe this will work for verisions 2.x. Once you get to v3 you may have issues and have to find a different solution.

Customize text_area Ruby on rails to Materialize css

What's the problem you have?, you just need to add the corresponding class:

<%= f.text_area :content id: 'icon_prefix2', class: 'materialize-textarea' %>

Ruby field_with_errors doesn't @extend .control-group .error

I figured it out. This has to do with Twitter-Bootstrap, which has a class .alert in it. That is the class responsible for showing errors with correct styling.
Bootstrap installs who knows where and is easiest to access through Developer Tools in browser (that sucks). I couldn't find where the source of the gem is located (it pointed me to folder that is not a folder). So I copied twitter css files from github directly into my app -> vendor/assets/stylesheets . Now I can deal with the problem.
I changed the style of .alert class inside one of bootstrap files (_alerts.scss) and added extra classes in my custom.css.scss file to make border around inputs red on errors.
Somehow this whole thing worked better in previous apps I made, I think this has to do with changes they made to bootstrap and possibly the fact that I messed with bootstrap's original design (like a full-width header).
The biggest thing that led me to solution was that the class is .alert and not .error like I thought. Also big thanks to browser developer tools, I couldn't find my files without you.

Edit:
Here's the download instructions on Bootstrap to get the files:
http://getbootstrap.com/getting-started/#download

Add a CSS class to %= f.submit %

<%= f.submit 'name of button here', :class => 'submit_class_name_here' %>

This should do. If you're getting an error, chances are that you're not supplying the name.

Alternatively, you can style the button without a class:

form#form_id_here input[type=submit]

Try that, as well.

Using Rails 3.1, where do you put your page specific JavaScript code?

I appreciate all the answers... and I don't think they are really getting at the problem. Some of them are about styling and don't seem to relate... and others just mention javascript_include_tag... which I know exists (obviously...) but it would appear that the Rails 3.1 way going forward is to wrap up all of your Javascript into 1 file rather than loading individual Javascript at the bottom of each page.

The best solution I can come up with is to wrap certain features in div tags with ids or classes. In the javascript code. Then you just check if the id or class is on the page, and if it is, you run the javascript code that is associated with it. This way if the dynamic element is not on the page, the javascript code doesn't run - even though it's been included in the massive application.js file packaged by Sprockets.

My above solution has the benefit that if a search box is included on 8 of the 100 pages, it will run on only those 8 pages. You also won't have to include the same code on 8 of the pages on the site. In fact, you'll never have to include manual script tags on your site anywhere ever again - except to maybe preload data.

I think this is the actual answer to my question.

Using Materialize `chip` and `autocomplete` in Ruby on Rails Form with Associated Models

This is almost certainly not the best way of doing this, but it does work. Please offer suggestions and I will update this, or if someone adds a better answer I will happily mark it as correct. This solution doesn't require much in the way of controller/model changes and is largely done with a (comparatively) short bit of jquery/JS so can be easily repeated within a project.


I've managed to get both autocomplete and chips working with Ruby on Rails, utilising simple_form form helpers where possible.

Effectively I am storing the JSON into a custom attribute for each case and then parsing this with some jquery/javascript when the view is loaded before using this to initialise either autocomplete or chips.

Autocomplete values are translated from name to id within the controller.

Chip values are recognised client side with some JS, and inputs created with the correct name and id for simpleform to automatically save the values as an array to the hash.

Full explaination and code is below.

Thank you to Tom for his helpful comments and input.


autocomplete

Requires you to create an input under variable_name and then add additional functions in the model to translate the name into an id for saving. Effectively following this tutorial.

<%= f.input :profession_name,  input_html: { data: { autocomplete: @professions_json  } } %>

As you can see above, the only real difference from adding a typical simple_form association is the following:

  • f.input rather than f.association - ensures a textbox is rendered rather than a drop down
  • :model_name rather than :model - ensures that the controller recognises this is a name that needs to be converted into an object
  • input_html: { data: { autocomplete: @model_json } } - this adds a custom attribute with all your JSON data, this is parse by

You need to ensure the names of your model are unique.


chips

This is a little more involved, requiring extra javascript functions. The code attachs a callback to the event of adding or removing a chip, before cycling through each and adding a hidden input. Each input has a name attribute which matches what simple_form expects, so it is correctly added to the hash params before being submitted to the controller. I couldn't get it to translate multiple names in an array, so just got it to re-read the id from the original JSON and add that as the value of the input.

  <div id="team_ids" placeholder="Add a team" name="setting[team_ids]" class="chips" data-autocomplete="<%=  @teams_json %>"></div>

From above you can see there are the following deviations from simple_form convention:

  • <div> rather than a <% f.input %> as Materialize chips needs to be called on a div
  • placeholder="..." this text is used as a placeholder once the chips is initialised, this can be left blank / not included
  • name="setting[team_ids]" helps simple_form understand which model this applies to
  • class="chips" ensures that our javascript later knows to initialise chips on this element
  • data-autocomplete="<%= @teams_json %>" saves the JSON data as an attribute of the div for parsing later

Currently the code re-parses the original JSON attribute, it is possible to reference the JSON data that is created on initialisation of the chips, this is likely better but I could not get it to work.

Custom Input Element - someone more experience than myself might be able to play around with this and create a custom element for simple_form... it was beyond me unfortunately.


Ruby on Rails Code

settings_controller.rb

class SettingsController < ApplicationController

...

def new

@user = current_user
@setting = Setting.new
@professions = Profession.select(:name)
@teams = Team.select(:id, :name)

# Prepare JSON for autocomplete and chips
@teams_json = @teams.to_json(:only => [:id, :name] )
@professions_json = @professions.to_json(:only => [:name] )

end

....

private
def setting_params
params.require(:setting).permit( :profession_name, :user_id, :profession_id, team_ids: [])
end

setting.rb

class Setting < ApplicationRecord

has_and_belongs_to_many :teams, optional: true
belongs_to :user
belongs_to :profession, optional: true

def profession_name
profession.try(:name)
end

def profession_name=(name)
self.profession = Profession.find_by(name: name) if name.present?
end

_form.html.erb N.B. this is a partial, as denoted by the preceding underscore

<%= simple_form_for @setting, validate: true, remote: true  do |f| %>

<%= f.input :profession_name, input_html: { data: { autocomplete: @professions_json } } %>

<div id="team_ids" placeholder="Add a team" name="setting[team_ids]" class="chips" data-autocomplete="<%= @teams_json %>"></div>

<%= f.submit %>

<% end %>

Demo

$(document).ready(function() {
// Cycle through anything with an data-autocomplete attribute // Cannot use 'input' as chips must be innitialised on a div $("[data-autocomplete]").each(function() {
var dataJSON = JSON.parse($(this).attr("data-autocomplete"));
// Prepare array for items and add each var items = []; var i; for (i = 0; i < dataJSON.length; ++i) { items[dataJSON[i].name] = null; // Could assign id to image url and grab this later? dataJSON[i].id }

// Check if component needs to be a chips if ($(this).hasClass("chips")) {
// Initialise chips // Documentation: https://materializecss.com/chips.html $(this).chips({ placeholder: $(this).attr("placeholder"), autocompleteOptions: { data: items, limit: Infinity, minLength: 1 }, onChipAdd: () => { chipChange($(this).attr("id")); // See below }, onChipDelete: () => { chipChange($(this).attr("id")); // See below } });

// Tweak the input names, etc // This means we can style the code within the view as we would a simple_form input $(this).attr("id", $(this).attr("id") + "_wrapper");
$(this).attr("name", $(this).attr("name") + "_wrapper");
} else {
// Autocomplete is much simpler! Just initialise with data // Documentation: https://materializecss.com/autocomplete.html $(this).autocomplete({ data: items, });
}

});
});

function chipChange(elementID) {
// Get chip element from ID var elem = $("#" + elementID);
// In theory you can get the data of the chips instance, rather than re-parsing it var dataJSON = JSON.parse(elem.attr("data-autocomplete"));
// Remove any previous inputs (we are about to re-add them all) elem.children("input[auto-chip-entry=true]").remove();
// Find the wrapping element wrapElement = elem.closest("div[data-autocomplete].chips")
// Get the input name we need, [] tells Rails that this is an array formInputName = wrapElement.attr("name").replace("_wrapper", "") + "[]";
// Start counting entries so we can add value to input var i = 0;
// Cycle through each chip elem.children(".chip").each(function() {
// Get text of chip (effectively just excluding material icons 'close' text) chipText = $(this).ignore("*").text();
// Get id from original JSON array // You should be able to check the initialised Materialize data array.... Not sure how to make that work var chipID = findElement(dataJSON, "name", chipText);
// ?Check for undefined here, will be rejected by Rails anyway...?
// Add input with value of the selected model ID $(this).parent().append('<input value="' + chipID + '" multiple="multiple" type="hidden" name="' + formInputName + '" auto-chip-entry="true">');

});
}

// Get object from array of objects using property name and valuefunction findElement(arr, propName, propValue) { for (var i = 0; i < arr.length; i++) if (arr[i][propName] == propValue) return arr[i].id; // Return id only // will return undefined if not found; you could return a default instead}

// Remove text from children, etc$.fn.ignore = function(sel) { return this.clone().find(sel || ">*").remove().end();};

// Print to console instead of posting$(document).on("click", "input[type=submit]", function(event) {
// Prevent submission of form event.preventDefault();
// Gather input values var info = []; $(this).closest("form").find("input").each(function() { info.push($(this).attr("name") + ":" + $(this).val()); });
// Prepare hash in easy to read format var outText = "<h6>Output</h6><p>" + info.join("</br>") + "</p>"; // Add to output if exists, or create if it does not if ($("#output").length > 0) { $("#output").html(outText); } else { $("form").append("<div id='output'>" + outText + "</div>"); }

});
<!-- jquery --><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- Materialize CSS --><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<!-- Materialize JavaScript --><script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<!-- Material Icon Webfont --><link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

<form class="simple_form new_setting" id="new_setting" novalidate="novalidate" data-client-side-validations="" action="/settings" accept-charset="UTF-8" data-remote="true" method="post"><input name="utf8" type="hidden" value="✓">

<div class="input-field col string optional setting_profession_name"> <input data-autocomplete='[{"id":1,"name":"Consultant Doctor"},{"id":2,"name":"Junior Doctor (FY1)"}]' class="string optional" type="text" name="setting[profession_name]" id="setting_profession_name" data-target="autocomplete-options-30fe36f7-f61c-b2f3-e0ef-c513137b42f8" data-validate="true"> <label class="string optional" for="setting_profession_name">Profession name</label></div>
<div id="team_ids" name="setting[team_ids]" class="chips input-field" placeholder="Add a team" data-autocomplete='[{"id":1,"name":"Miss T"},{"id":2,"name":"Surgical Take"}]'></div>

<input type="submit" name="commit" value="Create Setting" data-disable-with="Create Setting">
</form>


Related Topics



Leave a reply



Submit