isUndoAvailable and isRedoAvailable undefined in handsontable - javascript

This is first time I use the handsonTable. I am trying to have a menu button with items undo/redo outside the context menu of the handsontable, and to make those items enabled/disabled, I need to check if the undo and redo are available after the table is rendered. At my surprise, when I call the functions isUndoAvailable() and isUndoAvailable inside the afterRender handler (as stated in the documentation https://github.com/handsontable/handsontable/wiki/Methods ), it causes the undefined JS error.
Here's a jsfiddle example of what I'm trying to do :
var container = document.getElementById('example');
var hot = new Handsontable(container,
{
data: data,
minSpareRows: 1,
colHeaders: true,
contextMenu: true,
afterRender: function(isForced){
if(this.isUndoAvailable()){
// do something
}
}
});
Please have a look at this jsfiddle example. Am I doing something wrong ?
Thanks in advance.

It seems you have stumbled upon an edge case, in which the undo/redo plugin (seems) to load only after the afterRender callback.
hot.isUndoAvailable()
This works in other callbacks, at least a few I tried just now.
Here's an updated version of your fiddle, just call isUndoAvailable outside of the afterRender callback and it will work.

Related

Bootsrap-select retains default options inside SetTimeout call

I am initializing a bootstrap-select control successfully on document.ready, but if I put it inside a setTimeout, all my options are ignored and the default options are used instead. I have tried debugging through the selectpicker js code but I can't figure out why this behavior is different. Please note I did see a very similar question but it was never successfully answered (Bootstrap-Select plugin not working with time consuming page load).
Here is my JS code:
$(document).ready(function () {
...display code
setTimeout(function () {
$('#mySelect').selectpicker({
container: 'body',
actionsBox: true,
liveSearch: true,
maxOptions: 16,
noneSelectedText: 'Sites',
selectedTextFormat: 'count>0'
});
...more display code
}, 100);
});
If I remove the setTimeout, the selectpicker displays with the options listed in the code, but if I leave the setTimeout, only the default options are used. Does anyone know why this is the case and how to get it to use the correct options when called from within the setTimeout?

Kendo UI Javascript redraw of TreeView not rendering

After creating and rendering a Kendo UI TreeView to fill a DIV, repeat invocation alternately renders only "loading..." or works properly. Since I am having possibly similar problems with Kendo UI ContextMenu, I speculate there may be some required cleanup in between, which is passively done by even invocations such that odd invocations work, but I can't figure it out (a link to Kendo UI docs I might be missing so I can understand why I've missed this would be appreciated to help with other issues).
In my JSFiddle example, click "draw" over and over and you'll see the alternate behavior. Speculatively clicking "draw, destroy, draw, destroy..." does not seem to help.
https://jsfiddle.net/rk3nfnnu/
<script>
function TreeDestroy() { // http://stackoverflow.com/questions/5431351
$('#Tree_Space').data('kendoTreeView').destroy();
alert('destroyed');
}
function TreeShow() {
$('#Tree_Space').kendoTreeView({
dataSource: [ { Name: 'Top', items: [ { Name:'Item' } ] } ],
template: kendo.template($('#Tree_template').html())
});
alert('shown');
}
</script>
draw |
destroy
<div id='Tree_Space'>
</div>
<script type='text/x-kendo-template' id='Tree_template'>
#= item.Name#
</script>
I have updated that fiddle. The destroy(); method probably only destroys allocated dom elements after the widget was rendered (the nodes). I doubt it cleans up the wrappers and whatnot. In your TreeDestroy(), issue a clear on that element div. Of course, you should call TreeDestroy prior to TreeCreate just in case.
function TreeDestroy() { // http://stackoverflow.com/questions/5431351
$('#Tree_Space').data('kendoTreeView').destroy();
$('#Tree_Space').html('');
alert('destroyed');
}
Here is some kendoui documentation that refers to how to handle manual deletion of widgets.

Can't find element using UI hash in Marionette Layout

