Change 'Please match the format requested.' to something else? - ruby-on-rails

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

Related

If Else in HAML to check the value of a "select" box

= form_tag questions_path, :method=>:post do
= label :question, :type, 'Type: '
= select :question, :type, %w(Text Picture Audio Video), :id=> :question_type_combo
**- if :question_type_combo.selected != 'Text'**
= label :question,:url, 'URL: '
= text_field :question,:url, :id=> :question_url_text
= submit_tag 'Add Question',:id=>:add_question_button
Is something of this sort possible in HAML? I wish to render the textfield only for certain options if selected in the SELECT BOX above.
Yes and no. You can write a conditional based on the values of the record that you bind to the form:
= form_for #question do |f|
= f.label :type
= f.select, :type, %w(Text Picture Audio Video), id: 'question_type_combo'
- unless f.object.question_type_combo === 'Text'
= f.label :url
= text_field :url, id: 'question_url_text'
But this would only change the visibility after the user submits the form and not be very useful.
Instead you can just use jQuery to create an event handler for the ´change´ event.
$(document).on('change','#question_type_combo', function(){
var type = $(this).first(':selected').val();
var $other_input = $('#other_input');
if (type == 'Text') {
$other_input.hide();
} else {
$other_input.show();
}
});
// sets the initial state
// if you are using turbolinks
$(document).on('page:load', function(){
$('#question_type_combo').trigger('change');
});
// if you are not using turbolinks
$(function(){
$('#question_type_combo').trigger('change');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<div class="field">
<label>Type</label>
<select name="question[question_type_combo]" id="question_type_combo">
<option>Text</option>
<option>Something else</option>
</select>
</div>
<div class="field" id="other_input">
<label>URL</label>
<input type="text" name="question[url]">
</div>
</form>
- if :question_type_combo.selected != 'Text' This is not possible in the haml view, If you want to do something based on selected option you have to use js.
Or you may set the selected option using similar code if you have controller objects:
= select_tag("fee_discount", options_for_select(Fee.discounts.keys.map {|k| [k.titleize, k]}, selected: "#{"rewards" if #vendor.present? && #vendor.approved?}"), include_blank: true)
Or
You may keep the label and text_field inside a div with hide class.
And then using javascript you may hide unhide the div.

Select option required Ruby Rails

I have a mix of ruby rails code
I have a form with a selection option that i want to be a required, and i want to validate. If user do not select anything i want to validade with a error message.
However, I try to copy past code from internet, I'm new at ruby rails and I still not have a error message.
I also check that i use 'required' , or if i use 'validates_presence_of' doesn't make difference because it's a submit form (i think)
test_filteR_form.rb
class TestFilterForm < ApplicationForm
attribute :model, String
validates_presence_of :model
end
.html.erb
<%= f.input :fill_form_error_message,:as => :hidden, :input_html => { :value =>I18n.t('test.fill_form_error') } %>
<%= f.input :model, label: I18n.t('test.filters.model'), autofocus: true, input_html: {class: 'input-xlarge chosen-select' }, collection: TestType.options_for_select, include_blank: true %>
"/>
controller
def paginate
#test_form = TestForm.new(params)
unless #test_form.valid?
#model = params[:test_filter_form][:model]
#h_model = #model.pluralize + 'H'
#history, _query, #test_fields = TestQueryService.search!(params)
session[:test_query] = _query
session[:test_klass] = #model
else
format.json { render :json => { :error => #test_form.errors.full_messages }, :status => 422 }
end
js.coffee
$contentDiv.on 'ajax:error', 'form[data-value]', (event, xhr, status, error) ->
data = $.parseJSON(xhr.responseText)
$result = $(#).parents('tr').find('[data-result]')
controller.resultUpdateError xhr.status, $result.data('result'), data, $(#)
# Hide row loading spinner
$(#).parents('tr').find('span[role="result_update_spinner"]').hide()
# Hide saved form
$(#).parents('tr').find('.saved_form').hide()
resultUpdated: (result, data, $form) ->
if data.flash != undefined
# Sets a sucess message on page top
flash data.flash.type, data.flash.message
# Sets a success message on row
$fieldForm = $form.parents('tr').find(".messages")
$fieldForm.find('.controls').empty()
$fieldForm.find('.control-group .controls').css('color', 'green').append #_inlineMessage("Gravado com sucesso")
# Hide success message after some time
setTimeout ((self) ->
->
$fieldForm.find('.control-group .controls').empty()
return
)(this), 4000
Since you are dynamically created the selection box then there must be a default value selected which is not nil so there is no change seen you can manually create selection like this:
<div class="form-group">
<%= f.label :select_user_country %><br/>
<select class="form-control select2" name="user[country_id]">
<option value="" selected disabled>Select a Country</option>
<%#countries.each do |country|%>
<option value="<%=country.id%>"><%=country.name%></option>
<%end%>
</select>
</div>

Trying to use typeahead to autocomplete acts-as-taggable-on tag list

I have a page on my website with a form that allows users to add tags to an item. I want to incorporate typeahead so that when a user starts typing a tag in the input box, a dropdown list appears with existing tags that match the string. I think I can accomplish this with the basic example but it doesn't seem to be working.
Here is my show page with the script:
<div id="add-tag-form">
<%= form_tag add_tag_path, id: "custom-tag",
class: "form-group",
autocomplete: "off",
remote:true
do %>
<%= text_field_tag :tag, nil, placeholder: 'Add tag', class: "form-control typeahead", id: 'add-tag' %>
<%= submit_tag 'Add' %>
<span id = "tag-block" class="help-block"></span>
<% end %>
</div>
<script>
$(document).ready(function(){
var substringMatcher = function(strs) {
return function findMatches(q, cb) {
var matches, substringRegex;
// an array that will be populated with substring matches
matches = [];
// regex used to determine if a string contains the substring `q`
substrRegex = new RegExp(q, 'i');
// iterate through the pool of strings and for any string that
// contains the substring `q`, add it to the `matches` array
$.each(strs, function(i, str) {
if (substrRegex.test(str)) {
matches.push(str);
}
});
cb(matches);
};
};
var tags = Tag.all.pluck(:name);
$('#add-tag-form .typeahead').typeahead({
hint: true,
highlight: true,
minLength: 1
},
{
name: 'tags',
source: substringMatcher(tags)
});
});
</script>
In the console I get Uncaught SyntaxError: unexpected token : which is pointing to the :name in the line var tags = Tag.all.pluck(:name);. My thoughts are the ruby isn't being recognized within the script tags but I could be wrong. What is the correct way to do this?
Also, do I need the $(document).ready line at the start of the script?

How to use AngularJS with Rails Form?

I'm using simple_form with AngularJS:
= simple_form #post do |f|
= f.input :title, input_html: { "ng-model" => "title" }
It works great for my scenario on new post, but for editing on existing post, it doesn't bind/fill in existing value from post's title on form. From what I thought Rails already fill in the value, but AngularJS wipes it out after the page load because $scope.title is blank.
I found the trick is to actually create a controller with an init function that takes the value you want. In my case I just created a app/assets/javascripts/angular_app.js file that looks like this:
//= require_self
AngularRails = angular.module('AngularRails', []);
AngularRails.controller('PostFormCtrl', function($scope) {
$scope.init = function(title) {
$scope.title = title;
}
});
You'll have to translate the view into haml but it should look something like this:
<div ng-app="AngularRails">
<div ng-controller="PostFormCtrl" ng-init="init('<%= #post.title %>')">
<%= form_for #post, html: {name: "postForm", "novalidate" => true} do |f| %>
<%= f.text_field :title, "ng-model" => "title", required: true %>
<%= f.submit "ng-disabled" => "postForm.$invalid" %>
<% end %>
</div>
</div>
Remember to include the angular_app file into application.js and it should world. Obviously, this isn't a very robust solution but you could use active model serializer to convert the rails object to a json object. Then, pass that json object to the init function and in the controller, iterate over the key/value pairs of that json object and set them to $scope. Something like this:
AngularRails.controller('PostFormCtrl', function($scope) {
$scope.init = function(input) {
Object.keys(input).forEach(function(key) {
$scope[key] = input[key];
});
};
});
Hope that helps!

best_in_place gem: styling OK button

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);
}
}
}

Resources