Adding classes to all form widgets in symfony - symfony1

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...

Related

How to create a Matrix display from a list in a model

I have a model that called LinksListWidgetModel that contains an IEnumerable property named Links that contains a list of my website links.
In my Razor view, I have two columns (i.e. one for the grid "col-md-8" and one for another tool "col-md-4". I have a foreach loop that adds each link to the grid tool. I am trying to achieve the following look: Matrix View, however the buttons don't wrap after three, it just continues throughout the width of the page. I am using Bootstrap4.x and flexbox.
I tried following the example in this question: How I can make nice looking matrix of buttons with bootstrap 3?, but that didn't fix the issue either.
Here is my code:
LinksListGrid.cshtml:
#if (Model == null)
{
#Html.Partial("_WidgetNotConfigured") }
else
{
<div class="btn-group btn-matrix" role="group">
#foreach (var link in Model.Links)
{
<div class="linkContainer-gridItem">
<button>
<a class="linkContainer-gridItem-link btn btn-default" href="#link.Url">
#if (!string.IsNullOrEmpty(link.Icon))
{
<div>
<i class="icon #link.Icon"></i>
</div>
}
#link.Label
</a>
</button>
</div>
}
</div>
}
LinkContainer.scss:
.linkContainer {
padding: 1rem;
margin-bottom: 2rem;
position: relative;
background: #fff;
border-radius: 8px;
border: 1px solid #dddfe2;
.linkContainer-title {
color: #fff;
background: $colorBrandDarkBlue;
padding: 1rem;
border-radius: 5px;
margin: -.5rem -.5rem 1rem -.5rem;
font-weight: 400;
}
.linkContainer-list {
list-style: none;
padding: 0;
margin-bottom: 0;
font-family: "Montserrat",sans-serif;
.linkContainer-item {
position: relative;
.linkContainer-link {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
padding: .75rem .5rem;
color: $colorBrandBlue;
font-weight: 400;
font-size: 18px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-decoration: none;
background-color: transparent;
font-family: "Montserrat",sans-serif;
}
.icon:before {
font-size: 30px;
margin-right: 1rem;
}
}
.linkContainer-item:after {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
border-top: 1px dotted;
opacity: .25;
}
}
.linkContainer-grid {
width: 290px;
display: block;
margin: 0 auto;
padding: 0;
padding-bottom: 19px;
font-family: "Montserrat",sans-serif;
text-align: center;
.linkContainer-gridItem {
background-color: $colorBrandBlue;
border-radius: 25px;
width: 120px;
height: 120px;
margin: 10px;
display: inline-block;
vertical-align: top;
.linkContainer-gridItem-link {
color: white;
text-decoration: none;
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
padding: 20px 10px;
}
.icon {
font-size: 30px;
margin-bottom: 0.25rem;
}
}
}
#media screen and (max-width: 767px) {
&.linkContainer-gridLayout {
padding: 0;
}
}
}
.btn-matrix {
> .btn {
&:nth-child(3n+4) {
clear: left;
margin-left: 0;
}
&:nth-child(n+4) {
margin-top: -1px;
}
&:first-child {
border-bottom-left-radius: 0;
}
&:nth-child(3) {
border-top-right-radius: 4px !important;
}
&:nth-last-child(3) {
border-bottom-left-radius: 4px !important;
}
&:last-child {
border-top-right-radius: 0;
}
}
}
It ends up looking like this: Grid View
How can I change this to make it look like the first screenshot's matrix/grid display? After every 3 buttons it goes on the second line.
I don't think clear:left; will work on flexbox. But there are other ways to do it.
HTML & SCSS way
I think you would be better off just styling .btn-matrix yourself, without mixing .btn-group styles, because Bootstrap .btn-group brings in lots of noice on its .btn children, such as border-radius, border, etc. You would need to style them using your own styles anyway so why bother.
I think the following structure should be generic enough you can use to construct a matrix:
<div class="btn-matrix btn-matrix-white" role="group">
<a class="btn" href="#">
<div class="fa-stack fa-2x">
<i class="fas fa-circle fa-stack-2x" />
<i class="fas fa-chart-bar fa-stack-1x" />
</div>
Reports
</a>
...
</div>
First of all, in your original HTML structure, you had a button that wraps an anchor link. That's not necessary because Bootstrap has classes to style an anchor link to just look like a button.
Secondly, I am using icon stacking method to construct icons with circle backgrounds. You can read about it here: https://fontawesome.com/how-to-use/on-the-web/styling/stacking-icons
Finally, here is the styles:
$numOfMatrixItemPerRow: 3;
$matrixItemBorderWidth: 1px;
.btn-matrix {
display: flex;
flex-flow: row wrap;
box-shadow: 0 0 1rem #ccc;
border: 1px solid transparent;
border-radius: .25rem;
> .btn {
display: flex;
flex-flow: column nowrap;
align-items: center;
justify-content: flex-start;
flex-grow: 0;
flex-shrink: 0;
padding: 5%;
width: calc(100% / #{$numOfMatrixItemPerRow});
border-radius: 0;
}
&.btn-matrix-white {
> .btn {
background-color: #fff;
border-right: $matrixItemBorderWidth solid #ddd;
border-bottom: $matrixItemBorderWidth solid #ddd;
.fa-stack-2x {
color: var(--primary);
}
.fa-stack-1x {
color: #fff;
}
&:nth-child(#{$numOfMatrixItemPerRow}n) {
border-right: none !important;
}
&:last-child {
border-bottom: none !important;
}
}
}
}
Really nothing tricky there except you would need to use SASS Interpolation to involve $numOfMatrixItemPerRow variable into calculations:
Display .btn-matrix as a wrappable flex row
Set the width of each button to be 100% / $numOfMatrixItemPerRow so that each row will contain the exact number of items before breaking into new rows
Display .btn as flexbox column so that you can easily align the icons and text there
Set the right and bottom border of each button, with exceptions of the last one on each row and last one
demo: https://jsfiddle.net/davidliang2008/1wqost69/201/
ASP.NET MVC way
Now since you're using ASP.NET MVC, I think there is another approach you can consider. That is, in the loop where you loop though each link from the model, you can define a variable there and do something like:
<div class="btn-matrix">
#for (int i = 0; i < Model.Links.Count(); i++)
{
var link = Model.Links[i];
if (i % numOfItemsPerRow == 0)
{
<div class="row">
}
<div class="col-md-4">
<a class="btn" href="#link.Url">
#if (!string.IsNullOrEmpty(link.Icon))
{
<div>
<i class="icon #link.Icon"></i>
</div>
}
#link.Label
</a>
</div>
if ((i+1) % numOfItemsPerRow == 0 || i + 1 == Model.Links.Count())
{
</div>
}
}
</div>
I'm just making things up but hopefully you see where I am going.

Telerik Kendo Grid custom command icon

I use a kendo grid in my application with custom command for each line like this:
.Columns(
columns =>
{
//fields 1
//fields 2
columns.Command(command =>
{
command.Custom("View").Click("view");
command.Custom("Edit").Click("edit");
command.Custom("Delete").Click("delete");
}).Width(300);
}
)
It work but now I need to put icon (awesome font ?) instead of string.
Thank you for help
You can customize the look and like of the buttons with CSS. In the following example the button is customized with a background-image.
.k-grid-content .k-button {
width: 20px;
height: 20px;
border: none;
min-width: 0 !important;
border-radius: 0;
color: transparent;
text-indent: -10000%;
box-shadow: none !important;
}
.k-grid-content .k-button span {
display: none;
}
.k-grid-content .k-button.k-grid-Edit {
background: url(/img/icons/icon-edit.png) no-repeat;
}
I created a dojo for you. Hope it helps.
http://dojo.telerik.com/ETIYa/2

Loading/Please-Wait Pop-Up in MVC View

I have developed a web application using MVC architecture. My controller takes some time to process input, send it to the server and return it back to the view. I want a Loading/Please-wait kind of pop-up to be shown and exited automatically when the ActionResult returns the view. The corresponding part in my controller looks like this:
[HttpPost][STAThread]
public ActionResult Index(DropDownModel model)
{
BillingToolInterface_1.Program p = new BillingToolInterface_1.Program(state, billtype, recurring, budget, paytype, IA, spanish, veteran, status, LDC, rate, adjustments, billtemplate, readtype, server1, server2, choice, paramcheck);
//above step takes like 15 seconds to put the server result in
//DataJoin.Connector.data. I want a dialog to be displayed during this time.
model.Message = DataJoin.Connector.data;
return View(model);
}
You can use something like this to submit your form:
#using (Ajax.BeginForm("Index", "Controller", null, new AjaxOptions {HttpMethod = "POST", LoadingElementId = "please-wait"}, new {-- some html if needed --}))
That will show the please wait message until the callback finishes. The please-wait could be some markup like:
<div id="please-wait" style="display: none;">
<div class="modal"></div>
<div class="spinner">Loading...</div>
</div>
The css for that markup could be (not the best css but works):
#please-wait
{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
#please-wait .modal
{
z-index: 1999;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0.5;
-moz-opacity: 0.5;
background-color: black;
margin-left: 0;
}
#please-wait .spinner
{
z-index: 2000;
padding-top: 20px;
padding-left: 20px;
background: #E5E5E5 url("loading.gif") no-repeat 15px center;
width: 120px;
height: 40px;
border: 2px solid #666;
font-weight: bold;
text-align: center;
position: relative;
margin-left: auto;
margin-right: auto;
top: 35%;
display: block;
}

MVC Bootstrap: Autocomplete doesn't show properly

I have a MVC website and it had a searchbox with autocomplete, now I changed the layout using bootstrap. But now the autocomplete isn't been shown correctly anymore. See the picture the suggestions are not shown right.
the autocomplete goes through the text. I was fine before I used bootstrap.
I am using a SQL server to get the data and this is js file (I'm not good at ajax, i took it from a tutorial I followed)
$(function () {
var ajaxFormSubmit = function () {
var $form = $(this);
var options = {
url: $form.attr("action"),
type: $form.attr("method"),
data: $form.serialize()
};
$.ajax(options).done(function (data) {
var $target = $($form.attr("data-aptitude-target"));
var $newHtml = $(data);
$target.replaceWith($newHtml);
$newHtml.show("slide", 200);
});
return false;
};
var submitAutocompleteForm = function (event, ui) {
var $input = $(this);
$input.val(ui.item.label);
var $form = $input.parents("form:first");
$form.submit();
};
var createAutocomplete = function () {
var $input = $(this);
var options = {
source: $input.attr("data-aptitude-autocomplete"),
select: submitAutocompleteForm
};
$input.autocomplete(options);
};
$("form[data-aptituder-ajax='true']").submit(ajaxFormSubmit);
$("input[data-aptitude-autocomplete]").each(createAutocomplete);
});
this is the form in my view
<form method="get" action="#Url.Action("Index")"
data-aptitude-ajax="true" data-aptitude-target="#testList">
<input type="search" name="searchTerm" data-aptitude-autocomplete="#Url.Action("Autocomplete")" />
<input type="submit" value=Search />
And this is part of the controller of the view
public ActionResult Autocomplete(string term)
{
var model = db.tests
.Where(r => term == null || r.name.Contains(term))
.Select(r => new
{
label = r.name
});
return Json(model, JsonRequestBehavior.AllowGet);
}
//
// GET: /Test/
public ActionResult Index(string searchTerm = null)
{
var model = db.tests
.Where(r => searchTerm == null || r.name.StartsWith(searchTerm));
if (Request.IsAjaxRequest())
{
return PartialView("_Test", model);
}
return View(model);
}
I'm new to ajax as well as bootstrap 3.
I got the searchfunction and autocomplete from a tutorial I followed.
Anybody any idea on how to fix this, because it worked fine?
Thanks in advance!
I realise that this question in relatively old, however I'm not sure if you've found a solution and haven't got round to posting it??
Anyway - I have just run into this problem (using MVC 5, BootStrap 3 and typeahead.js 0.10.2).
I found this site, which kind of half solves "my/our" problem Typeahead CSS.
I've just ripped the following styles from the site and used them in my partial view (where the search box sits) and the box is now working and looks like all the demos on the typeahead.js example site
.tt-dropdown-menu {
min-width: 160px;
margin-top: 2px;
padding: 5px 0;
background-color: #fff;
border: 1px solid #ccc;
border: 1px solid rgba(0,0,0,.2);
*border-right-width: 2px;
*border-bottom-width: 2px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
box-shadow: 0 5px 10px rgba(0,0,0,.2);
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}
.tt-suggestion {
display: block;
padding: 3px 20px;
}
.tt-suggestion.tt-is-under-cursor {
color: #fff;
background-color: #0081c2;
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
}
.tt-suggestion.tt-is-under-cursor a {
color: #fff;
}
.tt-suggestion p {
margin: 0;
}
the only problem that remains for me now is that the suggestion text (from typeahead) and the actual text in the input is not aligned and looks like you're viewing it after too many beers!

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)

Resources