I'm not sure why I can't get the button element using my UI hash. This is what my Layout looks like:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
ui: {
btnSave: "#btnSave"
},
events: {
"click #ui.btnSave": "onSave"
},
onInitialize: function () {
this.listenTo(App.vent, "DisableSaveButton", function(val) {
this.disableSaveButton(val);
},this);
},
disableSaveButton: function () {
this.ui.btnSave.prop("disabled",val).toggleClass("ui-state-disabled",val);
},
onSave: function () {
alert("saved!");
}
})
In VS2013, when my breakpoint hits the line inside disableSaveButton method, I entered $("#btnSave") into the Watch window and I was able to get the element back. I could tell because it had a length of 1. From this, I know the button is rendered. However, if I enter this.ui.btnSave into the Watch window, I would get an element with length of 0.
My BaseLayout object is basically a custom object extended from Marionette.Layout
Marionette version: 1.8.8
Any ideas why I can't find the button element using this.ui.btnSave?
Thanks in advance!
Got some help from a coworker and the issue might be because the element is out of scope. Basically, inside the Layout object, 'this' does not contain the element. We were able replace 'this.ui.btnSave' with '$("#btnSave",this.buttonset.el)' and that works fine. buttonset is the region that actually contains the html element.
This seems like an inconsistency because even though the ui hash didn't work, the click event utilizing the ui hash did work.
UPDATE 6/3/2015:
Another coworker of mine provided a better solution. Basically, in my Layout I use a display function to display my view. It looks something like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
}
})
Basically, I'm saying to set the html of my region, which is this.buttonset.el, to my template's html. As of now, my layout doesn't know any of the elements inside the region. It just contains a region which displays the elements. So there is some sort of disconnect between my layout and the elements in my region.
The correct solution, as opposed to my earlier workaround, is to simply add the following line of code at the end:
this.bindUIElements();
From Marionette Annotated Source:
This method binds the elements specified in the “ui” hash inside the
view’s code with the associated jQuery selectors.
So this final code looks like this:
Layout: App.Base.Objects.BaseLayout.extend({
// Rest of the code left out for brevity
display: function() {
$(this.buttonset.el).html(_.template($("#buttonset-view").html(), {"viewType": viewType}));
this.bindUIElements();
}
})
With this, I was able to finally able to retrieve my element using this.ui.btnSave.

Ext.layout.CardLayout requires fullscreen?

I'm programming a Sencha Touch app with a moderately complex composition of Ext.TabBar with Ext.Panel 's inside it.
But my Ext.Panel using Ext.layout.CardLayout runs in to a mysterious problem when not having a fullscreen: true property set on it: it throws an TypeError: Object card has no method 'setActiveItem' when I try to call the panel's .setActiveItem() method which it didn't in my proof of concept version that had fullscreen: true turned on.
I can replicate the problem on Chrome's console at a page with the Sencha Touch library loaded like this:
> var p1 = new Ext.Panel({layout:'card', items:[{html:'a'},{html:'b'}]})
undefined
> p1.setActiveItem(0)
TypeError: Object card has no method 'setActiveItem'
And it doesn't happen with the .fullscreenproperty:
> var p2 = new Ext.Panel({fullscreen: true,
layout:'card',
items:[{html:'a'},{html:'b'}]})
undefined
> p2.setActiveItem(0)
subclass
What gives?
Version info: I'm using Sencha Touch 1.0.1a
Update 1 (Jan 3, ~10.30UTC+1h), stepping around with the debugger and discovering things:
Just setting layout: 'card' won't trigger the creation of an actual Ext.layout.CardLayout object being created for real. Since .setActiveItem() tries to delegate to the compent's .layout property, it will fail almost instantly. However, setting .layout to a new Ext.layout.CardLayout causes more problems down the line..
Update 2: (Jan 3, ~12:25UTC+1h) It all comes down to various component objects not being rendered/inserted in the dependency sufficiently to be ready to render. I managed to get my code working by adding listeners, first a listener for the added event in the enclosing panel that does a this.setLayout(new Ext.layout.CardLayout());, then an afterrender listener on the component being added that finally calls .setActiveItem() to switch to the desired card.
A card layout works fine when it's not the outer, fullscreen one, but of course something has to be the root, fullscreen panel.
In this case, I'm using a 'fit' layout around the inner card layout, and setting the active item works fine:
var inner = new Ext.Panel({
layout:'card',
items:[
{html:'a'},
{html:'b'}
]
});
var outer = new Ext.Panel({
fullscreen:true,
layout:'fit',
items:[
inner
]
});
I suspect the matter is more about whether the panel has been rendered or not, and fullscreen:true just happens to force immediate rendering (and of any children, which is why it works in my code above).
This is from the Ext.Component source:
if (this.fullscreen || this.floating) {
this.monitorOrientation = true;
this.autoRender = true;
}
if (this.fullscreen) {
var viewportSize = Ext.Viewport.getSize();
this.width = viewportSize.width;
this.height = viewportSize.height;
this.cls = (this.cls || '') + ' x-fullscreen';
this.renderTo = document.body;
}
I suppose you could do some of this set up manually... but I have to ask, how do you avoid have a fullscreen ancestor component outside it in the first place?
I had the same problem. The card layout only works if the container panel had the layout of 'fit'. But setting the container class to fit was causing the scroller to not working properly. So what I had to do is disabling the scroller for container and the card panel and set the scroller for the children of card panel.
Update 2 in my question kind of answers the question, although the solution feels distinctly cargo-cultish. I won't be too surprised if it breaks in future Sencha Touch versions.
Sencha may have changed this since this was posted since I don't get an error. However, setActiveItem() will force my views to be rendered immediately which is undesirable when setting up the UI. If you want to set the initial card without rendering the views immediately, use the property directly instead of the setter, like this:
yourComp.activeItem = yourView;
instead of
yourComp.setActiveItem(yourView);

