How can I replace an existing Kendo UI widget? - javascript

I'm trying to extend an existing KendoUI widget (autocomplete). Since our application is already using a lot of instances of the autocomplete widget, I donĀ“t want to create a new widget which extends the current one but rather replace the existing one.
I already found this topic: kendo-ui autocomplete extend, but unfortunately it points creating a new one.
I tried the following code:
var plg = kendo.ui.AutoComplete.extend({
options: {
name: 'AutoCompleteMyOne'
},
init: function (_element, _options)
{
kendo.ui.AutoComplete.fn.init.call(this, _element, _options);
/*...*/
}
});
kendo.ui.plugin(plg);
The point is the name-attribute of the options. If the name is only "AutoComplete" the initialization does not work anymore: This line ends in an endlessloop:
kendo.ui.AutoComplete.fn.init.call(this, _element, _options);
How can I call the base-initialization or is it really overwritten?

If you replace the auto-complete widget, then your code is effectively calling its own init method recursively.
So you need to store the existing method and call that one, e.g. like this:
var plg = (function (init) {
return kendo.ui.AutoComplete.extend({
options: {
name: 'AutoComplete'
},
init: function (_element, _options) {
// modify the placeholder
_options.placeholder += " (custom)";
init.call(this, _element, _options);
/*...*/
}
});
})(kendo.ui.AutoComplete.fn.init);
kendo.ui.plugin(plg);
(demo)

Related

How to replace jquery with the mithril equivalent?

Something like :
peer.on('open', function(id){ // this is a non jquery event listener
$('#pid').text(id);
});
With something like...this is not correct:
peer.on('open', function(id){
m('#pid',[id])
});
Is this even the right approach? Should I be establishing a controller and model before I attempt to convert from jquery?
More details:
I am trying to rewrite the connect function in the PeerJS example: https://github.com/peers/peerjs/blob/master/examples/chat.html
If your event listener is something like websockets, then the event happens outside of Mithril, which means you need to manage redrawing yourself. This is what you'll need to do:
Store your data in an independent model
Use that model when rendering your Mithril view
On the open event, update your model, then call m.redraw()
Conceptual example:
var myModel = { id: 'blank' }
var MyComponent = {
view: function () {
return m('#pid', myModel.id)
}
}
m.mount(document.getElementById('app'), MyComponent)
// This happens outside mithril, so you need to redraw yourself
peer.on('open', function(id) {
myModel.id = id
m.redraw()
})
In Mithril, you should not try to touch the DOM directly. Your event handler should modify the View-Model's state, which should be accessed in your View method. If you post more code, I could give a more detailed explanation of how it pieces together.
Here is a bare-bones example that shows the data flowing through Mithril. Your situation will need to be more complicated but I'm not currently able to parse through all of that peer.js code.
http://codepen.io/anon/pen/eNBeQL?editors=001
var demo = {};
//define the view-model
demo.vm = {
init: function() {
//a running list of todos
demo.vm.description = m.prop('');
//adds a todo to the list, and clears the description field for user convenience
demo.vm.set = function(description) {
if (description) {
demo.vm.description(description);
}
};
}
};
//simple controller
demo.controller = function() {
demo.vm.init()
};
//here's the view
demo.view = function() {
return m("html", [
m("body", [
m("button", {onclick: demo.vm.set.bind(demo.vm, "This is set from the handler")}, "Set the description"),
m("div", demo.vm.description())
])
]);
};
//initialize the application
m.module(document, demo);
Notice that the button is calling a method on the View-Model (set), which is setting the value of a property (vm.description). This causes the View to re-render, and the div to show the new value (m("div", demo.vm.description())).

Backbone Forms weird bug/behavior

