Programming jQuery UI like ExtJS - javascript

I'm trying to develop an abstraction layer to jQuery UI that allows define Widgets as Objects just like (or similar) to ExtJS. This is the concept:
var mydialog = new $.ui.dialog({
modal:true,
renderTo:'body',
title:'The Windows Tittle',
content:'The content of the Window'
});
Now I can say:
mydialog.show();
The first step (i think) was to add a Class creation function to jQuery, this allow to make classes:
$.MYNAMESPACE.dialog = $.Class ({
constructor:function(){}
//methods and properties
});
And here comes the real problem: What I have to put inside the previous class definition to link the real $.ui.dialog with mine? What I meant is that I don't want to create a new widget, I just want to reuse the code behind predefined jQuery UI widgets in order to create an abstraction layer that allows OOP with jQuery UI.
Thanks in advance

have you tried the jquery-ui widget factory? You might be re-inventing the wheel.js
slideshow on what you get with the widget factory
official splash page and the api
quick idea what it's doing. I want a new dialog with some custom events on it
//this is instantiating the widget, does not need to be in the same file
$(function(){
$(".some-selector").miDialog({autoopen:true //because we can});
});
//this is a definition, not an instantiation of the widget. aka,
$.widget("mi.miDialog" //namespace
,$.ui.dialog //inherit from this jquery widget
,//start your widget definition
{ options:{autoopen:false,//overwrite parent default option, while still giving instance option to override our definition's override of parent
someInstanceSafeOption: {why:"not",have:"a",subobject:"option"} },
//underscore functions are 'private' unless you dig into the prototype manually
_create :function(){
//you'll need this function. guaranteed to run once.
// upcoming version call parent create
this._super();
//current version call parent create
$.ui.dialog.prototype._create(this.options);
this.element.addClass("mi-dialog"); //help with custom styling
this._trigger("created"); //trigger custom events
//register for events here, as _create() will only run once per individual instance
},
_init:function(){
//this will run every time someone calls $('some-selector').miDialog();
//i have yet to use it much
},
publicFunction: function(some, params){
//this function does whatever you want, and is called $('some-selector'.miDialog("publicFunction", some,params);
},
_destroy: function(){
//clean up after your widget's create function, if needed.
}

Related

How to tell if TinyMCE is initiated/created on a page?

I have a page with a canvas where a user can add text with the "add text" button. When the user clicks this, a new TinyMCE is created right on the canvas. I would like to change this new TinyMCE, but because I cannot access the tinyMCE creation code (the init) I got to have some kind of "on init listener" for TinyMCE in my `document.ready()'.
I know you can make a callback on init of the TinyMCE like this:
setup: function (ed) {
ed.on('init', function(args) {
console.debug(args.target.id);
});
}
But as I said I can't reach the creation code of the TinyMCE.
How can I check whether a new TinyMCE instance has been created (and use it in Javascript)?
If your page provides you a configuration object but you want to tweak its settings, you can always use JavaScript to modify/extend your standard init on a page by page basis.
For example, start with your standard configuration:
baseConfig = {
selector: 'textarea'
....
}
...since this is just a simple JavaScript object you can inject additional properties/methods into that object before you use it to initialize TinyMCE.
For example:
customConfig = {
image_advtab: true,
content_css : "CSS/content.css?" + new Date().getTime(),
setup: function (editor) {
editor.on('init', function () {
//Do what you need to do once TinyMCE is initialized
});
}
}
Then you can "inject" customConfig into baseConfig. The easiest way is to use jQuery's extend method:
$.extend(baseConfig, customConfig);
...this will take all the methods and properties from customConfig and add them to baseConfig. Once you are done you can then load TinyMCE using the newly updated baseConfig:
tinymce.init(baseConfig);
You can also use JavaScript to remove properties (read up on the delete) so you could also remove configuration properties that you don't want in your TinyMCE init.

Delay Loading Custom Bindings

I'm working on an Single Page Application and we're using Knockout quite extensively. We've currently got a list of item that can be clicked, and upon doing so they'll load some content into a modal container. The image below illustrates the different items that'll trigger various content to be displayed:
The content of these containers differs substantially, and can have many different custom bindings spread over several tabs. The items in the image are fairly simple and just use Knockout Components but when we start displaying the modal contents they are much more heavy on the JavaScript hence using bindings.
I've recently added in lazy loading of the JavaScript and HTML templates required by the components and this has worked really well. I've had to use a custom component loader as for various reasons we don't want to use require or similar AMD module loader.
Now I'm faced with the same issue with custom knockout bindings, as I expect we could end up with 100 hundred bindings quite easily as this product expands. Unfortunately there doesn't seem to be an obvious way to load custom bindings in a lazy way like components though, and I'm trying to figure out if there's a way to do this, and what the best way would be. Note that I also don't know the name of the binding up front all of the time, sometimes I may wish to load them dynamically based on the name of an observable.
The only things I've managed to find of note so far, are that there is a ko.getBindingHandler() function which can be overridden, but it requires a synchronous load of a binding handler.
I have thought of an approach to try and do this, but it uses components and feels like a really backward way of achieving my end goal. It'd be something like this:
Replace a usual custom binding:
<div data-bind="lineChart: $data"/>
with
<div data-bind="component { name: compName, params: { vm: $data } }"/>
I'd then use a custom component loader, which is actually just loading the binding handler JavaScript, and writing out essentially a placeholder div with the custom binding in:
var bindingLoader = {
getConfig: function(name, callback) {
if(name.startsWith("binding-")) {
callback({ binding: name.replace("binding-", ""), jsUrl: "/bindings/" + name });
return;
}
callback(null);
},
loadComponent(name, componentConfig, callback) {
var obj = { };
obj.template = '<div data-bind="' + componentConfig.name + ': $data"/>';
$.ajax({ url: componentConfig.jsUrl, dataType: "text" })
.done(function(data)) {
(new Function(data))();
callback(obj);
});
}
}
I'm sure however there must be a better way of achieving this, but I can't think of any other options right now.
I've also answered this question on Github.
#Jeroen is right that there's no built-in way to asynchronously load custom bindings. But any binding can "lazily" perform its own action, which is what the component binding does. By overwriting ko.getBindingHandler, we can detect bindings that haven't yet been loaded, and start the loading process, then return a wrapper binding handler that applies the binding once it's loaded.
var originalGetBindingHandler = ko.getBindingHandler;
ko.getBindingHandler = function (bindingKey) {
// If the binding handler is already known about then return it now
var handler = originalGetBindingHandler(bindingKey);
if (handler) {
return handler;
}
if (bindingKey.startsWith("dl-")) {
bindingKey = bindingKey.replace("dl-", "");
if (ko.bindingHandlers[bindingKey]) {
return ko.bindingHandlers[bindingKey];
}
// Work out the URL at which the binding handler should be loaded
var url = customBindingUrl(bindingKey);
// Load the binding from the URL
var loading = $.getScript(url);
return ko.bindingHandlers["dl-" + bindingKey] = {
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
// Once the binding is loaded, apply it to the element
loading.done(function() {
var binding = {};
binding[bindingKey] = valueAccessor;
ko.applyBindingAccessorsToNode(element, binding);
});
// Assumes that all dynamically loaded bindings will want to control descendant bindings
return { controlsDescendantBindings: true };
}
}
}
};
http://jsfiddle.net/mbest/e718a123/
AFAIK: No, there is no generic way to lazily load custom bindings.
There are however a lot of options, but we can not recommend any specific one because they'll heavily depend on context. To summarize a few examples:
If possible you can use those bindings inside components, and lazily load the components;
Depending on what your binding handler does, it can itself delay loading until the latest needed time (e.g. in the init you'll merely register an event callback that will actually load the things you want to load);
If you properly use if bindings, any custom bindings inside of that will not be evaluated until needed. The same for foreach bindings, which will not apply custom bindings for array items unless those items are there.
You can call applyBindings to specific parts of the DOM only when you're ready to do so.
Et cetera. But again, your question borders on being too broad. Create one (or more?) new questions with actual scenario's, tell us why / how you'd need your custom binding to load lazily, and tell us what approaches you've tried and why they didn't work.

Appending function to Custom Event in Prototype JS

I'm working with a 3rd party product where I am extending the UI with my own custom functionality. Within part of that I need to call an event after the UI has been updated with an AJAX call. Luckily the app fires a call to a Custom Event using the Prototype JS library after the call is complete, like this:
$(document.body).fire("ns:customevent");
If I add my own custom event with the same name then this works as expected
$(document).observe("ns:customevent", function(event) {
//do custom stuff here
});
[I could not get $(document.body).observe() to work here but I don't think that really matters.]
My concern here is that there may be other parts of the app that have registered functions against that event, and I am (blindly) overwriting them with my own, which will lead to issues further down the line.
Does Prototype append custom functions even though they have the same name or does it in fact overwrite them? If it does overwrite them then how can I append my function to anything that is already existing? Is there anyway of viewing what
I would imagine something like this, but I hardly know Protoype and my JS is very rusty these days.
var ExistingCustomEvent = $(document.body).Events["ns:customevent"];
$(document).observe("ns:customevent", function(event) {
ExistingCustomEvent();
//do custom stuff here
});
I can't add my event handler or add in code to call my own function, I want to try avoiding the 3rd party library (if that would even be possible).
Thanks.
As an FYI for anyone else that stumbles upon this question, following the comment from Pointy it turns out that Prototype does append the functions to the custom event.
I verified this by trying the following and both alerts fired.
$(document).observe("ns:customevent", function(event) {
alert("ALERT 1");
});
$(document).observe("ns:customevent", function(event) {
alert("ALERT 2");
});
Great :)

jQuery Plugin - Public method - Data only targeting one element

I'm trying to write a plugin that will select multiple elements and then apply some private methods to them (see code below). Then I also want to give the user the ability to trigger the activation of the plugin's methods manually with a .activate() function.
Here is my code :
MARKUP : https://github.com/simonwalsh/jquery.imagepox/blob/master/demo/index.html
JS : https://github.com/simonwalsh/jquery.imagepox/blob/master/dist/jquery.imagepox.js
Basically, when I select multiple items and then try to use the manual activation like so :
$(".pox-wrapper").imagepox({ // NOTE: selects two elements
manualActivation: true
});
var manual = $(".pox-wrapper").data('imagepox');
setTimeout(function(){
manual.activate();
}, 5000);
It will only apply the activate() method to the first element in the query...
This is my first jQuery plugin and I've been able to handle everything so far but I'm not sure about this one or even if it is the right way to effectively call a public method. I also tried using a custom event with an event listener in the plugin but it still only applies the methods on the first element in the page.
Thanks in advance :)
its not your plugin's fault. data does not work like that, it doesnt know how to return data from a collection of elements. Because think about it, each element in the collection contains its own data object!
So when you call data on a collection, it returns the data from the first one. The quick solution would be to change the innards of the setTimeout into a loop over all the elements in the set and call activate on them.
setTimeout(function(){
$(".pox-wrapper").each(function(){
$(this).data('imagepox').activate();
})
}, 5000);
It seems to me that you want to add functions to collections of jquery objects. This is the usecase of a jquery plugin. You can create a lightweight one like this:
$.fn.imagepox.activate = function(){ //do this after you create your plugin!
return this.each(function(){
var $this = $(this);
var data = $this.data('imagepox');
if(data){
data.activate();
}
});
};
now you can call it like this:
$(".pox-wrapper").imagepox.activate()

How to Connect dojo/aspect to a Widget?

I am having trouble figuring out how to use dojo/aspect with widgets.
Consider the following:
require( [ 'dijit/form/Button' ],
function( Button)
{
var myButton = new Button({label: 'Click me!'});
} );
How would I connect to the button's postCreate() or startup() methods to discover when it has been rendered?
There seems to be no point when I can add advice to a method. See the comments, here:
require( [ 'dijit/form/Button', 'dojo/aspect' ],
function( Button, aspect )
{
// I cannot use aspect.after() here as I have no instance yet
var myButton = new Button({label: 'Click me!'});
// ...but I cannot do it here either as the lifecycle has already kicked off
} );
(The button is just to make it easier to explain the issue. My real-world problem involves widgets that contain other widgets, so I need to know when the whole lot have rendered before performing an action).
By instantiating the widget programmatically, the postCreate method of the widget is implicitly being called. As far as I know there isn't an easy (or really a good reason to) to connect to the postCreate stage in the widget lifecycle.
startup, on the other hand you need to call explicitly when programmatically instantiating a widget:
var myButton = new Button({label: 'Click me!'});
aspect.after(myButton, 'startup', function(){
console.log('startup called');
});
//place button in page (since startup implies the widget has been placed in the page
myButton.placeAt(someDomNode)
myButton.startup();
If you want to do work during the postCreate lifecycle of a widget, you'll likely want to subclass that widget. Doing so would look something like this:
//in a file like my/custom/widget.js
define(['dojo/_base/declare','dijit/form/Button'],function(declare,Button){
return declare('my.custom.widget',[Button],{
postCreate:function(){
//we still want to call the parent class's postCreate function
this.inherited(arguments);
//create some other widgets as well
}
});
});

Categories

Resources