Adding multiple buttons in TinyMce in a loop doesn't work - javascript

I have a config list for buttons like this:
var config = [{
name: 'first',
insertionConfig: {
title: 'first button',
onsubmit: function(){
// do sth
}
}
},{
name: 'second',
insertionConfig: {
title: 'second button',
onsubmit: function(){
// do sth
}
}
}
]
and in my TinyMce plugin I want to add all buttons according to their config. So it would end up like this:
tinymce.PluginManager.add('myPlugin', function(editor, url) {
for (var i in config) {
item = config[i];
editor.addButton(item.name, {
text: item.name,
onclick: function() {
editor.windowManager.open({
title: item.insertionConfig.title,
onsubmit: item.insertionConfig.onsubmit
}
};
}
});
but when I click on first button, it shows the second button's title. all configs of buttons refer to last added button. I know problem is something about the 'item' in the loop (all buttons refer to same item object which is the last one) but I don't know how to fix it.

Try creating a locally scoped variable for item inside the onclick function:
The issue you are running into is how variables are managed in JavaScript at the time the function is actually run. The click function is not actually run until you click an item and at that time item is pointing to the last item in the array.
EDIT: Check out this TinyMCE Fiddle for how this may happen: http://fiddle.tinymce.com/REfaab/1

Related

angular-slickgrid, trigger the cell edit on select editor change event

I am using angular-silkgrid with angular 7. I am using inline editing feature. I am using single select editor for inline edit. Currently I want to achieve this functionality:
As soon as user click on the editable field , the select drop down will be visible.On select any option from select dropdown the inline mode should exist and column value should be updated.
currently I need to click on other field to exit from inline mode(I want to achieve this on select option select)
editor: {
// display checkmark icon when True
enableRenderHtml: true,
// tslint:disable-next-line:max-line-length
collection: [{
value: 1,
label: 'Sucessful'
}, {
value: 2,
label: 'Unsucessful'
}],
model: Editors.singleSelect, // CustomSelectEditor
elementOptions: {
autoAdjustDropPosition: false,
onClick: (event, rr) => {
// here i want to write some code which can trigger to grid to start update
}
}
}
Thanks All for the answers. I have solved my issue as below:
editor: {
enableRenderHtml: true,
collection: [{ value: CCLStaus.Sucessfull, label: 'Sucessful' }, { value: CCLStaus.UnSucessfull, label: 'Unsucessful' }],
model: Editors.singleSelect,// CustomSelectEditor
elementOptions: {
onClick: (event) => {
const updateItem = this.angularSilkGrid.gridService.getDataItemByRowIndex(this.rowInEditMode);
updateItem.status = +event.value;
this.angularSilkGrid.gridService.updateItem(updateItem, { highlightRow: false });
this.angularSilkGrid.gridService.renderGrid();
}
}
}
Generally,
grid.getEditorLock().commitCurrentEdit()
will commit and close the editor.
Also, any of
grid.navigateRight()
grid.navigateLeft()
grid.navigateDown()
grid.navigateUp()
grid.navigateNext()
grid.navigatePrev()
will commit and exit gracefully. In the select2 editor, you'll notice:
this.init = function () {
...
// Set focus back to select2 element on closing.
$input.on('select2:close', function (e) {
if ((e.params.originalSelect2Event && e.params.originalSelect2Event.data)
|| e.params.key === 9) {
// item was selected with ENTER or no selection with TAB
args.grid.navigateNext();
} else {
// closed with no selection
setTimeout(function () {
$input.select2('focus');
}, 100);
}
});
};
this.destroy = function () {
$input.select2('destroy');
$input.remove();
};
, noting that args.grid.navigateNext() will commit and close the editor, including calling the destroy() method at the appropriate time.
From the Angular-Slickgrid Editor Example there's a checkbox in the example to auto commit and that is a setting to you need to enable in your Grid Options
this.gridOptions = {
autoEdit: true,
autoCommitEdit: true,
}
The lib will internally call grid.getEditorLock().commitCurrentEdit() like Ben wrote in his answer, in Angular-Slickgrid you can just set the autoCommitEdit flag that I added.

How can I add a dynamic context menu to tinyMCE?

Is there a way to add custom dynamic elements to the context menu in tinyMCE 4.x, after init? I created custom menu items but many of them have sub-items that are dependent on other things going on in my application.
I tried using editor.on('contextmenu') but the menu still does not update. Any ideas?
Add the contextmenu plugin
Override the default context menu (some plugins automatically add their own entries) by defining the contextmenu option. It is a pipe-delimited list of custom menu items (which you define in step 3)
Define a list of custom menu items. These can have their own onclick event handlers, or define sub-menus.
tinymce.init({
...
plugins: [..., 'contextmenu'],
contextmenu: 'customItem1 | customItem2',
setup: function (editor) {
editor.addMenuItem('customItem1', {
text: 'Menu Item 1',
context: 'tools',
onclick: function () {
alert('Menu item 1 clicked');
}
});
editor.addMenuItem('customItem2', {
text: 'Menu Item 2',
context: 'tools',
menu: [ {
text: "Sub-menu item 1",
onclick: function () {
alert('Sub-menu item 1');
}
}, {
text: "Sub-menu item 2",
onclick: function () {
alert('Sub-menu item 2');
}
}]
});
}
});
References:
TinyMCE addMenuItem
TinyMCE contextmenu plugin doc
Custom menu item blog post
Similar SO question
Yes, it is possible.
The JavaScript Object Function can be used to declare a value dynamically inside editor events.
Even you can go for loops, but only one menu is supported in Dynamic (Since Context Menu Value is unique) make dummy context menu and declare separately (Apply your own logic).
On Sub-menu: to create a Dynamic Menu, use an Array and push it via JavaScript Object Methods in loops to display dynamically.
For Reference : Dynamic data added in custom TinyMCE Editor using AngularJs
This is how I did it
I used jQuery $.each to iterate through my objects, you could also use vanilla JavaScript
//register plugin to process context menu on a specific tag
tinymce.PluginManager.add('contextmenu-plugin', function (editor) {
var selectedCode
// Create a function which returns an array of items, these can be Submenus or Simple Items
var contextMenuItems = () => {
return [
{
type: 'submenu',
text: "Submenu 1",
getSubmenuItems: () => {
if (selectedCode){
var contextMenuItems = []
$.each( ArrayWithData, (index, data ) => {
contextMenuItems.push({
type: 'item',
text: `${data}`,
onAction: () => {
console.log("Clicked submenu option");
}
})
})
// return array of contextmenuitems -> this goes to the Submenu
return contextMenuItems
}
}
},
{
icon: 'remove',
text: 'Remove data',
onAction: () => {
console.log(`Removed data`)
}
}
}
]
}
// now register the contextmenu
editor.ui.registry.addContextMenu('contextmenu', {
update: function (element) {
//this way you can call contextMenuItems() every time you show the context menu
return (element.tagName == "your-condition" && element.className.includes("another condition") ) ? contextMenuItems() : ""
}
});
});

Adding to x-editable select source with button click

I have using BootstrapTable with X-editable. I have a select box, that I would like to update the source data with a button click. Ideally, I like to get the source from the column, push a value to it and reload it without changing any edits to that column by the user.
Full code:
http://jsfiddle.net/rp4nkb46/1/
relevant code:
$('#addoption').click(function () {
names.push({value: 5, text: 'Bob'})
$('#table').bootstrapTable('OnRefresh', {});
});
Use a function to return the names array rather than specifying the array directly in your table setup:
$(function () {
$('#table').bootstrapTable({
columns: [{field: 'Contact',
title: 'Contact',
editable: {
type: 'select',
source: function() { return names; }
}
}],
data: data
});
});
It seems that the X-Editable will bind a supplied array once upon creation of the control but will call the function each time.

Sencha Touch 2: How to override back button on Navigation View

I was wondering how to ovverride the back button on a navigation view. I tried using onBackButtonTap but it doesnt seem to work http://www.senchafiddle.com/#8zaXf
var view = Ext.Viewport.add({
xtype: 'navigationview',
onBackButtonTap: function () {
alert('Back Button Pressed');
},
//we only give it one item by default, which will be the only item in the 'stack' when it loads
items: [
{
//items can have titles
title: 'Navigation View',
padding: 10,
//inside this first item we are going to add a button
items: [
{
xtype: 'button',
text: 'Push another view!',
handler: function() {
//when someone taps this button, it will push another view into stack
view.push({
//this one also has a title
title: 'Second View',
padding: 10,
//once again, this view has one button
items: [
{
xtype: 'button',
text: 'Pop this view!',
handler: function() {
//and when you press this button, it will pop the current view (this) out of the stack
view.pop();
}
}
]
});
The fiddle you've mentioned works well in my local project on my machine. For some reason, it doesn't work on fiddle site. Try running it on your local project.
Still instead of using onBackButtonTap config, it's good to extend Ext.navigation.View class and override onBackButtonTap method. That way you'll have more control over whole components. You'd also like to override other configs as well. Here's what I'd use -
Ext.namespace('Ext.ux.so');
Ext.define('Ext.ux.so.CustomNav',{
extend:'Ext.navigation.View',
xtype:'customnav',
config:{
},
onBackButtonTap:function(){
this.callParent(arguments);
alert('back button pressed');
}
});
the line this.callParent(arguments) will allow component to behave in default way + the way to wanted it to behave. And if you want to completely override the back button behavior you can remove this line. Try doing both ways.
To use this custom component, you can use -
launch: function() {
// Destroy the #appLoadingIndicator element
Ext.fly('appLoadingIndicator').destroy();
var view = Ext.create('Ext.ux.so.CustomNav', {
fullscreen: true,
items: [{
title: 'First',
items: [{
xtype: 'button',
text: 'Push a new view!',
handler: function() {
//use the push() method to push another view. It works much like
//add() or setActiveItem(). it accepts a view instance, or you can give it
//a view config.
view.push({
title: 'Second',
html: 'Second view!'
});
}
}]
}]
});
}
Give this a shot. It'll work for you yoo.

ExtJS and this.control query

I have an issue with the next code block:
run: function(e, row){
var me = this;
var container = Ext.getCmp('centercontainer');
try {
container.removeAll();
} catch(e) { }
// This block is called from another file, I just put it here to show you.
me.panels = [{
xtype: 'tabpanel',
id: 'containertabpanel',
items: [{
itemId: 'package',
title: me.PackageTitle
},{
itemId: 'excursion',
title: me.ExcursionTitle
}]
}];
// Reset
container.setTitle(me.EditDestinationTitle + row.data.name);
container.add(me.panels);
me.tabs = container.getComponent('containertabpanel');
// console.log(Ext.ComponentQuery.query('#containertabpanel > #package'))
me.control({
// Work with
// 'tab': {
// Doesn't work
'containertabpanel > package': {
mouseover: me.doPackage
}
})
},
Anyone knows how do I get to catch the click event of "package" item of tabpanel component?
I saw when I use just "tab" selector on this.control query, that work, but I can't get only "package" tab component.
Thank you in advance.
In your definition of your tabpanel you can specify -
listeners:{
click:{
fn: function(){
//click handling code goes here
}
}
}
If I understood correctly this is controller code and you are trying to catch an item click on the panel which is one of many in a tabpanel
What you can do is identify your panel by any property that is unique to it via the component query syntax like this: button[myprop=blah]
This syntax will match any buttons on the page with the following config:
{
xtype:'button'
myprop:'blah'
}
In your case you can try tab[itemId=package]
What you also need to be careful about is controller can listening only for events that are fired by the components. Make sure the event you are listening for is fired (check the docs). You can always fire custom events if necessary.
You need to do this
me.control({
// Work with
// 'tab': {
// Doesn't work
'containertabpanel > #package': {
mouseover: me.doPackage
}
})

Categories