BRIEF:
I write my custom backbone editor, but Backbone can't initialize it because can't find schema for it. Backbone looks for schema in Form.editors array at backbone-forms.js? How can I register schema of my custom editor?
DETAILED:
I use Backbone Forms which are initialized in the next way:
backbone-forms.js
var Form = Backbone.View.extend({
initialize: function(options) {
//.....
//Check which fields will be included (defaults to all)
var selectedFields = this.selectedFields = options.fields || _.keys(schema);
_.each(selectedFields, function(key) {
var fieldSchema = schema[key];
fields[key] = this.createField(key, fieldSchema); // <==== Here troubles begins
}, this);
},
//....
}, {
//....
editors: {} // <===== QUESTION: where I should put my custom editor in this array???
});
Problem: When Backbone creating new Form it calls createSchema method which looks like:
createSchema: function(schema) {
//........
//PROBLEM: Form.editors[schema.type] is undefined
schema.type = (_.isString(schema.type)) ? Form.editors[schema.type] : schema.type;
return schema;
}
and Form.editors[schema.type] is undefined. That means I can't create/render my custom editor!
Question: Where/How I can register my custom editor in Form.editors array?
You can reference the custom editor directly from within the schema, i.e. reference the function itself rather than a string:
var CustomEditor = Backbone.Form.editors.Base.extend({});
var form = new Backbone.Form({
schema: {
customField: { type: CustomEditor }
}
});
Or, like you found, you can also add the editor to Backbone.Form.editors so you can use the string as a shortcut.

Referencing a parent object in callback functions with jQuery

I've a page that is generated dynamically, and that includes certain number (user-dynamically-defined) of advanced scatter plot charts. I intend to create a JavaScript object which defines the scatter plot itself, i.e. which takes some parameters, some data, and some container ID, and which will create the various elements needed to obtain the visualisation: canvas elements, toolbar, etc.. To do so, I started with the following (simplified) class:
(function () {
if (!this.namespace) { this.namespace = {};}
this._instances = { index: 0 };
this.namespace.ScatterPlot = function (containerId, file, options) {
_instances.index ++;
this.id = this.containerId+"-"+_instances.index ;
this.containerId = containerId ;
_instances [this.id] = this;
// ... Do stuffs with file and options ...
// Initialize elements once the DOM is ready
$(this.updateDOM);
}
namespace.ScatterPlot.prototype = {
updateDOM: function() {
$("<canvas>")
.click(clickCallback)
.appendTo("#"+this.containerId);
//(...)
},
clickCallback: function() {
alert("Some click: "+this.id);
}
}
})();
Each object can be created with:
var v1 = new namespace.ScatterPlot("container1", "foo", "foo");
var v2 = new namespace.ScatterPlot("container2", "foo", "foo");
There are two problems here: (1) in updateDOM, 'this' does not make reference to my initial ScatterPlot object, which means that this example will never work, and (2) similarly, the clickCallback will not be able reference the scatterplot with 'this' either.
I'm new to javascript, and I'm still struggeling to understand the logic of OO programming in javascript, so the question is: I'm I taking the wrong direction here ? After some digging, I could roughly achieve what I wanted by passing this to updateDOM:
$(this.updateDOM(this)); // This blows my eyes but does the trick, at least partially
updateDOM: function(that) {
$("<canvas>")
.click(that.clickCallback)
.appendTo("#"+that.containerId);
//(...)
},
clickCallback: function() {
// Not working either... Should pass 'that' to the function too
alert("Some click: "+this.id);
}
But I don't feel this patters to be very elegant... And the problem is not fixed either regarding the click callback.
Thoughts ?
Have a look at MDN's introduction to the this keyword.
The standard ways of dealing with that issue are using a that variable - not as an argument, but in a separate function:
var that = this;
$(function() {
that.updateDOM();
});
// or
$(this.getClickCallback());
...
namespace.ScatterPlot.prototype.getClickCallback = function() {
var that = this;
return function clickCallback(e) {
alert("Some click: "+that.id);
};
};
Alternatively, you can always use .bind() (or $.proxy for older browsers) which do quite what the second example does in a more generic way:
$(this.clickCallback.bind(this));

Grid Drag & Drop: Suppress for some records (groups)

