I have implemented editable AG-Grid. In the grid, one of the column displays country of the player as shown below:
Now in last column, when user click on the cell, I want to display list of available countries as a dropdown.
Here by default AG-Grid displays normal dropdown. Which I want to replace with Bootstrap-select.
To achieve this, I have implemented custom selector and using Bootstrap-select library.
But when cell is clicked, Dropdown is not being rendered. I am not getting any error though in console.
Here is the code of my custom cell-editor:
var selectCellEdior = function () { };
selectCellEdior.prototype = {
init: function (params) {
selector = document.createElement('select');
for(var i = 0; i < params.values.length; i++) {
var option = params.values[i];
$('<option />', { value: option, text: option }).appendTo(selector);
}
this.cellSelector = selector;
$(this.cellSelector).selectpicker({ size: 'auto' });
},
getValue: function () {
return $(this.cellSelector).find('.btn-text').text();
},
getGui: function () {
return this.cellSelector;
},
destroy: function () {
$(this.cellSelector).remove();
}
};
Here is the Sample Code
I dont understand what is the issue.
The problem is the selectpicker jQuery method seems to add a display: none to the select element. Ag-Grid is adding it to the DOM you just can't see it. Additionally the getValue function is not returning the selected option's text.
The following changes to getValue and getGui will make the grid work:
...
getValue: function () {
return $(this.cellSelector).val();
},
getGui: function () {
this.cellSelector.style.display = 'block';
return this.cellSelector;
},
...
Here is the modified Plunker
For anyone looking at this now.... Ag-grid has a method afterGuiAttached that is called after the GUI has been attached to the Grid's cell. If you're noticing that the bootstrap-select still isn't "quite right" after following the instructions above, put ALL of the calls to selectpicker in afterGuiAttached instead of init or getGui. This will place the bootstrap-select into a div with the right classes it needs (dropdown, bootstrap-select, etc), and create all of the bootstrap-select elements that are properly shown/hidden.
Related
Goals
I am trying to add row selection functionality which is connected between the master grid and detail grid.
When a checkbox on a master grid row is selected, I want to select all the checkboxes in the associated detail grid.
When a checkbox on a master grid row is deselected, I want to deselect all the checkboxes in the associated detail grid.
When only partial checkboxes inside of a detail grid are selected, I want the master grid row to show a partial selection icon in the checkbox.
I want to keep count of how many items were selected in all of the detail grids
Questions/Problems
All of this functionality already exists for Groups, but I cannot get it to work for Master/Detail. I've tried adding in the line that I believe should add this for Groups cellRendererParams: {checkbox: true}, but that does nothing for the detail grid.
I've also tried implementing the functionality myself, but I hit a couple of roadblocks.
I don't know how to set the partial-checkbox icon manually or if that's even possible.
I implemented the onSelectionChanged event handler for the detail grid, but inside of the function it looks like I can't access the Angular component context to update the count of selected items. Not sure how to pass in the context this.
Code that I have working so far:
When a checkbox on a master grid is selected/deselected, the checkboxes in the associated detail grid are all selected/deselected.
public onMasterRowSelected() {
this.gridOptions.api.forEachNode(masterRowNode => {
if(masterRowNode.isSelected()) {
this.gridOptions.api.getDetailGridInfo(`detail_${masterRowNode.id}`).api.selectAll();
} else {
this.gridOptions.api.getDetailGridInfo(`detail_${masterRowNode.id}`).api.forEachNode(detailRowNode => detailRowNode.setSelected(false));
}
});
}
Added the onSelectionChanged() event listener for the detail grid.
constructor() {
this.gridOptions = {
...
detailCellRendererParams: {
getDetailRowData: function(params) {
params.successCallback(params.data.items); // Defines where to find the detail fields out of the rowData object
},
detailGridOptions: {
rowSelection: 'multiple',
defaultColDef: {
sortable: true
},
getRowNodeId: function (rowData) {
return rowData.id; // detail items are indexed by id field
},
onSelectionChanged: function (params) {
console.log(this); // I expected this to print out my Angular component object, but instead it prints out this.gridOptions.detailCellRendererParams.detailGridOptions
console.log("TEST"); // I know that this function is successfully being called because this line is printed
this.selectedItems = 0;
params.api.forEachNode(detailRowNode => {
if(detailRowNode.isSelected()) {
this.selectedObs++; // I believe that this would work if I had access to the correct "this" context
}
});
},
columnDefs: [
{headerName: 'Column 1', field: 'column1', checkboxSelection: true},
{headerName: 'Column 2', field: 'column2'},
]
}
}
};
}
Here is an Angular example where you can push the parent component as a reference to the detail grid and set it as its context. Doing this allows you to register a component method as a callback for the detail grid's onRowSelected method.
https://stackblitz.com/edit/ag-grid-detailrow-selectcallback?file=src/app/app.component.ts
Move the onSelectionChanged(params) function to the same level as the detailCellRendererParams object.
internalOnSelectionChanged(params)
{
this.detailGridApi = params.api;
this.detailGridColumnApi = params.columnApi;
console.log(this);
}
Then, on the onSelectionChanged, do this:
onSelectionChanged: this.internalOnSelectionChanged.bind(this);
This solved my issues that were very similar to yours.
I have had a select control where I could select from user names. Now I want to convert that select into combo box so I can give the user ability to enter text as well. The values populated in the combo box are in text property as {FirstName}, {LastName}. I do not want to use additional text as it shows at end of the row wih lot of space in between. My issue with Combo box is :
Values get populated but how do I filter? There is already some logic that is written on change method. I want to do custom filtering on the values. For example: If I write "P" it should show all the values that have P in the text (First name and last name). Where to write filter function? Also I found custom filtering code in demokit, I want to use it - but when I use it in inti method under delegate, I get error - this.get....setfilterfunction().. is not a function
View.xml
<ComboBox items= "{path:'items>/name'}" id="A"
selectedKey="{item>/Header/Name}" change="nameSelected">
<core:ListItem key="{order>NameID}" text="{order>LastName} ,{order>FirstName}"/>
</ComboBox>
Controller.js
_initializeData: function () {
var name = this.getView().byId("A");
name.addEventDelegate({
onAfterRendering: function() {
this.getView().byId("A").setFilterFunction(function(sTerm, oItem) {
return oItem.getText().match(new RegExp(sTerm, "i")) ||
oItem.getKey().match(new RegExp(sTerm, "i"));
});
}
},
nameSelected: function () {
......some logic processing..
}
You have to bind this in your function. I guess.
Can you access this.getView().byId("A") , when you put a break point in the onAfterRendering part ?
Try this solution: You are not working in the right scope.
_initializeData: function () {
var name = this.getView().byId("A");
name.addEventDelegate({
onAfterRendering: this.setFilter.bind(this) });
},
setFilter: function() {
this.getView().byId("A").setFilterFunction(function(sTerm, oItem) {
return oItem.getText().match(new RegExp(sTerm, "i")) ||
oItem.getKey().match(new RegExp(sTerm, "i"));
});
},
I'm trying to modify the TinyMCE 4 "link" plugin to allow users to select content from ListBox elements that are dynamically updated by AJAX requests.
I'm creating the ListBox elements in advance of editor.windowManager.open(), so they are initially rendered properly. I have an onselect handler that performs the AJAX request, and gets a response in JSON format.
What I need to do with the JSON response is to have it update another ListBox element, replacing the existing items with the new results.
I'm baffled, and the documentation is terribly unclear. I don't know if I should replace the entire control, or delete items and then add new ones. I don't know if I need to instantiate a new ListBox control, or render it to HTML, etc.
Basically, I have access to the original rendered ListBox (name: "module"} with
win.find('#module');
I have the new values from the AJAX request:
var data = tinymce.util.JSON.parse(text).data;
And I've tried creating a new Control configuration object, like
newCtrlconfig = {
type: 'listbox',
label: 'Class',
values: data
};
but I wouldn't know how to render it, much less have it replace the existing one.
I tried
var newList = tinymce.ui.Factory.create(newCtrlconfig);
and then
newList.renderHtml()
but even then, the rendered HTML did not contain any markup for the items. And examining these objects is just frustrating: there are "settings", "values", "_values", "items" all of which will happily store my values, but it isn't even clear which of them will work.
Since it's a ListBox and not a simple SELECT menu, I can't even easily use the DOM to manipulate the values.
Has anyone conquered the TinyMCE ListBox in 4.x?
I found this on the TinyMCE forum and I have confirmed that it works:
tinymce.PluginManager.add('myexample', function(editor, url) {
var self = this, button;
function getValues() {
return editor.settings.myKeyValueList;
}
// Add a button that opens a window
editor.addButton('myexample', {
type: 'listbox',
text: 'My Example',
values: getValues(),
onselect: function() {
//insert key
editor.insertContent(this.value());
//reset selected value
this.value(null);
},
onPostRender: function() {
//this is a hack to get button refrence.
//there may be a better way to do this
button = this;
},
});
self.refresh = function() {
//remove existing menu if it is already rendered
if(button.menu){
button.menu.remove();
button.menu = null;
}
button.settings.values = button.settings.menu = getValues();
};
});
Call following code block from ajax success method
//Set new values to myKeyValueList
tinyMCE.activeEditor.settings.myKeyValueList = [{text: 'newtext', value: 'newvalue'}];
//Call plugin method to reload the dropdown
tinyMCE.activeEditor.plugins.myexample.refresh();
The key here is that you need to do the following:
Get the 'button' reference by taking it from 'this' in the onPostRender method
Update the button.settings.values and button.settings.menu with the values you want
To update the existing list, call button.menu.remove() and button.menu = null
I tried the solution from TinyMCE forum, but I found it buggy. For example, when I tried to alter the first ListBox multiple times, only the first time took effect. Also first change to that box right after dialogue popped up didn't take any effect.
But to the solution:
Do not call button.menu.remove();
Also, the "hack" for getting button reference is quite unnecessary. Your job can be done simply using:
var button = win.find("#button")[0];
With these modification, my ListBoxes work just right.
Whole dialogue function:
function ShowDialog() {
var val;
win = editor.windowManager.open({
title: 'title',
body: {type: 'form',
items: [
{type: 'listbox',
name: 'categorybox',
text: 'pick one',
value: 0,
label: 'Section: ',
values: categories,
onselect: setValuebox(this.value())
},
{type: 'listbox',
name: 'valuebox',
text:'pick one',
value: '',
label: 'Page: ',
values: pagelist[0],
onselect: function(e) {
val = this.value();
}
}
]
},
onsubmit: function(e) {
//do whatever
}
});
var valbox = win.find("#valuebox")[0];
function setValuebox(i){
//feel free to call ajax
valbox.value(null);
valbox.menu = null;
valbox.settings.menu = pagelist[i];
// you can also set a value from pagelist[i]["values"][0]
}
}
categories and pagelist are JSONs generated from DB before TinyMCE load. pagelist[category] = data for ListBox for selected category. category=0 means all.
Hope I helped somebody, because I've been struggling this for hours.
It looks like the tinyMCE version that is included in wordpress 4.3 changed some things, and added a state object that caches the initial menu, so changing the menu is not enough anymore.
One will probably have to update the state object as well. Here is an example of updating the menu with data coming from an ajax request:
editor.addButton('shortcodes', {
icon: 'icon_shortcodes',
tooltip: 'Your tooltip',
type: 'menubutton',
onPostRender: function() {
var ctrl = this;
$.getJSON( ajaxurl , function( menu) {
// menu is the array containing your menu items
ctrl.state.data.menu = ctrl.settings.menu = menu;
});
}
});
As far as I can tell, these other approaches are broken in TinyMCE 4.9.
After spending most of the day tinkering to fix my own usage of these approaches, this is the working function I've found:
function updateListbox(win, data) { // win is a tinymce.ui.Window
listbox = win.find('#listbox'); // Substitute your listbox 'name'
formItem = listbox.parent();
listbox.remove();
formItem.append({
label: 'Dynamic Listbox',
type: 'listbox',
name: 'listbox',
values: data
});
}
So I have an editable line of text on my website. Whenever the text is changed and is above a certain length, I truncate the text.
Simplified jsfiddle here - http://jsfiddle.net/3kwCr/1/
On subsequent clicks on the text to edit, the truncated value with ellipsis is picked up. How do I get jEditable to pick up the actual value which is present as an attribute in the div?
data: function() { $('.editable-value').attr('value') }
will not work as I have several of these editable lines of text
I need something like
data: function() { this.attr('value') }
where this would the div object to which .editable has been applied to.
Just wrap this into jQuery object so you can use jQuery methods on it. Below is updated code. I also updated the example jsFiddle.
$('.editable').editable(function(value, settings) {
$(this).attr('value', value);
if (value.length > 10) {
return(value.slice(0,10)) + '...';
} else {
return(value);
}
}, {
data : function(value) { return($(this).attr('value')); },
type : 'text',
submit : 'OK'
});
did somebody manage to sort multiple items at once with jquery.ui.sortable?
we are working on a photo managing app.
select multiple items
drag them to a new location.
thanx
I had a similar requirement, but the solution in the accepted answer has a bug. It says something like "insertBefore of null", because it removes the nodes.
And also i tried jQuery multisortable, it stacks the selected items on top of each other when dragging, which is not what i want.
So I rolled out my own implementation and hope it will save others some time.
Fiddle Link.
Source code:
$( "#sortable" ).sortable({
// force the cursor position, or the offset might be wrong
cursorAt: {
left: 50,
top: 45
},
helper: function (event, item) {
// make sure at least one item is selected.
if (!item.hasClass("ui-state-active")) {
item.addClass("ui-state-active").siblings().removeClass("ui-state-active");
}
var $helper = $("<li><ul></ul></li>");
var $selected = item.parent().children(".ui-state-active");
var $cloned = $selected.clone();
$helper.find("ul").append($cloned);
// hide it, don't remove!
$selected.hide();
// save the selected items
item.data("multi-sortable", $cloned);
return $helper;
},
stop: function (event, ui) {
// add the cloned ones
var $cloned = ui.item.data("multi-sortable");
ui.item.removeData("multi-sortable");
// append it
ui.item.after($cloned);
// remove the hidden ones
ui.item.siblings(":hidden").remove();
// remove self, it's duplicated
ui.item.remove();
}
});
There's a jQuery UI plugin for that: https://github.com/shvetsgroup/jquery.multisortable
jsFiddle: http://jsfiddle.net/neochief/KWeMM/
$('ul.sortable').multisortable();
... or just define a 'items' option to your multisortable that way (for example) :
$('table tbody').multisortable({
items: 'tr'
});
you can use shvetsgroup/jquery.multisortable
but it will create problem.. because, that js is designed only for tags...
but customize it to use it, its very simple i'll tell you how????
at first download that .js and use it in your program...
step 1. open the js file...now edit the following lines...
$.fn.multiselectable.defaults = {
click: function(event, elem) {},
mousedown: function(event, elem) {},
selectedClass: 'selected',
items: 'li'
};
the above are lines from 107 to 112....
there you can see "items: 'li'
in that use your tag which you are used to enclose those image like if you are using, or or anything you are using like this
$.fn.multiselectable.defaults = {
click: function(event, elem) {},
mousedown: function(event, elem) {},
selectedClass: 'selected',
items: 'div' // or any tag you want...
};
and 249 to 254
selectedClass: 'selected',
placeholder: 'placeholder',
items: 'li'
};
}(jQuery);
change the line " item:'li' " with your tag like this
selectedClass: 'selected',
placeholder: 'placeholder',
items: 'div' // or anything else
};
}(jQuery);
if you are working on textboxes inside those envelopes.. you have to get rid of these lines too
// If no previous selection found, start selecting from first selected item.
prev = prev.length ? prev : $(parent.find('.' + options.selectedClass)[0]).addClass('multiselectable-previous');
var prevIndex = prev.index();
after that comment line...
add a line code that search textbox or check box or any interaction element inside it...
like this..
// If no previous selection found, start selecting from first selected item.
item.children("input").focus(); // customize this code to get your element focus...
prev = prev.length ? prev : $(parent.find('.' + options.selectedClass)[0]).addClass('multiselectable-previous');
var prevIndex = prev.index();
and also to indicate selected tags or elements... use styles like this
div { margin: 2px 0; cursor: pointer; }
div.selected { background-color: orange }
div.child { margin-left: 20px; }
actually i used div.. instead of that you can use any tag you wish...
hope will help u.... if it is not... read again.. and ask again....
wishes