Below I've tried to simplify more complex TreeView usage, where I'm failing to implement a ContextMenu on TreeView node items, into a fiddle that exhibits potentially related issues. Steps: In my simplified example one left clicks to select a node, then right clicks on another node, and finally dismisses with escape, and then selection indications are confused. I've tried "return false", select(nothing), and preventDefault() to no avail.
My question is: is this a bug in Kendo UI, or am I missing something in my usage of TreeView?
https://jsfiddle.net/3cp9m8wo/
<div id='Tree_Space'></div>
<script type='text/x-kendo-template' id='Tree_template'>
#= item.Name#
</script>
<script>
$('#Tree_Space').kendoTreeView({
dataSource: [ { Name: 'Top', items: [ { Name:'Item' }, { Name:'Item' } ] } ],
template: kendo.template($('#Tree_template').html())
});
$("#Tree_Space").data("kendoTreeView").expand('.k-item');
</script>
My full goal would be to disable Kendo UI selection on TreeView nodes completely, allowing me to implement left clicks (actions) and right clicks (ContextMenus) for elements I placed within tree nodes. However, I've not seen a way to disable select on TreeView. I do find JQuery.click() does seem to work and deselect Kendo UI selections, but Kendo UI Context Menu fails to popup on right click, and displays other artifacts - one of which I think I've isolated here in hopes of learning something.
You could try using something in your template to control this:
<script type='text/x-kendo-template' id='Tree_template'>
//Ideally, your server will return a collection that determines if the item can be selected, likewise, you could add a IsParentNode or something to indicate the item should be treated differently.
#if (item.CanSelect != 'null' && item.CanSelect==true) { #
<span>#: item.Name#</span>
#}else{#
<span class='no-select'>#: item.Name#</span>
#}#
</script>
<script type="text/javascript">
$(document).ready(function () {
//Add code here to tie into the onmousedown of all .no-select elements
$(document).on("click", "no-select", function (e) {
e.preventDefault();
});
});
</script>
Related
In tinymce 4, the menu bar is rendered but each menu is rendered only on click.
To illustrate this, notice that each menu from the menu bar has the mce-menu class.
At any time, if no menu is open, if you try to get the set of menus, you'll fail because they aren't rendered yet:
var menuSet = $('.mce-menu');
// menuSet.length : 0
But if you click a menu bar header, let say the insert menu, it will be rendered and opened. Now, keeping it open, going to the console and retrying:
var menuSet = $('.mce-menu');
// menuSet.length : 1
and you'll get the opened menu.
Now if you close it clicking anywhere out of the opened menu, and retrying:
var menuSet = $('.mce-menu');
// menuSet.length : 1
... the menu is not removed from the DOM. It's a good news: since the menu was rendered once, we can get and manipulate it.
I have some DOM manipulation to do with each .mce-menu elements, but I'll have to now when each menu is opened the first time.
But how to handle a such event ?
I can't get any clue from the official documentation nor the forums, or anywhere.
It's definitely possible, but we both were not aware enough of how JS events are managed.
I tried to code my events handlers the old way :
$('body').on('click', function() {
do_stuff();
});
While I had to do it the new, correct way :
$('body').on('click', '.mce-btn', function() {
do_stuff();
});
With this, the events are correctly managed.
Try to use onPostrender funtion :
editor.addMenuItem("mybtn", {
type: "menuitem",
name: 'mybtn',
onPostRender:function (){
// write your code here//
},
I resolved the issue by writting a convenient `Tinymce 4 plugin focused on that purpose.
Of course this plugin is open-sourced by the GNU GPL v2 license, following the original Tinymce licensing policy.
Tinymce Plugin MenusController:
https://github.com/sirap-group/tinymce-plugin-menuscontroller
But I didn't wrote the documentation yet, my apologies.
However, here is how you can use it:
Install the plugin
Download the latest release tarball from github or, even better, install it from bower:
bower install tinymce-plugin-menuscontroller
If you don't know bower, discover it here: https://bower.io (npm i -g bower; bower --help).
The npm package isn't available yet, I'd provide it soon (but any Pull Request on github is welcome...).
By default, the plugin folder would be downoaded and placed in ./bower_components. If you've installed tinymce the same way, you've got also ./bower_components/tinymce or ./bower_components/tinymce-dist.
You don't need to add the script to your index.html file because tinymce load it itself if you setup it correctly.
So you need to :
symlink it to the tinymce plugin folder:
$ cd ./bower_components/tinymce/plugins
$ ln -s ../../tinymce-plugin-menuscontroller menuscontroller
load it in tinymce init. For example:
tinymce.init({
selector: 'textarea',
// [...]
plugins: 'menuscontroller'
})
Get the plugin instance:
var editor = window.tinymce.activeEditor
var menusCtl = editor.plugins.menuscontroller
// at this point, if menusCtl is undefined, something gone wrong in the setup step: please check the previous steps.
Plugin API (v0.2.1)
Instance Methods
Get the menu bar:
menusCtl.getMenubar()
Get each menu by the name it was registered with:
menusCtl.getMenuByName(String: name)
Get the toolbars
menusCtl.getToolbars
Events
Event: menusController:mceMenuRendered event
When any tinymce menu is rendered
$('body').on('menusController:mceMenuRendered', function (evt, menuDomNode) {
console.log(menuDomNode)
})
The menusController:mceMenuRendered event is called one for each menu of the active editor menubar, when it is rendered, so when the user click the dropdown menu (File link for the "file" menu, Insert for the "insert" menu, etc...).
Event: menusController:mceMenuItemRendered:<menuDomID>
When any menu item is rendered. Let's say we've created a menu item with the my-custom-menu-item identifier. So tinymce set its DOM ID to my-custom-menu-item. Thus, the MenusController plugin will create and bind to body the following event:
menusController:mceMenuItemRendered:my-custom-menu-item
So you can handle the rendered event of your custom menu item listening on it:
$('body').on('menusController:mceMenuItemRendered:my-custom-menu-item',
function (evt, menuItemDomNode) {
console.log(menuItemDomNode)
}
)
MenusController API (v0.3.0+)
A the time of wrinting (Monday, mars the 13th, 2017), the last released version is the v0.2.1. But the v0.3.0 is planned to be released soon, and will provide a new event, more useful than the last.
Event: menusController:mceMenuItemRendered
When you need to know the menu item ID to handle the event menusController:mceMenuItemRendered:<menuDomID> and get the menu item DOM Node as callback argument, the event menusController:mceMenuItemRendered don't needs it but provides it as callback argument for each new rendered menu item:
$('body').on('menusController:mceMenuItemRendered',
function (evt, menuItemID) {
console.log(menuItemID) // 'my-custom-menu-item'
// So you can hanlde all menu item even if you don't know its ID
// And you can also handle the DOM Node with the selector by ID
var selector = '#' + menuItemID
var menuItem = $(selector)
console.log(menuItem) // jQuery object (the menu item)
}
)
With tinymce you can fully customize the menu buttons via the editor object:
tinymce.init({
/*....*/
setup: function(editor) {
editor.addButton('mybutton', {
type: 'menubutton',
text: 'My button',
icon: false,
onclick: function(){
alert('Some Message');
},
menu: [{
text: 'Menu item 1',
onclick: function() {
alert('Some Message');
}
}]
});
}
/*....*/
});
Unfortunately you cannot insert html inside the text property, but i think you can do that with more research. You can also create a callback function for the click event on the menu button.
Personally, I will use tinymce official api to modify the dom instead of doing some other event driven dom manipulation.
You can find more a good example here
I've been fighting with this for some time now and I'm hoping someone here has run into a similar problem and solution.
Background
Our project is an ASP.NET MVC 4 site. It uses both jQuery 1.11.3 and Sencha ExtJs 4.2.2. One of the features I'm working on is a page that will call different partial views that have individual report parameters. The partials are also used in their own respectable pages. However, this feature dynamically loads the partial views via jQuery ajax and displayed inside of a jQuery dialog. Some of the partial view are using the ExtJs Ext.Button with a menu of Ext.menu.DatePicker. On their own respectable page the date pickers work perfectly fine. However, when the partial is loaded inside of the jQuery dialog it causes the datepicker calendar to set the position incorrectly causing massive overflow in the dialog.
Markup & code for the date picker on the partial
<td width="35%" class="tr35_form_input_normal">
#Html.TextBoxFor(model => model.CutOff, new { #style = "float:left;" })
<div id="datePickerButton" style="float:left;margin-top:-2px;"></div>
<div style="clear:both;">
#Html.ValidationMessageFor(model => model.CutOff)
</div>
#Html.HiddenFor(model => model.BeginDate)
#Html.HiddenFor(model => model.EndDate)
</td>
<script type="text/javascript">
Ext.create('Ext.Button', {
iconCls: 'x-form-date-trigger',
renderTo: 'datePickerButton',
handler: function () {
this.menu = new Ext.menu.DatePicker({
renderTo: 'datePickerButton',
autoShow: true,
handler: function (picker, date) {
$(cutOffDate).val(Ext.Date.format(date, 'n/j/Y')).change();
}
});
this.menu.picker.setMinDate(new Date($(hfBeginDate).val()));
this.menu.picker.setMaxDate(new Date($(hfEndDate).val()));
if (!isNaN(Date.parse($(cutOffDate).val()))) {
this.menu.picker.setValue(new Date($(cutOffDate).val()));
}
}
});
</script>
This is how it looks & works on it's own page.
How the dialog looks before I click on the date picker button.
What's happening when I click the button.
I can see what's happening and have an idea on how to fix it by using the jQuery widget factory to use the _allowInteraction method, which I've done before to fix an issue with ExtJs grids and their filters inside of a jQuery dialog. The problem though is, the DatePicker has not been created yet when I first click the button so there's no way I can find the element/component.
The other thing you'll notice how the jQuery dialog seems to overflow. I've tried to set the overflow to hidden on all the underlying elements and no matter what it still repositions the inner form element at some negative position.
Any help would be greatly appreciated.
OK, so after some more playing around here's my solution and it's quite simple.
<script type="text/javascript">
Ext.override(Ext.menu.DatePicker, {
initComponent: function(){
this.callParent();
},
listeners: {
show: function(){
var el = $('#' + this.id);
var dlg = $(el).parents('.ui-dialog');
//Make sure we are dealing with a datepicker
//inside of a jQuery dialog.
if($(dlg).length > 0){
$(dlg).find('div.x-css-shadow').remove()
$(el).appendTo('body');
}
}
}
});
</script>
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.
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.
I'm using Ember.js with handlebars and I need to make a div within my page collapse/expand when clicked. I know how to do this in jQuery, but I can't use any jQuery. Does anyone know how to accomplish this? Also I don't want to just toggle a hide attribute, I need the full sliding up and down feature for collapsing. If anyone has any ideas, I'd really appreciate it.
Thanks
Clicking on your view will cause a click event to be triggered. You can code your animation in any manner you want inside a click event handler in your view:
CollapsableView = Ember.View.extend({
click : function(event) {
this.$().toggle('fast');
}
})
The proper way of doing this in Ember is via the awesome Liquid Fire addon.
The outline:
Install Liquid Fire into your project.
Define a transition like this:
this.transition(
this.hasClass('transition-spoiler'),
this.toValue(true),
this.use('toDown'),
this.reverse('toUp')
);
In your controller/component, create a property spoilerIsVisible and a toggleSpoiler property:
spoilerIsVisible: false,
actions: {
toggleSpoiler: function() {
this.toggleProperty('spoilerIsVisible');
}
}
In your page/component template, create a button and a spoiler wrapper like this:
<button {{action 'toggleSpoiler'}}>
{{if spoilerIsVisible 'Show spoiler' 'Hide spoiler'}}
</button>
{{#liquid-if spoilerIsVisible class="transition-spoiler"}}
<p>Dumbledore dies</p>
{{/liquid-if}}
Note that you can wrap steps 3-4 into an x-spoiler component or something.
I do something similar, but with a tree-structure. I have written a blog post about this previously here: http://haagen-software.no/blog/post/2012-05-05-Ember_tree
It has the features you need in it, in that it adds and removed elements from the DOM when the nodes are clicked on.
A working example can be seen in an app I am currently building here: https://github.com/joachimhs/EurekaJ/tree/netty-ember/EurekaJ.View/src/main/webapp