Hy there,
is it possible to control which records can be dragged and where they can be dropped (suppress drag-operation either right from the beginning or in the middle during hovering)?
What i need in detail is the following:
I'm having a grid with some groups (lets say male & female) and only want to activate the d&d inside group 'female' which means 2 things:
1.) I started dragging a record from group 'female' (Lisa). As soon as the drag is outside the group 'female' (above group 'male'...) it should display an error-state like when dragging outside the bounds of the grid:
2.) Starting to drag an item from group 'male' should either not be possible at all (just don't show the d&d panel) or show the error-state like mentioned above right from the beginning and never change to "correct"-state.
Thanks,
mike
After some digging around in the sources of ext i just found a solution which works but isn't perfect at all:
The "drop-allowed-indication" can be handled by the underlying DropZone which is created in onViewRender of the treeviewdragdrop-plugin. This is not documented but can be seen in the source-code of the plugin.
Everything that needs to be done (at least for this example) is to override/extend the onNodeOver- & onContainerOver-method of the DropZone to return the appropriate css-class for the drop-not-allowed- or drop-allowed-indication.
Ext.override(Ext.view.DropZone, {
onNodeOver: function(nodeData, source, e, data) {
if (data && data.records && data.records[0]) {
// The check should be better specified, e.g. a
// female with the name 'Malena' would be recognized as male!
if (nodeData.innerHTML.indexOf(data.records[0].get('sex')) < 0) {
return this.dropNotAllowed;
}
}
return this.callOverridden([nodeData, source, e, data]);
},
onContainerOver: function(source, e, data) {
return this.dropNotAllowed;
}
});
Working example: http://jsfiddle.net/suamikim/auXdQ/
There are a few things i don't like about this solution:
The override changes (per definition...) the behaviour of all DropZones in my application. How can i only override/extend the specific DropZone of one grid?I've tried the following:
Add an interceptor to the dropZone after the gridview has been rendered: http://jsfiddle.net/suamikim/uv8tX/
At first this seems to work because it shows the correct drop-allowed-indication but it drops the record even if the indicator shows that it's not allowed (it always shows the "green line"...)
Define a new dnd-plugin which extends the treeviewdragdrop-plugin and just override the onNodeOver-method of the dropZone after it's creation: http://jsfiddle.net/suamikim/5v67W/
This kind of does the opposite from the interception-method. It also shows the correct indication but it never shows the "green line" and won't allow the drop anywhere...
The class i'm overriding (Ext.view.DropZone) is marked private in the documentation with a note that it shouldn't be used directly...
I would really appreciate some comments on those 2 issues and maybe even some better solutions!
Thanks, mik
Edit:
I adjusted the version in which i defined a new dnd-plugin which extended the original gridviewdragdrop-plugin. The "magic" was to also extend gridviewdropzone and extend the onNodeOver-method instead of just overriding it.
This needs to be done because the original onNodeOver-method which is now called by callParent handles the "green line" and finally allows the drop.
The only thing my extended gridviewdragdrop-plugin does now is to create a instance of the new dropzone-class instead of the standard gridviewdropzone in the onViewRender-method.
This seems like a reasonable way so far:
// Extend the treeview dropzone
Ext.define('ExtendedGridViewDropZone', {
extend: 'Ext.grid.ViewDropZone',
onNodeOver: function(nodeData, source, e, data) {
if (data && data.records && data.records[0]) {
// The check should be specified, e.g. a female with the name 'Malena' would be recognized as male!
if (nodeData.innerHTML.indexOf(data.records[0].get('sex')) < 0) {
return this.dropNotAllowed;
}
}
return this.callParent(arguments);
},
onContainerOver: function(source, e, data) {
return this.dropNotAllowed;
}
});
Ext.define('ExtendedGridDnD', {
extend: 'Ext.grid.plugin.DragDrop',
alias: 'plugin.extendeddnd',
onViewRender: function(view) {
this.callParent(arguments);
// Create a instance of ExtendedGridViewDropZone instead of Ext.grid.ViewDropZone
this.dropZone = Ext.create('ExtendedGridViewDropZone', {
view: view,
ddGroup: this.dropGroup || this.ddGroup
});
}
});
Working example: http://jsfiddle.net/5v67W/1/
Nonetheless I'd still appreciate different approaches because it still feels like it could be done easier...
You can do it like this for Ext 5 and 6
On your treepanel definition:
listeners: {
viewready : 'onViewReady'
}
on your ViewController:
onViewReady : function (tree) {
var view = tree.getView(),
dd = view.findPlugin('treeviewdragdrop'),
rec;
dd.dropZone.onNodeOver = function (data, e) {
rec = view.getRecord(e.getTarget(view.itemSelector));
return rec.get('customProperty') === 'someValue' ? this.dropAllowed : this.dropNotAllowed;
}
},
reference https://www.sencha.com/forum/showthread.php?282685
https://www.sencha.com/blog/declarative-listeners-in-ext-js-5/

Creating methods on the fly

Hi I'm trying to author a jQuery plugin and I need to have methods accessible to elements after they are initialized as that kind of object, e.g.:
$('.list').list({some options}); //This initializes .list as a list
//now I want it to have certain methods like:
$('.list').find('List item'); //does some logic that I need
I tried with
$.fn.list = function (options) {
return this.each(function() {
// some code here
this.find = function(test) {
//function logic
}
}
}
and several other different attempts, I just can't figure out how to do it.
EDIT:
I'll try to explain this better.
I'm trying to turn a table into a list, basically like a list on a computer with column headers and sortable items and everything inbetween. You initiate the table with a command like
$(this).list({
data: [{id: 1, name:'My First List Item', date:'2010/06/26'}, {id:2, name:'Second', date:'2010/05/20'}]
});
.list will make the <tbody> sortable and do a few other initial tasks, then add the following methods to the element:
.findItem(condition) will allow you to find a certain item by a condition (like findItem('name == "Second"')
.list(condition) will list all items that match a given condition
.sort(key) will sort all items by a given key
etc.
What's the best way to go about doing this?
If you want these methods to be available on any jQuery object, you will have to add each one of them to jQuery's prototype. The reason is every time you call $(".list") a fresh new object is created, and any methods you attached to a previous such object will get lost.
Assign each method to jQuery's prototype as:
jQuery.fn.extend({
list: function() { .. },
findItem: function() { .. },
sort: function() { .. }
});
The list method here is special as it can be invoked on two occasions. First, when initializing the list, and second when finding particular items by a condition. You would have to differentiate between these two cases somehow - either by argument type, or some other parameter.
You can also use the data API to throw an exception if these methods are called for an object that has not been initialized with the list plugin. When ('xyz').list({ .. }) is first called, store some state variable in the data cache for that object. When any of the other methods - "list", "findItem", or "sort" are later invoked, check if the object contains that state variable in its data cache.
A better approach would be to namespace your plugin so that list() will return the extended object. The three extended methods can be called on its return value. The interface would be like:
$('selector').list({ ... });
$('selector').list().findOne(..);
$('selector').list().findAll(..);
$('selector').list().sort();
Or save a reference to the returned object the first time, and call methods on it directly.
var myList = $('selector').list({ ... });
myList.findOne(..);
myList.findAll(..);
myList.sort();
I found this solution here:
http://www.virgentech.com/blog/2009/10/building-object-oriented-jquery-plugin.html
This seems to do exactly what I need.
(function($) {
var TaskList = function(element, options)
{
var $elem = $(element);
var options = $.extend({
tasks: [],
folders: []
}, options || {});
this.changed = false;
this.selected = {};
$elem.sortable({
revert: true,
opacity: 0.5
});
this.findTask = function(test, look) {
var results = [];
for (var i = 0,l = options.tasks.length; i < l; i++)
{
var t = options['tasks'][i];
if (eval(test))
{
results.push(options.tasks[i]);
}
}
return results;
}
var debug = function(msg) {
if (window.console) {
console.log(msg);
}
}
}
$.fn.taskList = function(options)
{
return this.each(function() {
var element = $(this);
if (element.data('taskList')) { return; }
var taskList = new TaskList(this, options);
element.data('taskList', taskList);
});
}
})(jQuery);
Then I have
$('.task-list-table').taskList({
tasks: eval('(<?php echo mysql_real_escape_string(json_encode($tasks)); ?>)'),
folders: eval('(<?php echo mysql_real_escape_string(json_encode($folders)); ?>)')
});
var taskList = $('.task-list-table').data('taskList');
and I can use taskList.findTask(condition);
And since the constructor has $elem I can also edit the jQuery instance for methods like list(condition) etc. This works perfectly.
this.each isn't needed. This should do:
$.fn.list = function (options) {
this.find = function(test) {
//function logic
};
return this;
};
Note that you'd be overwriting jQuery's native find method, and doing so isn't recommended.
Also, for what it's worth, I don't think this is a good idea. jQuery instances are assumed to only have methods inherited from jQuery's prototype object, and as such I feel what you want to do would not be consistent with the generally accepted jQuery-plugin behaviour -- i.e. return the this object (the jQuery instance) unchanged.

Categories

Resources