Preserve option class in jquery select2 dropdown items - jquery-select2

Does anybody know if it possible to transfer the element class to the jquery select2 dropdown items?
<select>
<option class="group-1"></option>
<option class="group-1"></option>
<option class="group-2"></option>
<option class="group-2"></option>
</select>
I am trying to build a dynamic list based on a user's selected value from another dropdown, so if a user selects 'group 1' then I wish to only show the items with class 'group-1'. I was hoping to be able to do something like $('ul.select2 li.group-2').hide().
EDIT: I am splitting this question into 2 for clarity in the answers I have developed to solve this problem,
How to preserve option element classes in the select2 listing? - I am providing an answer below for those interested in this.
How to dynamically filter a select2 listing based on option class? I have opened a new question thread in which I am answering this question as the solutions quite different.

I managed to solve the question by using the templating functionality of the select2 plugin,
<script type="text/javascript">
(function( $ ) {
'use strict';
var opportunities = $('select#opportunities').select2({
dropdownParent: $('#select-opportunity'),
templateResult: formatItem
});
function formatItem (state) {
if (!state.id) { return state.text; }
var $state = $(
'<span class="opportunity-item ' + state.element.className + '">' + state.text + '</span>'
);
return $state;
}
})( jQuery );
</script>
NOTE: this does not allow you to manipulate the select2 dropdown list because the class attribute is only transfered to the inner custom <span> element of our item, and not to the actual <li> element of the dropdown select2 list. However it becomes very useful to further style the dropdown list, for example,
<style>
.opportunity-item.group-1::after{
content: '';
display: inline-block;
margin-left:5px;
width: 10px;
height: 10px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
background-color: green;
}
.opportunity-item.group-2::after{
content: '';
display: inline-block;
margin-left:5px;
width: 10px;
height: 10px;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
background-color: blue;
}
</style>
resulting in,

Related

Changing colors and styling of collection_select dropdown with rails bootstrap and select2 implemented