ExtJS: starting HtmlEditor defaulting to source

I'm using ExtJS 3.2.1 and I need a component almost identical to the bundled HtmlEditor, with one exception: it must start editing the HTML source code directly. The reason I don't use a normal TextArea is that the user should be able to preview the result of his actions before submitting.
I've tried calling toggleSourceEdit(), as per ExtJS documentation, with no success. Debugging, I see that the editor object has the sourceEditMode property set to true, and the Source Edit button seems as if it was "pressed", but clicking on it does not render the typed HTML, and clicking it again goes to the Source Mode.
I've tried calling toggleSourceEdit() after the container show() method, on the container afterLayout listener and on the editor afterRender listener. I've tried also calling it on another button that I added to the container. The result is the same on every try.
The only other option I see is updating ExtJS to 3.3.0, but I haven't seem anything related on the changelogs. Either way, it's going to be my next step. EDIT: The app had another problems when updating, we'll make a bigger effort to update later. As of right now, we are using the HtmlEditor in its original setting.
Thanks!
ran into the same problem (using 3.3.0 by the way)
stumbled upon a fix by dumb luck. i have no idea why this works, but second time is the charm. call it twice in a row to achieve the desired effect..
HTMLEditor.toggleSourceEdit(true);
HTMLEditor.toggleSourceEdit(true);
hope that helps!
Rather calling toggleSourceEdit(), try to setup the configuration while you create HtmlEditor Object
Using toggleSourceEdit() caused some problems for me. One was that this seemed to put the editor somewhere in limbo between source edit and WYSIWYG mode unless I used a timeout of 250ms or so. It also puts the focus in that editor, and I don't want to start the form's focus in the editor, especially since it's below the fold and the browser scrolls to the focused html editor when it opens.
The only thing that worked for me was to extend Ext.form.HtmlEditor and then overwrite toggleSourceEdit, removing the focus command. Then adding a listener for toggling to the source editor when the component is initialized. This is for Ext 4.1 and up. For older versions, replace me.updateLayout() with me.doComponentLayout().
var Namespace = {
SourceEditor: Ext.define('Namespace.SourceEditor', {
extend: 'Ext.form.HtmlEditor',
alias: 'widget.sourceeditor',
initComponent: function() {
this.callParent(arguments);
},
toggleSourceEdit: function (sourceEditMode) {
var me = this,
iframe = me.iframeEl,
textarea = me.textareaEl,
hiddenCls = Ext.baseCSSPrefix + 'hidden',
btn = me.getToolbar().getComponent('sourceedit');
if (!Ext.isBoolean(sourceEditMode)) {
sourceEditMode = !me.sourceEditMode;
}
me.sourceEditMode = sourceEditMode;
if (btn.pressed !== sourceEditMode) {
btn.toggle(sourceEditMode);
}
if (sourceEditMode) {
me.disableItems(true);
me.syncValue();
iframe.addCls(hiddenCls);
textarea.removeCls(hiddenCls);
textarea.dom.removeAttribute('tabindex');
//textarea.focus();
me.inputEl = textarea;
} else {
if (me.initialized) {
me.disableItems(me.readOnly);
}
me.pushValue();
iframe.removeCls(hiddenCls);
textarea.addCls(hiddenCls);
textarea.dom.setAttribute('tabindex', -1);
me.deferFocus();
me.inputEl = iframe;
}
me.fireEvent('editmodechange', me, sourceEditMode);
me.updateLayout();
}
})
}
Then to use it:
Ext.create('Namespace.SourceEditor', {
/*regular options*/
listeners: {
initialize: function(thisEditor) {
thisEditor.toggleSourceEdit();
}
}
});
htmlEditor.toggleSourceEdit(true);
one time should be enough if you do this listening to the afterrender event of the editor.

Categories

Resources