JsTree custom contextmenu in TypeScript - angularjs

I try to use jstree control in my TypeScript code for an angularjs application. I use jstree typings and jstree.directive to show a tree. Everything works to the point when I need to handle menu item click and call for the base method. Inside of my action there is no "this" (contextmenu) scope. Any suggestions?
class MapTreeViewController {
mapTreeView: JSTree;
vm.mapTreeView = $('#jstree').jstree(
{
'core': { 'data': items },
'plugins': ['themes', 'ui', 'contextmenu'],
'contextmenu': {
'items': function(node:any) {
var vmNode = this;
return {
'rename': { // rename menu item
'label': 'Rename',
'action': function(obj) {
this.rename(obj);
}
}
};
}
}
});
}
Somewhere inside of a method.

this is not an instance - take a look at the original function to see how to obtain an instance:
https://github.com/vakata/jstree/blob/master/src/jstree.contextmenu.js#L84
"action" : function (data) {
var inst = $.jstree.reference(data.reference),
...

Related

ExtJS 6.7 - How to attach blur and focus events on every form copoment?

I'd like to mark each field wrapping container with custom css class when field is focused and remove that class when field is blured. So I would like to attach focus/blur event methods to every form field component I add to any form.
in Ext 4 I did it like this:
Ext.ComponentManager.all.on('add', function(map, key, item) {
// Check if item is a Window and do whatever
if (item instanceof Ext.form.field.Base) {
item.on('focus', function(theField) {
var parentDom = null; //theField.bodyEl.findParent('.x-form-fieldcontainer');
if (!parentDom) {
parentDom = theField.bodyEl.findParent('.x-field');
}
if (parentDom) {
var parentEl = Ext.get(parentDom);
parentEl.addCls('focused-field');
}
}, item);
item.on('blur', function(theField) {
var parentDom = null; //theField.bodyEl.findParent('.x-form-fieldcontainer');
if (!parentDom) {
parentDom = theField.bodyEl.findParent('.x-field');
}
if (parentDom) {
var parentEl = Ext.get(parentDom);
parentEl.removeCls('focused-field');
}
}, item);
}
});
I'm not sure how to do it in ExtJS 6
Any help appreciated
Regards
Armando
You don`t need it, ExtJs has already '.x-field-focus' css class which is added to wrapper element on focus, so you can try to add your styles to the existing class. You can also look at the $form-field-focus-* theme variables..
Anyway, if you want to add this functionality, you can override the 'Ext.form.field.Base' class which is the parent of all the form fields.
Something like this:
Ext.define('overrides.form.field.Base', {
override: 'Ext.form.field.Base',
customCssOnFocus: 'focused-field',
initEvents: function() {
this.callParent(arguments);
this.on('focus', this.addCustomCssOnFocus, this);
this.on('blur', this.removeCustomCssOnBlur, this);
},
addCustomCssOnFocus: function() {
Ext.get(this.getEl().findParent('.x-field')).addCls(this.customCssOnFocus);
},
removeCustomCssOnBlur: function() {
Ext.get(this.getEl().findParent('.x-field')).removeCls(this.customCssOnFocus);
}
});

Quill Editor, The 'on' method works only once

I have the following, script for the QuillEditor(I have multiple editors):
var editors = {};
function editors() {
var toolbarOptions = [
[{'list': 'ordered'}, {'list': 'bullet'}],
];
data.forEach(function (el) {
editors[el] = new Quill(el['editor'], {modules: {toolbar: toolbarOptions}, theme: 'snow', scrollingContainer:el['quill'] });
editors[el].on('text-change', copyText(el['text'], editors[el]));
});
}
}
function copyText(text, editor) {
text.innerHTML = editor.root.innerHTML;
console.log(text.innerHTML)
}
To use it in backend, I'm copying the text from the editor to a textarea copyText(el['text'].
I need to always work, but it is coping text/html, only once when the function is executed. I'm expecting editors[el].on('text-change', to work like an event listener.
The scrollingContainer, doesn't a scroll. I check that the target exist, is the parent of the editor.
I am not sure if this part of the error but you have an extra } after after the editors function.
The main problem here is that instead of setting the event listener you are running the event listener which is why it is running only once.
So change the event-listener line to:
editors[el].on('text-change', function () {
copyText(el['text'], editors[el]);
});
I don't generally like creating functions in other functions and especially inside loops so I would recommend creating a function factory function that will create a new function for each element.
function createListener(text, editor) {
return function(){
copyText(text, editor);
};
}
And call it like this:
editors[el].on('text-change', createListener(el['text'], editors[el]));

How to pass View Param to View Model DataSource in Kendo Mobile?

What is the correct way to pass a view variable from the URL to a View Model to filter the result?
For example:
dataSource: new kendo.DataSource( {
transport: {
read: {
url: 'http://api.endpoint.com/resource',
}
parameterMap: function(options,type) {
if (type === 'read') {
return {
FormID: view.params.FormID
};
}
}
});
In the example above, there's a parameter in the URL called "FormID" and I would like to pass that value right to the parameterMap function. There is no "view" object, so I'm just putting that as an example.
I tried hooking into to the "data-show" and "data-init" functions to set this value to use, but the datasource fetches the data before these functions run.
Thanks
The configuration option options.transport.read can be a function, so you can compose the url there:
dataSource: new kendo.DataSource({
transport: {
read: function (options) {
// get the id from wherever it is stored (e.g. your list view)
var resourceId = getResourceId();
$.ajax({
url: 'http://api.endpoint.com/resource/' + resourceId,
dataType: "jsonp",
success: function (result) {
options.success(result);
},
error: function (result) {
options.error(result);
}
});
}
}
});
To connect this with your list view, you could use the listview's change event:
data-bind="source: pnrfeedsDataSource, events: { change: onListViewChange }"
then in viewModel.onListViewChange you could set the appropriate resource id for the item that was clicked on:
// the view model you bind the list view to
var viewModel = kendo.observable({
// ..., your other properties
onListViewChange: function (e) {
var element = e.sender.select(); // clicked list element
var uid = $(element).data("uid");
var dataItem = this.dataSource.getByUid(uid);
// assuming your data item in the data source has the id
// in dataItem.ResourceId
this._selectedResource = dataItem.ResourceId;
}
});
Then getResourceId() could get it from viewModel._selectedResource (or it could be a getter on the viewModel itself). I'm not sure how all of this is structured in your code, so it's difficult to give more advice; maybe you could add a link to jsfiddle for illustration.
You may use a "global" variable or a field in the viewmodel for that purpose. Something like
var vm = kendo.observable({
FormID: null,
dataSource: new kendo.DataSource( {
transport: {
read: {
url: 'http://api.endpoint.com/resource',
}
parameterMap: function(options,type) {
if (type === 'read') {
return {
FormID: vm.FormID
};
}
}
})
});
function viewShow(e) {
vm.set("FormID", e.view.params.FormID);
// at this point it is usually a good idea to invoke the datasource read() method.
vm.dataSource.read();
}
The datasource will fetch the data before the view show event if a widget is bound to it. You can work around this problem by setting the widget autoBind configuration option to false - all data-bound Kendo UI widgets support it.

Rally Ext JS change functionalty of existing component

I am trying to either override or extend an existing component - the Rally.ui.PercentDone component. I want to provide it with a portfolio item with all the data necessary to render it using the same color coding Rally native apps use (the Rally Health Color Calculator).
I really just need the function to pass the correct record. Here is my idea so far:
Ext.define('Custom.PercentDone', {
requires: ['Rally.ui.renderer.template.progressbar.PortfolioItemPercentDoneTemplate', 'Rally.util.HealthColorCalculator'],
extend : 'Rally.ui.PercentDone',
alias : 'widget.cpercentdone',
config: {
record: null
},
constructor: function(config) {
config = this.config;
this.renderTpl = Ext.create('Custom.renderer.template.progressbar.PercentDoneTemplate', {
calculateColorFn: Ext.bind(function(recordData) {
console.log('called my custom coloring fn');
var colorObject = Rally.util.HealthColorCalculator.calculateHealthColorForPortfolioItemData(config.record, config.percentDoneName);
return colorObject.hex;
}, this)
});
this.renderData = config;
this.mergeConfig(config);
this.callParent([this.config]);
}
});
var custom = Ext.create('Custom.PercentDone', {
record: item,
percentDoneName: 'PercentDoneByStoryPlanEstimate',
});
but my calculate color function is not being called instead of the default.
I got it to work by overriding:
var percentDoneByStoryCountEl = Ext.create("Rally.ui.PercentDone", {
record: item,
percentDoneName: "PercentDoneByStoryCount",
percentDone: item.PercentDoneByStoryCount
});
tpl = percentDoneByStoryCountEl.renderTpl;
tpl.calculateColorFn = function (recordData) {
var colorObject = Rally.util.HealthColorCalculator.calculateHealthColorForPortfolioItemData(item, "PercentDoneByStoryCount");
return colorObject.hex;
};
Ext.override(percentDoneByStoryCountEl, {
renderTpl: tpl
});
but I would like to figure out how to do it via extending the component instead.
Thanks for your help.

How to get access to a window that is loaded into a panel

I'm loading an external script (that creates a new window component) into a panel, which works fine.
Now, I want to access the created window from a callback function to register a closed event handler. I've tried the following:
panel.load({
scripts: true,
url: '/createWindow',
callback: function(el, success, response, options) {
panel.findByType("window")[0].on("close", function { alert("Closed"); });
}
});
However, the panel seems to be empty all the time, the findByType method keeps returning an empty collection. I've tried adding events handlers for events like added to the panel but none of them got fired.
I don't want to include the handler in the window config because the window is created from several places, all needing a different refresh strategy.
So the question is: how do I access the window in the panel to register my close event handler on it?
The simplest solution would be to simply include your close handler in the window config that comes back from the server using the listeners config so that you could avoid having a callback altogether, but I'm assuming there's some reason you can't do that?
It's likely a timing issue between the callback being called (response completed) and the component actually getting created by the ComponentManager. You might have to "wait" for it to be created before you can attach your listener, something like this (totally untested):
panel.load({
scripts: true,
url: '/createWindow',
callback: function(el, success, response, options) {
var attachCloseHandler = function(){
var win = panel.findByType("window")[0];
if(win){
win.on("close", function { alert("Closed"); });
}
else{
// if there's a possibility that the window may not show
// up maybe add a counter var and exit after X tries?
attachCloseHandler.defer(10, this);
}
};
}
});
I got it to work using a different approach. I generate a unique key, register a callback function bound to the generated key. Then I load the window passing the key to it and have the window register itself so that a match can be made between the key and the window object.
This solution takes some plumbing but I think its more elegant and more reliable than relying on timings.
var _windowCloseHandlers = [];
var _windowCounter = 0;
var registerWindow = function(key, window) {
var i;
for (i = 0; i < _windowCounter; i++) {
if (_windowCloseHandlers[i].key == key) {
window.on("close", _windowCloseHandlers[i].closeHandler);
}
}
};
var loadWindow = function(windowPanel, url, params, callback) {
if (params == undefined) {
params = { };
}
windowPanel.removeAll(true);
if (callback != undefined) {
_windowCloseHandlers[_windowCounter] = {
key: _windowCounter,
closeHandler: function() {
callback();
}
};
}
Ext.apply(params, { windowKey: _windowCounter++ });
Ext.apply(params, { containerId: windowPanel.id });
windowPanel.load({
scripts: true,
params: params,
url: url,
callback: function(el, success, response, options) {
#{LoadingWindow}.hide();
}
});
};
Then, in the partial view (note these are Coolite (Ext.Net) controls which generate ExtJs code):
<ext:Window runat="server" ID="DetailsWindow">
<Listeners>
<AfterRender AutoDataBind="true" Handler='<%# "registerWindow(" + Request["WindowKey"] + ", " + Detailswindow.ClientID + ");" %>' />
</Listeners>
</ext:Window>
And finally, the window caller:
loadWindow(#{ModalWindowPanel}, '/Customers/Details', {customerId: id },
function() {
#{MainStore}.reload(); \\ This is the callback function that is called when the window is closed.
});

Resources