I want to change the colors and style a dropdown in rails to match the rest of my web app styling. I am using Rails, Bootstrap, and select2 and I have no idea where to add css and how to even begin accessing the the different parts of the dropdown so that I can color them and make them fit with the rest of the design on the app.
this is the implementation of the dropdown
<%= collection_select(:id, :name, #product, :id, :name,{:include_blank => true},{class: "js-example-basic-single" }) %>
Please advise me on how to accomplish styling this item, thanks.
I'm not sure what your question is so I will try to answer it as best as I can. I believe you should be adding your CSS to the application.css file that should be under app\assets\stylesheets. Then if you want to customize all your dropdowns the same you can use the select tag in css. Here is an example
select {
position: relative;
display: inline-block;
cursor: pointer;
text-align: center;
text-decoration: none;
font-family: Arial, Helvetica, sans-serif;
border-radius: 3px;
border: none;
background: #f1f1f1;
padding: 2px;
}
select:hover {
background-color: dodgerblue;
}
<select>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
Quick update
adding the below worked for me... suprising this isnt in the bootstrap gem
select {
display: block;
width: 100%;
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #495057;
background-color: #fff;
background-clip: padding-box;
border: 1px solid #ced4da;
border-radius: 0.25rem;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}

jQuery ui Draggable with <select> tag

With select tag draggable does not work. What can I do with this?
<div id="draggable">
<select style="width: 200px;">
</select>
</div>
$(document).ready(function(){
$('#draggable').draggable();
});
Demo: https://jsfiddle.net/wrqLwyk6/2/
This may help you
HTML:
<ul id="draggable">
<li>
<select style="width: 200px;">
<option>56456456456</option>
<option>sdfsdf</option>
</select>
</li>
<ul>
CSS:
#draggable { list-style-type: none; margin: 0; padding: 0; width: 60%; }
#draggable li { margin: 0 3px 3px 3px; padding: 0.4em; cursor:move; }
Script:
$(document).ready(function(){
$('#draggable').draggable();
});
Check at : https://jsfiddle.net/759td80m/
Actually, the <div> is draggable, but you should actually click and drag on the <div> itself, not the <select>. Almost all of the form controls like <textarea> blocks dragging in similar way. To test this, just add a background color or something to <div> and drag the remaining area to the right of <select>.
But users wouldn't be aware of this invisible area (if there is any). It is to handle such situations we have the handle option:
$(document).ready(function() {
$('#draggable').draggable({
handle: '.handle'
});
});
#draggable .handle {
display: inline-block;
float: left;
width: 20px;
height: 20px;
background: dodgerblue;
border: 1px solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<div id="draggable">
<span class="handle"></span>
<select style="width: 200px;">
</select>
</div>
In the demo, click and drag the blue box which is used as handle.

jQuery mobile- How to change the icon of select to a custom icon?

I have to change the select icon in <select> to a fa icon.
The code I use:
<select class="sortBy" name="sortBy" data-url="${url}" data-icon="ui-icon-arrow-sort">
<option value="">Sort By</option>
<option value="new" selected="selected">Newest</option>
<option value="old">Oldest</option>
</select>
The CSS is:
.ui-icon-arrow-sort{
content: "\f0dd";
display: inline-block;
font: normal normal normal 14px/1 FontAwesome;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
}
For now it's not working well.
Since Font-Awesome icons aren't SVG, they can't be used as background-image as jQM icons. However, you still can override arrow icon in select, but it requires JS to add custom class to button within select.
First, add data-icon="false" to select to prevent jQM from adding arrow icon. Then, on pagecreate, add your custom class to select's button.
.ui-icon-arrow-sort:after {
content:"\f0dd";
font: normal normal normal 20px/1 FontAwesome;
position: absolute;
top: 0;
bottom: 0;
margin: 0 auto;
right: 30px;
line-height: 2em;
}
$(document).on("pagecreate", "#pageID", function () {
$(".sortBy", this).closest(".ui-btn").addClass("ui-icon-arrow-sort");
});
Demo

Unexpected result with jQuery.after

I have an application with nearly 70 items in a <select> that allow multiple selection.
I'm trying to replace its behavior by two lists, where the user can drag/drop between a source list to a target list. The initial select should reflect the moves.
I'm trying to make it generic because I have to reuse the behavior in several pages, sometimes several times in the same page.
What I have by now (I reduced the actual number of items for readability) :
<div style="border: solid 1px red; overflow:auto">
<select name="lst1" multiple="multiple" id="lst1" class="go">
<option value="1">Element N°1</option>
<option value="2">Element N°2</option>
<option selected="selected" value="3">Element N°3</option>
<option value="4">Element N°4</option>
<option value="5">Element N°5</option>
</select>
</div>
<script type="text/javascript">
$(function () {
$(".go").each(function (index, el) {
var source = document.createElement("ul");
var target = document.createElement("ul");
$(el).after(source);
$(el).after(target);
$(source).addClass("sourceItems");
$(target).addClass("targetItems");
$(el + "option:selected").each(function (index2, sub) {
var item = document.createElement("li");
item.textContent = $(sub).text();
$(target).append(item);
});
$(el + "option:not(:selected)").each(function (index2, sub) {
var item = document.createElement("li");
item.textContent = $(sub).text();
$(source).append(item);
});
$(source).sortable({ connectWith : target});
$(target).sortable({ connectWith : source });
});
});
</script>
<style type="text/css">
.sourceItems, .targetItems { list-style-type: none; float:left; margin: 0; padding: 0; margin-right: 10px; background: #eee; padding: 5px; width: 200px; border: solid 1px black; }
.sourceItems li, .targetItems li { margin: 5px; padding: 5px; width: 180px; height: 12px; cursor: move; }
</style>
This code is producing unexpected result :
The "target" list's generated DOM looks like ok :
<ul class="targetItems ui-sortable">
<li>Element N°3</li>
<li>Element N°7</li>
<li>Element N°8</li>
<li>Element N°14</li>
<li>Element N°20</li>
<li>Element N°15</li>
<li>Element N°21</li>
</ul>
but the "source" list's generated DOM is a mess (it contains the page DOM in each element)):
<li>
.sourceItems, .targetItems { list-style-type: none; float:left; margin: 0; padding: 0; margin-right: 10px; background: #eee; padding: 5px; width: 200px; border: solid 1px black; }
.sourceItems li, .targetItems li { margin: 5px; padding: 5px; width: 180px; height: 12px; cursor: move; }
</li><li>
</li><li></li><li>
.sourceItems, .targetItems { list-style-type: none; float:left; margin: 0; padding: 0; margin-right: 10px; background: #eee; padding: 5px; width: 200px; border: solid 1px black; }
.sourceItems li, .targetItems li { margin: 5px; padding: 5px; width: 180px; height: 12px; cursor: move; }
Element N°1
Element N°2
Element N°3
Element N°4
Element N°5
Element N°6
Element N°7
Element N°8
Element N°9
...
Element N°3Element N°7Element N°8Element N°14Element N°15Element N°20Element N°21Element N°23Element N°27Element N°29Element.
I can't see what's wrong. I'd appreciate someone points me the root of the issue.
Here is a jsfiddle that show the issue : http://jsfiddle.net/gsYHb/
[edit] If i reverse the order of creating the source and target elements, the problem occurs on the other list :
var source = document.createElement("ul");
var target = document.createElement("ul");
$(el).after(target);
$(el).after(source);
in this case, this is the target list that is messy.
As you will see in http://jsfiddle.net/stevebeauge/gsYHb/6/, I have been able to solve my issue.
It was due to the $(this + "option:not(:selected)") selector that was returning the whole page.
Replacing this code with $(this).children("option:not(:selected)") make the script works (+ some other minor fixes)

Adding classes to all form widgets in symfony

I am trying to find a better way to assign classes to form elements in symfony. Currently, I can't seem to get away from assigning each one manually. ie:
$this->widgetSchema['title']->setAttribute("class","fieldInput");
$this->widgetSchema['tag_line']->setAttribute("class","fieldInput");
$this->widgetSchema['description']->setAttribute("class","fieldInput");
// etc
Things I tried without success
1) looping through $this->widgetSchema, treating it as an array and setting attributes to each key
2) $this->widgetSchema->setAttribute() but this only applied the class to the label that was generated, not the form element
There must be a way to hit all the fields without specifically directing them?
Can anyone point me in the right direction?
There is a way:
Create a sfWidgetFormSchemaFormatter in lib/widget, which contains for instance, this (code from symfonians):
class sfWidgetFormSchemaFormatterDiv extends sfWidgetFormSchemaFormatter
{
protected
$rowFormat = "<div class=\"form-row%is_error%\">\n %label%\n %error%\n %hel<div class='myfieldclass'%field%</div>\n p%\n%hidden_fields%</div>\n",
$errorRowFormat = "%errors%\n",
$helpFormat = '<div class="form-help">%help%</div>',
$decoratorFormat = "\n %content%";
public function formatRow($label, $field, $errors = array(), $help = '', $hiddenFields = null)
{
return strtr(parent::formatRow($label, $field, $errors, $help, $hiddenFields), array(
'%is_error%' => (count($errors) > 0) ? ' field_error' : '',
//'%is_required%' => $field,
));
}
}
Then, in your form, do:
$oDecorator = new sfWidgetFormSchemaFormatterDiv($this->getWidgetSchema());
$this->getWidgetSchema()->addFormFormatter('div', $oDecorator);
$this->getWidgetSchema()->setFormFormatterName('div');
Then you can style elements with the selector .myfieldclass input or whatever you want, this is a great way to change the structure/look of your forms.
From within the form class:
foreach ($this->getWidgetSchema()->getFields() as $field)
{
$field->setAttribute('class', 'custom-class');
}
You could also call that code from the constructor of a custom sfWidgetFormSchemaFormatter class if you have many form classes that need it:
public function __construct(sfWidgetFormSchema $widgetSchema)
{
parent::__construct($widgetSchema);
foreach ($this->getWidgetSchema()->getFields() as $field)
{
$field->setAttribute('class', 'custom-class');
}
}
I am having the same issue.
Situation
We have come to think that the separation between CSS and symfony's widget generation is a big learning curve for the integrators and we think there must be a way to leave the integrators more independent from the developers.
Client always want to have custom styled forms... and yet, custom handmade !DRY code is done. What I mean is that, Graphic designers do not design forms with only input text and label, they find all creative ways and our reality is to make it work in the framework to look the same as they design.
That's why we thought of using a sfWidgetFormSchemaFormatter, but form controllers are all different, and we wanted to be able to inject styling FROM the views... because it's an integration matter, not application matter. per se.
Solution path
My team-lead and I came up to a solution. Mostly using partials and looping through them. A form view could look like what follows.
Our solution is not perfect because we'd like to say by the call in the view which controller could get which class names. But it's better than to use CSS's input[type=text] because our client still uses < IE8. But at least,
So we thought of Why not mapping field id's with a className that could have a different behavior (different from label+input (and of course +helper+error if needs be)).
What we do is that we inject control style for those special controllers (e.g.: label+select+select+select+helper+error) let's say this Widget has id some_field_id and we need it to the parent <li>.
We could do it like this:
$node_classes_map => array('some_field_id'=>'date-selector')
So... now's time for some code:
First, we need to use the 'list' widgetFormatter:
<?php
// lib/form/SomeForm.php
class SomeForm extends BaseSomeForm
{
public function configure()
{
$this->widgetSchema->setFormFormatterName('list');
}
}
Or, you could use qais answer to have ALL forms as lists (at last!! I didn't knew before now).
The view:
<?php
// apps/appName/modules/moduleName/templates/someviewSuccess.php
$node_classes_map = array(
'some_field_id'=>'date-selector'
// .date-selector would have many select side by side.
// (no CSS for that in this example)
);
include_partial('global/patterns/formParent',
array(
'form'=>$form,
'form_classes'=>'labels-on-left',
'node_classes_map' => $node_classes_map
)
);
formParent looks like:
<?php
// apps/appName/templates/_patterns/formParent.php
$form_classes_attr = (isset($form_classes))?' class="'.$form_classes.'"':'';
$form_list_node_partial = 'global/patterns/listFormDefaultNode';
$model_name_lowercase = strtolower($form->getModelName());
$node_classes_map = (isset($node_classes_map))?$node_classes_map:array();
?>
<?php use_stylesheets_for_form($form) ?>
<?php use_javascripts_for_form($form); ?>
<form action="<?php
echo url_for($model_name_lowercase.'/'.($form->getObject()->isNew() ? 'create' : 'update').(!$form->getObject()->isNew() ? '?id='.$form->getObject()->getId() : ''))
?>"<?php echo $form_classes_attr;
?> method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" '?>>
<h2><?php echo __(($form->getObject()->isNew()?'new.':'edit.').$model_name_lowercase.'.form.title'); ?></h2>
<fieldset>
<ol>
<?php
foreach($form as $item){
$node_classes = (isset($node_classes_map[$item->renderId()]))?$node_classes_map[$item->renderId()]:'';
include_partial($form_list_node_partial,
array(
'item'=>$item,
'node_classes' => $node_classes
)
);
}
?>
<ol>
</fieldset>
<?php echo $form->renderHiddenFields() ?>
<?php echo $form->renderGlobalErrors() ?>
<?php echo (!$form->getObject()->isNew())? '<input type="hidden" name="sf_method" value="put" />':''; ?>
</form>
Each form element gets rendered by either a custom one or listFormDefaultNode.
From there, you could come up with a different partial to render a controller differently from a mainstream label+input+helper+errors to something more complex like label+select+select+select+helper+erros.
This is my default form *Node
<?php
// apps/appName/templates/_patterns/listFormDefaultNode.php
/**
* Manual: http://www.symfony-project.org/forms/1_4/en/A-Widgets
*/
if(!$item->isHidden()){ ?>
<li class="<?php echo $item->renderId().((is_string($node_classes))?' '.$node_classes:''); ?>">
<?php echo $item->renderLabel(); ?>
<?php echo $item; ?>
<?php echo (!!$item->renderHelp())?'<span class="helpers">'.$item->renderHelp().'</span>':''; // I did not find any hasHelper(), so switching to bool ?>
<?php echo ($item->hasError())?'<span class="alertify error">'.$item->renderError().'</span>':''; ?>
</li>
<? }
Lastly, here is a start from my CSS:
Please note, here is a part of a project CSS and this uses part of concepts i'm developping with Snippies. Go there to get more ideas how I am structuring my CSS.
/* ****** Misc ****** */
/**
* General form views settings
**/
form ol, form ul {
list-style-type: none;
padding: 0;
margin: 0;
}
form input.text, form select {
display: block;
border: 0;
padding: 4px;
line-height: 12px;
font-size: 15px;
}
form select {
background-color: #e3e8eb;
color: #506775;
margin: 0;
}
form label {
color: #345569;
display: block;
font-weight: bold;
margin-bottom: 5px;
}
form input.text {
color: #345569;
border: none;
background-color: #e3e8eb;
color: #738996;
border: 0px solid #c3c987;
padding: 4px;
line-height: 12px;
width: 100%;
}
form fieldset {
padding: 0;
margin: 0;
border: none;
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px solid #98a7af;
padding: 10px 0;
}
form li {
margin-bottom: 10px;
}
form li .helpers {
color: #98a7af;
}
/**
* /General form views settings
**/
/**
* General error messages
**/
.alertify {
padding: 0.8em;
margin-bottom: 1em;
border: 2px solid #ddd;
}
.alertify strong, .alertify p {
padding: 0 0 10px 0;
display: block;
}
.alertify ul {
margin: 0;
padding: 0 0 0 15px;
}
.alertify ul li {
padding: 0 0 5px 0;
}
.error, .alert {
background: #fbe3e4;
color: #8a1f11;
border-color: #fbc2c4;
}
/**
* /General error messages
**/
/* ****** /Misc ****** */
/* ****** Specialization ****** */
/**
* Form labels on the left
*/
form.labels-on-left fieldset {
padding: 0;
margin: 0;
padding-bottom: 10px;
margin-bottom: 10px;
}
form.labels-on-left li {
clear: both;
overflow: hidden;
}
form.labels-on-left li input.text, form.labels-on-left li select {
border: 0;
padding: 4px;
line-height: 12px;
}
form.labels-on-left li input.text {
width: 815px !important;
float: left;
padding: 6px;
color: #506775;
}
form.labels-on-left li select {
background-color: #e3e8eb;
color: #506775;
height: 26px;
width: 400px;
margin: 0;
float: left;
}
form.labels-on-left li .beside {
float: left;
}
form.labels-on-left li .beside a {
font-weight: bold;
padding: 5px 20px;
font-size: 14px;
display: block;
}
form.labels-on-left li label {
color: #345569;
width: 80px;
font-size: 120%;
text-align: right;
font-weight: bold;
float: left;
display: block;
padding: 5px 10px 0 0;
}
form.labels-on-left li .error span, form.labels-on-left li .helpers span {
padding: 5px;
display: block;
}
form.labels-on-left li .helpers {
width: 100%;
}
/**
* /Form labels on the left
*/
/* ****** /Specialization ****** */
This is old but for anyone trying to find this just go into the xxxxxFormFilter.php inside /lib/filter/doctrine/ and do this:
$this->getWidget('inputname')->setAttribute('class_name','class_name/id_name');
This way you can individually specify and control everything.
Depending on what base form you extend, you can add the following in the configure method:
public function configure()
{
foreach ($this->widgetSchema->getFields() as $field) {
$field->setAttribute('class','my-custom-class');
}
parent::configure();
}
I did mine in BaseFormDoctrine. You can use the same approach to add/override any attribute in the field or even add custom rules based on field type.
Apply the class to a parent element, not the individual elements:
TEMPLATE:
<form class="my-form" action="" method="">
// put widgets here
</form>
CSS:
.my-form select {}
.my-form input[type=text] {}
.my-form textarea {}
.my-form input[type=submit] {}
etc...

Resources