best_in_place gem: styling OK button - ruby-on-rails

best_in_place is working well but I'd like to use fontawesome icons for the optional "OK" button, rather than a string. How can I incorporate the '<i class="icon-ok"></i>'.html_safe syntax in the :ok_button hash?
= best_in_place #book, :default_price_amount, :html_attrs => {:class => 'medium_no_dropdown'}, :ok_button => "OK"

This is an old question, and the desired functionality is now supported in the best_in_place gem using the :ok_button_class option. Usage is like this:
<%= best_in_place #post, :title, :ok_button => "Submit", :ok_button_class => "btn post-title" %>

There is a solution , but not exactly to add a style to the ok_button . If you don't mind to use unicode glyphs , you can try :
= best_in_place #book, :default_price_amount, :html_attrs => {:class => 'medium_no_dropdown'}, :ok_button => "✓".html_safe
The table with all the unicode characters could be your reference for another variant .
The problem with the real styling of ok_button is that the hash is accepting data-attribute only for defining of the button . Probably in one of the next versions of BIP this will be improved.
In the source code, where the button is created (best_in_place.js):
if(this.okButton) {
output.append(
jQuery(document.createElement('input'))
.attr('type', 'submit')
.attr('value', this.okButton)
)
}
'value' is what we pass on the hash . If there is a way to make a reference to the glyph-codes , defined by awesome font ( for icon-ok ) , it would be beautiful .

Since I spent couple hours to do the same thing, I found that we can override the prototype of this function to create <button> instead of <input type="button">. However, the activateForm function only wait for click event from input[type="button"] and since I can't override it, so I try another (a bit dirty) way – and it works.
Overwrite this script at another js tag/files
BestInPlaceEditor.prototype.placeButtons = function (output, field){
'use strict'
// the ok button isn't changed
if (field.okButton) {
output.append(
jQuery('<button>').html(field.okButton).attr({
type: 'submit',
class: field.okButtonClass
})
)
}
if (field.cancelButton) {
// create new cancel "<button>"
var $resetBtn = jQuery('<button>').html(field.cancelButton).attr({
type: 'reset',
class: field.cancelButtonClass
}),
// and traditional cancel '<input type="button">', but this should be hidden
$_resetBtn = jQuery('<input>').val(field.cancelButton).css({ display: 'none' })
.attr({
type: 'button',
class: '__real-btn-close-best_in_place'
});
// and bind event to 'trigger' click traditional button when the new <button> is clicked
$resetBtn.bind('click', function (event) {
$(event.currentTarget).parents('form.form_in_place').find('input.__real-btn-close-best_in_place').trigger('click');
event.stopPropagation(); // << also neccessary
});
// append both
output.append($_resetBtn).append($resetBtn);
}
}
}

Related

How to use Ajax for nested form on active admin rails

