`format` callback in newest version of jQuery UI selectmenu - javascript

I want to make <select> replacement with custom text formatting using jQuery UI Selectmenu.
Third and fifth selects in this fiddle are good examples of what I am trying to achieve:
http://jsfiddle.net/fnagel/GXtpC/
In the fiddle, there is defined function addressFormatting(), which takes the original option text and returns html output which will be used for rendering the selectmenu. This function is passed as a callback in the selectmenu initialization:
$('select').selectmenu({
format: addressFormatting
});
I am using jQuery UI Selectmenu 1.11.4. The problem is that the format callback option is not present in this version.
This is a portion of code from jQuery UI Selectmenu version 1.5.0pre, used in the provided fiddle:
$.widget("ui.selectmenu", {
options: {
appendTo: "body",
typeAhead: 1000,
style: 'dropdown',
positionOptions: null,
width: null,
menuWidth: null,
handleWidth: 26,
maxHeight: null,
icons: null,
format: null, // <<<<<<<<<<<< FORMAT OPTION IS PRESENT <<<<<<<<<<<<
escapeHtml: false,
bgImage: function() {}
},
And this is the portion of code from newer version that I am using:
var selectmenu = $.widget( "ui.selectmenu", {
version: "1.11.4",
defaultElement: "<select>",
options: {
appendTo: null,
disabled: null,
icons: {
button: "ui-icon-triangle-1-s"
},
position: {
my: "left top",
at: "left bottom",
collision: "none"
},
width: null,
// callbacks
change: null,
close: null,
focus: null,
open: null,
select: null
},
format option is not present here, and using it in the initialization has no effect.
In the API documentation, there is _renderItem() method, whose name suggest it could be used to add custom formatting to the select, but it has private scope, so I can't use it from outside of the widget. There is also public create() method, but I am not sure if I can or if I should use it to change the structure of created selectmenu.

As mentioned format option has been removed from the selectmenu before it was a part of the jquery-ui library. There is a way to inject custom code into the selectmenu widget and overwrite functions that handle this functionality.
// Create objects that you want inside the menu list
var RenderItem = function(item) {
return $('<div/>')
.addClass('ui-menu-item-wrap')
.append(
$('<span/>')
.addClass('ui-menu-item-header')
.text(item.label + ' (' + item.km + " km)")
).append(
$('<span/>')
.addClass('ui-menu-item-description')
.text(item.desc)
);
};
// Extend functions in the selectmenu plugin
$.widget("ui.selectmenu", $.ui.selectmenu, {
// Input middleware to the _setText function, that will build
// jQuery objects to render
_renderItem: function( ul, item ){
var li = $( "<li>" ),
wrapper = $( "<div>", {
title: item.element.attr( "title" )
} );
if ( item.disabled ) {
this._addClass( li, null, "ui-state-disabled" );
}
// Insert middleware
this._setText( wrapper, RenderItem(item));
return li.append( wrapper ).appendTo( ul );
},
// Overwrite this function to add custom attribute values from the option
_parseOption: function( option, index ) {
var optgroup = option.parent( "optgroup" );
return {
element: option,
index: index,
value: option.val(),
label: option.text(),
desc: option.attr('data-desc'), // Custom <option> value saved to item
km: option.attr('data-km'), // Custom <option> value saved to item
optgroup: optgroup.attr( "label" ) || "",
disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" )
};
},
// Overwrite this function to append a value, instead of inserting text
// So that the jQuery element is handled correctly.
_setText: function(element, value) {
if (value) {
element.append(value);
} else {
element.html(" ");
}
}
});

Related

Generating TinyMCE drop-down menu dynamically

I am trying to create toolbar button in TinyMCE with options that are derived from the array. I've followed the examples on Tiny's website and the button is getting generated as expected. Here is the code:
var mergeFields = {one: "first", two: "second", three: "third"};
tinymce.init({
selector: 'textarea',
menubar: false,
toolbar: 'mergefields',
setup: function (editor) {
editor.ui.registry.addMenuButton('mergefields', {
text: 'Merge Fields',
fetch: function (callback) {
var items = [];
for (var fieldName in mergeFields) {
var menuItem = {
type: 'menuitem',
text: mergeFields[fieldName],
onAction: function() {
// The problem: this function always inserts the last element of the array
// instead of the expected fieldName associated with this menuItem
editor.insertContent(fieldName);
},
};
items.push(menuItem);
}
callback(items);
},
});
}
});
<script src="https://cloud.tinymce.com/5/tinymce.min.js?apiKey=XXXXX"></script>
<textarea>Editor</textarea>
The problem happens when one of the options is selected and the anonymous function assigned to onAction property is executed -- it always inserts "three" into the document (presumably because after running through the whole array, fieldName is set to "three"). How can I make the onAction handler insert the right value into the document?
This needs to work in TinyMCE 5.
I've found a similar question here: Adding custom dropdown menu to tinyMCE and insert dynamic contents, but it was referring to TinyMCE 4 and unfortunately the provided answer does not work for TinyMCE 5.
Thanks for your help!
I had the same problem.
I solved it using value+onSetup
https://jsfiddle.net/stvakis/tjh7k20v/8/
var mergeFields = {
one: "first",
two: "second",
three: "third"
};
tinymce.init({
selector: 'textarea',
menubar: false,
toolbar: 'mergefields',
setup: function(editor) {
editor.ui.registry.addMenuButton('mergefields', {
text: 'Merge Fields',
fetch: function(callback) {
var items = [];
for (var fieldName in mergeFields) {
var menuItem = {
type: 'menuitem',
text: mergeFields[fieldName],
value:fieldName,
onSetup: function(buttonApi) {
var $this = this;
this.onAction = function() {
editor.insertContent($this.data.value);
};
},
};
items.push(menuItem);
}
callback(items);
},
});
}
});

jquery autocomplete - selected value disappears from textbox

I have written a custom jquery autocomplete function to display certain values and textfields to update on selecting the value as per the code below:
<input type="text" name="promoitem" id="promoitem">
$('#promoitem').autocomplete({
source: "BckProcesses/GetPromoItems.asp",
create: function() {
$(this).data('ui-autocomplete')._renderItem = function(ul, item) {
return $('<li>')
.append('<a>' + item.promodesc + '</a>')
.appendTo(ul);
}
},
select: function(event, ui) {
$('#promoitem').val(ui.item.promodesc);
$('#promocost').val(ui.item.promocost);
$('#promoqty').val(ui.item.qty);
$('#hidden_promo_item_id').val(ui.item.id);
}
});
This is what is return by the source file (GetPromoItems.asp)
[{"id": "1", "promodesc": "Ipad 4 ", "promocost": "200", "qty": "1"},{"id": "2", "promodesc": "Village Tickets", "promocost": "20", "qty": "2"}]
However, when I select the value from the ul, everything gets populated except the promoitem textfield. That fields goes to being blank.
Can anyone please let me know what could be causing this?
Thanks
Sam
Since you're providing your own logic in the select handler, you need to prevent the default action, which is to place ui.item.value in the input.
Right now, your code is running, and then jQueryUI is immediately trying to place ui.item.value in the input, which explains the empty value.
So really all you need to do is call event.preventDefault(); or return false; from the select handler:
select: function(event, ui) {
$('#promoitem').val(ui.item.promodesc);
$('#promocost').val(ui.item.promocost);
$('#promoqty').val(ui.item.qty);
$('#hidden_promo_item_id').val(ui.item.id);
event.preventDefault(); // <---
}
After spending an hour, finally got to an point that Jquery UI autocomplete sets the value to default.
Only one line needs to put and prevent Jquery default function to get the wok done.
// pincode list autocomplete
$('input[name=\'pincode\']').autocomplete({
'source': function (request, response) {
$.ajax({
url: 'index.php?route=seller/pincode/pincodeAutocomplete&filter_name=' + encodeURIComponent($('input[name=\'pincode\']').val()),
dataType: 'json',
success: function (json) {
json.unshift({
pincode_id: '',
pincode: '-- None --'
});
response($.map(json, function (item) {
return {
label: item['pincode'],
value: item['pincode_id']
}
}));
}
});
},
'select': function (event, ui) {
event.preventDefault();
$('input[name=\'pincode\']').val(ui.item.label);
$('input[name=\'pincode_id\']').val(ui.item.value);
}
});

How do I add styles using CKEditor Custom drop down plugin

So I added this plugin which gives me the code to have a dropdown menu on my CKeditor toolbar, which holds all my styles which apply themselves on click. See code:
CKEDITOR.plugins.add( 'tokens',
{
requires : ['richcombo'], //, 'styles' ],
init : function( editor )
{
var config = editor.config,
lang = editor.lang.format;
// Gets the list of tags from the settings.
var tags = []; //new Array();
//this.add('value', 'drop_text', 'drop_label');
tags[0]=["[contact_name]", "Name", "Name"];
tags[1]=["[contact_email]", "email", "email"];
tags[2]=["[contact_user_name]", "User name", "User name"];
// Create style objects for all defined styles.
editor.ui.addRichCombo( 'tokens',
{
label : "Insert tokens",
title :"Insert tokens",
voiceLabel : "Insert tokens",
className : 'cke_format',
multiSelect : false,
panel :
{
css : [ config.contentsCss, CKEDITOR.getUrl( editor.skinPath + 'editor.css' ) ],
voiceLabel : lang.panelVoiceLabel
},
init : function()
{
this.startGroup( "Tokens" );
//this.add('value', 'drop_text', 'drop_label');
for (var this_tag in tags){
this.add(tags[this_tag][0], tags[this_tag][1], tags[this_tag][2]);
}
},
onClick : function( value )
{
editor.focus();
editor.fire( 'saveSnapshot' );
editor.insertHtml(value);
editor.fire( 'saveSnapshot' );
}
});
}
});
So what this code does is just insert whatever is in the tags ["[contact_name"] so when you click on "Name" in the dropdown, it just drops [contact_name] in the text editor. I want to know how I make each tag a specific function which adds css to whatever is selected in the text editor. For example have a function called 'Red Font' and whatever < p > font exists it turns red.
CKEDITOR.replace( 'editorId', {
extraPlugins: 'tokens'
});
add this code and replace the editor id with your editor id.
that's it.
CKEDITOR.replace( 'editor', {
extraPlugins: 'tokens' // why tokens see below
});
because you have given
CKEDITOR.plugins.add( 'tokens',
{
requires : ['richcombo']
..

How to manually set the value (and trigger the select event) to jQuery autocomplete

I know there are a few questions about this, but I couldn't find anyone that sets the value and also triggers the select function.
My code is:
$("#ux-selfservice-account-edit-nationality").autocomplete({
source: countryList,
minLength: 1,
autoFocus: true,
select: function(event, ui) {
$(this).val(ui.item.label).attr("oid", ui.item.oid);
var select = $(this).closest(".ux-selfservice-account-box-edit").find(".ux-selfservice-account-edit-dni-type");
// Check if the user has selected a different country (against the site)
if (ui.item.iataCode == options.countryCode) {
$(select).find("option").show();
}
else {
$(select).find("option:not([value='PAS']):not([value=''])").hide();
if ($(select).val() != "PAS") $(select).val('');
}
return false;
},
focus: function(event, ui) {
return false;
},
search: function(event, ui) {
$(this).attr("oid", "0");
}
});
The countries list is something like this:
[
{
iataCode: "AR",
label: "Argentina",
value: "Argentina",
oid: 28515
},
....
]
As you can see I have a very small check in the select function, if the user selects a different country I hide or show some options from another select drop down.
Now my problem is that sometimes I want the country to be set by javascript, so that the user sees the country name in the field, the oid is set to the attr and also checks the country.
Now I am doing something like this..
$("#ux-selfservice-account-edit-nationality").val(getCountryName(profile.personalInfo.country)).attr("oid", profile.personalInfo.country);
But of course this is wrong and doesn't check the other validation. also I can't do the validation here because I don't have the countryCode (iataCode). I know I can find it in the list, but the point is to use the same function of the autocomplete..
Why don't you extract the logic in the select event handler to a separated function that you can call elsewhere from your Javascript code?
All you need in that function is the country input (which is this in your current code, and the selected country json, which is the ui.item);
So you could extract your current logic into a new function countrySelected:
var countrySelected = function (countryInput, countryJSON) {
$(countryInput).val(countryJSON.label).attr("oid", countryJSON.oid);
var select = $(countryInput).closest(".ux-selfservice-account-box-edit").find(".ux-selfservice-account-edit-dni-type");
// Check if the user has selected a different country (against the site)
if (countryJSON.iataCode == options.countryCode) {
$(select).find("option").show();
} else {
$(select).find("option:not([value='PAS']):not([value=''])").hide();
if ($(select).val() != "PAS") $(select).val('');
}
}
Then update your autocomplete declaration so the select event handler uses this function:
$("#country").autocomplete({
minLength: 1,
source: countries,
autoFocus: true,
select: function (event, ui) {
//just call the new function
countrySelected(this, ui.item);
return false;
},
focus: function (event, ui) {
return false;
},
search: function (event, ui) {
$(this).attr("oid", "0");
}
});
This way you can also manually call the countrySelected function:
var countries = [{
iataCode: "AR",
label: "Argentina",
value: "Argentina",
oid: 28515
},
...
];
countrySelected($("#country"), countries[0]);
I have created this fiddle where you can see it in action.

Add an "All" item to kendo ui listview populated by a remote datasource

I am building a website using MVC 4, Web API, and Kendo UI controls.
On my page I am using a Kendo UI Listview to filter my grid. I'm trying to add an "ALL" option as the first item in my listview.
Here is the listview:
var jobsfilter = $("#jobfilter").kendoListView({
selectable: "single",
loadOnDemand: false,
template: "<div class='pointercursor' id=${FilterId}>${FilterName}</div>",
dataSource: filterDataSource,
change: function (e) {
var itm = this.select().index(), dataItem = this.dataSource.view()[itm];
if (dataItem.FilterId !== 0) {
var $filter = new Array();
$filter.push({ field: "JobStatusId", operator: "eq", value: dataItem.FilterId });
jgrid.dataSource.filter($filter);
} else {
jgrid.dataSource.filter({});
}
}
});
Here is my datasource:
var filterDataSource = new kendo.data.DataSource({
transport: {
read: {
url: "api/Filter"
}
},
schema: {
model: { id: "FilterId" }
}
});
I have tried a few different methods to make this happen:
I can make it work if I attach it to a button - but I need it there
when the data loads.
If I add it to the dataBound event of the listview, it causes the
databound event to go into a loop and adds the item a bunch (IE) or kills the browser (firefox). Adding preventDefault did nothing.
I've read up on adding a function to the Read paramter of the
datasource, but I think that is simply not the correct place to do
it.
Based on what I've read, I think that I should be able to do it in the dataBound event of the listview and that my implementation is incorrect. Here is the listview with dataBound event added that crashes my browser (Firefox) - or adds about 50 "All" items to the listview (IE).
var jobsfilter = $("#jobfilter").kendoListView({
selectable: "single",
loadOnDemand: false,
template: "<div class='pointercursor' id=${FilterId}>${FilterName}</div>",
dataSource: {
transport: {
read: {
url: "api/Filter"
}
}
},
dataBound: function (e) {
var dsource = $("#jobfilter").data("kendoListView").dataSource;
dsource.insert(0, { FilterId: 0, FilterName: "All" });
e.preventDefault();
},
change: function (e) {
var itm = this.select().index(), dataItem = this.dataSource.view()[itm];
if (dataItem.FilterId !== 0) {
var $filter = new Array();
$filter.push({ field: "JobStatusId", operator: "eq", value: dataItem.FilterId });
jgrid.dataSource.filter($filter);
} else {
jgrid.dataSource.filter({});
}
}
});
Any help would be greatly appreciated.
Why don't you add it server-side?
Anyway, if you want to do it in dataBound, just check whether it exists and only add if it doesn't:
dataBound: function (e) {
var dsource = this.dataSource;
if (dsource.at(0).FilterName !== "All") {
dsource.insert(0, {
FilterId: 0,
FilterName: "All"
});
}
},
As an explanation to the problem you're seeing: you're creating an infinite loop since inserting an element in the data source will trigger the change event and the list view will refresh and bind again (and thus trigger dataBound).
You could also encapsulate this in a custom widget:
(function ($, kendo) {
var ui = kendo.ui,
ListView = ui.ListView;
var CustomListView = ListView.extend({
init: function (element, options) {
// base call to widget initialization
ListView.fn.init.call(this, element, options);
this.dataSource.insert(0, {
FilterId: 0,
FilterName: "All"
});
},
options: {
name: "CustomListView"
}
});
ui.plugin(CustomListView);
})(window.jQuery, window.kendo);

Categories

Resources