Please help me out with this problem. I am new on rails
I am trying to use the ajax method to get values on the active admin nested form. But only the top of the form gets dynamic values. After changing values on the top one form on the nested form then ajax makes a request then it changes the values(i.e. update the form values).
But after adding a new buy line item then ajax makes a request but doesn't update the values on the form i.e.same values for every new buy line item.
my js code
app/assets/javascripts/buys.js
// for nested buyline items of buys resources
$('.lineItem').change(function () {
$.ajax({
url: '/admin/get_buys_buy_line_item',
type: 'GET',
dataType: "json",
data: {
product_id: $('.lineBuyProduct').val(),
buy_quantity: $(".lineBuyQuantity").val(),
buy_quantity_unit: $(".lineBuyUnit").val(),
buy_price: $(".lineBuyAmount").val(),
},
success: (data) => {
alert(data);
// if (data == null) {
// document.getElementById('lineQuantity').value = ' ';
// document.getElementById('lineAmount').value = ' ';
// }
// else {
// document.getElementsByClassName("linebuyQuantity").value = data['1'];
// document.getElementsByClassName('linebuyAmount').value = data[0];
// console.log("Dynamic select OK!")
// }
}
});
});
});
My active admin forms
f.inputs 'Line Items', class: "lineItem" do
table do
thead do
tr do
th 'S.N', class: 'form-table__col'
th 'Product', class: 'form-table__col'
th 'Quantity', class: 'form-table__col'
th 'Unit', class: 'form-table__col'
th 'Amount', class: 'form-table__col'
th 'Expiry Date', class: 'form-table__col'
th 'Destroy', class: 'form-table__col'
end
end
end
f.has_many :buy_line_items, new_record: 'Add Buy Line Item', heading: false, allow_destroy: true do |x|
x.input :sn, label: false
x.input :product, label: false, as: :select2, collection: Product.drop_down_options,
input_html: { required: true, class: 'lineBuyProduct' }
x.input :buy_quantity, label: false, input_html: { required: true, class: 'lineBuyQuantity' }
x.input :buy_quantity_unit, label: false, collection: Unit.all.pluck(:name),
input_html: { class: 'lineBuyUnit' }
x.input :buy_price, label: false,
input_html: { required: true, class: 'lineBuyAmount' }
x.input :expiry_date, as: :date_picker, input_html: { style: 'width:auto' }, label: false
end
end
Some of my screenshots of how my program behaves and what my expectations are
In this image my first selection of product on buy line iteme then on request we can see json are renderd
But in this image after selecting another product again same json data are rendered that means doesn't update with different respective product values
And there are also some problems on active admin blaze theme, after adding class on form then it changes to legend color and add item button colors to light brown
This picture is about after removing custom class name lineItem.But, after removing this class then ajax doesn't hit for that form
Might be i faced such problem due to nested form's class on active admin.While i used default class of those form from inspect then it even doesn't hit to that form for ajax request.
So, please expert team help me to slove this problem.
If you want to send the data to create a new entity(record), you need to use the http POST method: https://api.jquery.com/jquery.post/
The GET request it just retrieves an already existing entity. Probably that's why you can read some data after an ajax call, but it's not updated.
More about the differences between GET and POST methods:
When should I use GET or POST method? What's the difference between them?
If this fix doesn't work, check the rails logs.
I have never used anything related to Ruby, and I'm not completely sure if I understood your question. I'm assuming you have problems with the AJAX behavior for new items in the list. Considering that, I think your problem could be related to attaching the "change" event to the new items. I think there might be many different other ways to get the behavior you want, but I will just elaborate on your solution using JS.
From the activeAdmin's code you can see they trigger an event called "has_many_add:after" when a new item was successfully added to its parent. Considering that, I would try changing your Javascript code to:
// for nested buyline items of buys resources
$("#buy_line_items").on("has_many_add:after", function(event, fieldset, parent){
fieldset.change(function () {
$.ajax({
url: '/admin/get_buys_buy_line_item',
type: 'GET',
dataType: "json",
data: {
product_id: $(this).find('.lineBuyProduct').val(),
buy_quantity: $(this).find(".lineBuyQuantity").val(),
buy_quantity_unit: $(this).find(".lineBuyUnit").val(),
buy_price: $(this).find(".lineBuyAmount").val()
},
success: (data) => {
alert(data);
// if (data == null) {
// document.getElementById('lineQuantity').value = ' ';
// document.getElementById('lineAmount').value = ' ';
// }
// else {
// document.getElementsByClassName("linebuyQuantity").value = data['1'];
// document.getElementsByClassName('linebuyAmount').value = data[0];
// console.log("Dynamic select OK!")
// }
}
});
});
});
I'm very confident that this will work, mainly because I don't have how to try it (if you give me the generated HTML maybe it'd help me). You can try it and reach back to see if it worked or if any adjustment is required.
You might want to change your commented lines accordingly. Also about the identifier "#buy_line_items" I'm not sure if it exists in the rendered HTML so you might need to adjust that as well.

Change 'Please match the format requested.' to something else?

Is there an easy way to customise the message that appears when the pattern: "([A-Za-z0-9\-\_]+)" argument is not satisfied and this message appears:
e.g.
<%= f.text_field :username, pattern: "([A-Za-z0-9\-\_]+)" %>
You can add a "title" attribute to append on to that message:
<%= f.text_field :username, pattern: "([A-Za-z0-9\-\_]+)" , title: "Letters, numbers, hyphen, and underscore Only"%>
This will result in the following message:
Please match the format requested: Letters, numbers, hyphen, and underscore Only
Otherwise, for more custom messages, you can use client side validation (via javascript) server side validations (via rails and ActiveModel)
For a fully custom message you must use JavaScript.
const username = document.querySelector("#user_username");
username.addEventListener("invalid", ({ target }) => {
target.setCustomValidity("Hello World!");
});
username.addEventListener("change", ({ target }) => {
target.setCustomValidity("");
});
<form>
<input id="user_username" name="user[username]" pattern="[A-Za-z0-9_-]+" />
<input type="submit" />
</form>
You could add this in your Rails view in a similar manner:
<%= f.text_field :username, pattern: "[A-Za-z0-9_-]+" %>
...
<script>
const username = document.querySelector("#user_username");
username.addEventListener("invalid", ({ target }) => {
target.setCustomValidity("Hello World!");
});
username.addEventListener("change", ({ target }) => {
target.setCustomValidity("");
});
</script>
If you need this sort of functionality more often, or want a cleaner solution you might want to write some unobtrusive JavaScript. For example:
function useCustomValidity({ target }) {
target.setCustomValidity(target.getAttribute("custom-validity"));
}
function clearCustomValidity({ target }) {
target.setCustomValidity("");
}
document.querySelectorAll("input[custom-validity]").forEach((input) => {
input.addEventListener("invalid", useCustomValidity);
input.addEventListener("change", clearCustomValidity);
});
With the above JavaScript being loaded (you need to add additional handlers when using Turbolinks). You can now have the following view:
<%= f.text_field :username, pattern: "[A-Za-z0-9_-]+", 'custom-validity': 'Hello World!' %>
See also: MDN Input: Client-side validation

Rails simple_form collection to %ul?

so I will get right into asking.
This is an input from my simple_form
= f.input :focus, collection: [[t('alg.focus.execution'), 'Execution'], [t('alg.focus.management'), 'Management'], [t('alg.focus.client'), 'Client'], [t('alg.focus.development'), 'Development']], :wrapper => :inline_checkbox, :include_blank => false, label: t('recruiter.jobs.new.focus')
and the output in html is this
<div class="select required job_focus">
<select class="select required" name="job[focus]" id="job_focus">
<option value="Execution">Execuție</option>
<option value="Management">Management</option>
<option value="Client">Client</option>
<option value="Development">Dezvoltare</option></select></div>
Now what I want to do is to change the select tag into ul and option into li, this way I can customize the drop down menu as I want.
I have found a way in simple_form, that you can add a wrapper class to tag or to use another tag instead of other tags, but as I have seen is only limited to some tags like input, label, error etc. But I could not found how to change select and option.
by adding this element to the input :wrapper => :inline_checkbox,
and by adding this into simple_form.rb this
config.wrappers :inline_checkbox, :error_class => 'error' do |b|
b.use :html5
b.use :input
b.use :label
b.wrapper :tag => 'div', :class => 'controls' do |ba|
ba.use :error, :wrap_with => { :tag => 'span', :class => 'help-inline' }
ba.use :hint, :wrap_with => { :tag => 'p', :class => 'help-block' }
end
end
So I need your help.
Thank in advance.
You're looking at this the wrong way. SimpleForm isn't build to do the kind of thing you're looking for. It's built to make forms. I get that you're trying to fake a <select> with a <ul> but that's not typical form behavior.
You'll just want to create the <ul> using normal view helper methods. Then, you'll need to use JS to make any stage changes are saved to a hidden input field that will get POSTed with the rest of your form.
Example in Jquery
function customSelect(){
// Iterate over each select element
$('select').each(function () {
// Cache the number of options
var $this = $(this),
numberOfOptions = $(this).children('option').length;
// Hides the select element
$this.addClass('s-hidden');
// Wrap the select element in a div
$this.wrap('<div class="selects"></div>');
// Insert a styled div to sit over the top of the hidden select element
$this.after('<div class="styledSelect"></div>');
// Cache the styled div
var $styledSelect = $this.next('div.styledSelect');
// Show the first select option in the styled div
$styledSelect.text($this.children('option').eq(0).text());
// Insert an unordered list after the styled div and also cache the list
var $list = $('<ul />', {
'class': 'options'
}).insertAfter($styledSelect);
// Insert a list item into the unordered list for each select option
for (var i = 0; i < numberOfOptions; i++) {
$('<li />', {
text: $this.children('option').eq(i).text(),
rel: $this.children('option').eq(i).val()
}).appendTo($list);
}
// Cache the list items
var $listItems = $list.children('li');
// Show the unordered list when the styled div is clicked (also hides it if the div is clicked again)
$styledSelect.click(function (e) {
e.stopPropagation();
$('div.styledSelect.active').each(function () {
$(this).removeClass('active').next('ul.options').hide();
});
$(this).toggleClass('active').next('ul.options').toggle();
});
// Hides the unordered list when a list item is clicked and updates the styled div to show the selected list item
// Updates the select element to have the value of the equivalent option
$listItems.click(function (e) {
e.stopPropagation();
$styledSelect.text($(this).text()).removeClass('active');
$this.val($(this).attr('rel'));
$list.hide();
/* alert($this.val()); Uncomment this for demonstration! */
});
// Hides the unordered list when clicking outside of it
$(document).click(function () {
$styledSelect.removeClass('active');
$list.hide();
});
});
}
and then just call this function wherever you need, on a click, when the document is ready.
You can use https://github.com/Slashek/bootstrap-select-rails gem and simply add selectpicker class to the input_html.
An example such as
<%= f.input :sharing_policy,
label: t('portal_new.privacy.share_designs'),
collection: sharing_policy_options,
selected: current_school[:sharing_policy],
include_blank: false,
include_hidden: false,
input_html: {class: 'selectpicker'}
%>

Rails check_box_tag - get all the values checked in view(haml) file

So I have multiple checkboxes on my page. I collect all of them like shown in the code below. I would like to access the values of the array before passing it on to the controller
= check_box_tag "names[]", ob.name, false, class: 'cbx'
I am able to pass them with my older code
%fieldset.actions
= form.submit "Upgrade", :class => 'button'
Logs:
Processing by SomeController#create as HTML Parameters:
{"utf8"=>"✓",
"names"=>["ron", "jacob"], "commit"=>"NameButton"}
Ok. So i would like to access all values in my haml files. Is there a way before i submit my form, I can access which checkboxes are selected.
I would like to pass the names[] to my controller as a parameter.
=link_to script_name1, { :action => 'create', :names => 'dontknowhowtopassnames[]' }, :method => :post
Rails version - 3.2.17
You can do that using Javascript.
The exact implementation depends on what exactly you want to do with those values, but you could, for example, use the change event to track and maintain an array of all checked values :
/*
* should properly init this array if some of your checkboxes are already
* checked when the form is loaded
*/
var names = [];
$(document).ready(function() {
$(document).on('change', '.cbx', function() {
var name = $(this).val();
var idx = names.indexOf(name);
if ($(this).prop('checked') && idx === -1) {
names.push(name);
} else if (!($(this).prop('checked')) && idx >= 0) {
names.splice(idx, 1);
}
});
});
Updated with complementary answer:
To submit a form with a link instead of a button:
In your view, replace
%fieldset.actions
= form.submit "Upgrade", :class => 'button'
with
= link_to "Submit", "#", :class => 'submit_link'
Then in your Javascript, add the following inside the $(document).ready body:
$(document).on('click', 'a.submit_link', function(e) {
$(this).closest('form').submit();
e.preventDefault();
});
And that's it. You're making your life very complicated by trying to serialize the form data on your own while the form's submit() method can take care of it for you. You can serialize data on your own, and it's sometimes useful, for instance when dealing with AJAX, but in your case the default submit() is perfect for the job.

RoR Displaying associated records based on select menu option

I am new to RoR and I am looking for a way to dynamically display values in some text fields depending on the option chosen from a select menu in a form. The associated records are stored in another table. Hopefully in a way AJAX can be used such that it wouldn’t require a page refresh. I have seen examples in which select menus are dynamically changed according to values of select menus but not text fields.
Thanks,
Alex
PS: I am using rails 3.
Here's how I did it. I'm a Rails beginner too, so this may not be the best/most efficient way, but it works:
JS:
$('#select_box_id').live('change', function() {
var select_field_val = $('#select_box_id').val();
if(select_field_val == "") select_field_val = "0";
$.get('/some_controller_action/' + select_field_val, function(data) {
$('#text_field_div').html(data);
});
return false;
});
Controller:
def some_controller_action
#some_processing...
result = whatever
render :partial => "my_partial", :locals => { :text_field_value => result }
end
View:
<div id="text_field_div">
<%= render :partial => 'my_partial', :locals => { :text_field_value => "" } %>
</div>
Partial:
<%= text_field_tag :text_field, text_field_value %>

Resources