I'm trying to detect whether a custom element with a specific name was registered or not. Is there a way to make such check?
Or is there a way to get list of registered custom elements?
I do document.registerElement, but what else is there? Is it one-way API?
There is a way to check whether an element was registered. Registered elements have their own constructors, while unregistered ones would use plain HTMLElement() for constructor (or HTMLUnknownElement() whether the name is not valid, but this is out of scope of the question):
document.registerElement('x-my-element');
document.createElement('x-my-element').constructor
//⇒ function x-my-element() { [native code] }
document.createElement('x-my-element-not-registered').constructor
//⇒ function HTMLElement() { [native code] }
That said, the checker might look like:
var isRegistered = function(name) {
return document.createElement(name).constructor !== HTMLElement;
}
Or, with syntactic sugar:
String.prototype.isRegistered = function() {
return document.createElement(this).constructor !== HTMLElement;
}
'x-my-element'.isRegistered()
//⇒ true
'xx-my-element'.isRegistered()
//⇒ false
The mostly careful version:
String.prototype.wasRegistered = function() {
switch(document.createElement(this).constructor) {
case HTMLElement: return false;
case HTMLUnknownElement: return undefined;
}
return true;
}
'x-my-element'.wasRegistered()
//⇒ true
'xx-my-element'.wasRegistered()
//⇒ false
'xx'.wasRegistered()
//⇒ undefined
There is no way to access a list of registered elements, AFAIK.
BTW, I still think that the try-catched registration (as proposed by #stephan-muller) suits your needs better.
Since custom elements is now part of the latest standard, I thought I'd share how to do this in 2017+:
Note: the document.registerElement function has been deprecated in favor of customElements.define().
customElements is defined as a global in window. There are three methods defined:
define
get
whenDefined
get is the important one here. get takes a string of the element name and returns the constructor for the named custom element, or undefined if there is no custom element definition for the name.
So in 2017+ to check if an element has been registered do you:
const myElementExists = !!customElements.get('my-element');
I'm not sure if there's a way to get a list of defined elements however.
NOTE: this doesn't work in IE. See here for browser compatibility
EDIT (2021): This no longer works as /deep/ was removed a few years ago.
Combining a few of the above approaches you can iterate over everything in use and spit out a unique list of custom (and registered) elements:
function isRegistered(name) {
return document.createElement(name).constructor.__proto__ !== window.HTMLElement;
}
var allElems = document.querySelectorAll('html /deep/ *');
var nodeNames = [].map.call(allElems, el => el.nodeName.toLowerCase())
.filter((value, index, self) => self.indexOf(value) === index)
console.log('all elements', nodeNames);
console.log('registered, custom elements', nodeNames.filter(isRegistered))
There doesn't seem to a way to see all registered elements at the moment, but there is a way to check whether or not an element has been registered already: wrap the register in a try...catch block:
try {
document.registerElement('x-my-element');
} catch(e) {
console.log('already exists', e);
}
Run this twice in your console and you'll see the error logged.
This does have a drawback if you simply want to check whether or not it was registered though: if it was not, it will be after running this. There also isn't a way to unregister an element it seems.
While I am not sure it applies to other Web Component frameworks, while using Polymer in Chrome, I have a CustomElements object to the window object. The CustomElements object has a key/value collection of all registered custom elements called registry.
function isRegistered(name) {
if (window.CustomElements && window.CustomElements.registry)
return name in window.CustomElements.registry;
return undefined;
}
As written already on the Polymer's Slack channel, this is a dirty one that can make the job:
function isElementRegistered(elementId) {
return Polymer.telemetry.registrations.find(function(item) { return item.is === elementId })
}
Not sure how much Polumer.telemetry.registrations is reliable though (haven't seen it on the doc) and Array.prototype.find is not cross-browser!
Here is one way you get get a list of all the registered customElements using ES6
// Get all elements
const elements = document.querySelectorAll('*')
// Create an array from elements
const elementArray = Array.from(elements)
// Map to node names
const nodeNames = elementArray.map(element => element.nodeName.toLowerCase())
// Filter by which ones are registered
const allCustomElementNames = nodeNames.filter(customElements.get.bind(customElements))
in scenarios where custom element classes (constructors) self-register an element, it is sufficient to check for the presence of the class
I was looking for a list of all registered custom elements. Based on answers here, came up with something like this:
let original = customElements.define;
window.customTags = [];
customElements.define = function () {
console.log("custom elements hijacked", arguments);
window.customTags.push({ tag: arguments[0], ctor: arguments[1] })
original.apply(customElements, arguments);
}
setTimeout(() => {
console.log(window.customTags);
}, 1000)
Related
I need to develop a pure javascript plugin wich can be accessed like jquery typed plugins ( $('.pluginWrapper').pluginInit();
However i need to to use pure javascript and i was thinking maybe about 2 supported formats:
document.getElementById('pluginWrapper').pluginInit();
pluginInit(document.getElementById('pluginWrapper'));
I know that you have to do an IIFE to wrap it and call it via object methods but i do not know how i can bind that to an element.
I am mentioning that i am a begginer so please can someone please explain something about this. Cheers!
You would be safer developing a plugin interface that simply exposes plugins as functions which take an element as an argument.
function MyPlugin(element) {
// do stuff with element
element.setAttribute('data-plugin-id', 'pluginName');
}
The other approach involves extending Element.prototype. Which could potentially be a dangerous action in production software.
However, it is still possible.
Element.prototype.pluginInit = function() {
// the element is accessible as `this`
this.setAttribute('data-plugin-id', 'pluginName');
}
A function is easy for everyone to understand. Plugin writers don't have to understand any interfaces for creating and registering plugins, they just need to know that they should write functions that take elements as arguments.
There's a great talk from Rich Hickey (the creator of Clojure) called Simplicity Matters in which he stresses that the worst thing you can do is add additional complexity when simple solutions will do.
In this case, you don't need anything more complex than a function which takes an element as an argument.
If it is completely essential that you have control of the function, you could write a simple interface for registering and initiating plugins.
function Plugin(element) {
if(element === null) {
throw new TypeError("Element must not be null!");
}
// get all the plugin names from the store
var pluginNames = Object.keys(Plugin.store);
// make sure `this` is set to the element for each plugin
var availablePlugins = pluginNames.reduce(function(plugins, name) {
plugins[name] = Plugin.store[name].bind(element);
return plugins;
}, {});
// return an object containing all plugins
return availablePlugins;
}
// we'll store the plugins in this object
Plugin.store = {};
// we can register new plugins with this method
Plugin.register = function(name, pluginFn) {
Plugin.store[name] = pluginFn;
};
Which you could use like this.
Plugin.register('myPlugin', function() {
this.setAttribute('data-plugin-id', 'myPlugin');
});
Plugin(document.getElementById('pluginWrapper')).myPlugin();
If you want the plugin function to take a selector, the same way as jQuery, then you can use document.querySelectorAll inside your definition for Plugin.
function Plugin(selector) {
var element = document.querySelectorAll(selector);
if(element === null) {
throw new TypeError("Element must not be null!");
}
// get all the plugin names from the store
var pluginNames = Object.keys(Plugin.store);
// make sure `this` is set to the element for each plugin
var availablePlugins = pluginNames.reduce(function(plugins, name) {
plugins[name] = Plugin.store[name].bind(element);
return plugins;
}, {});
// return an object containing all plugins
return availablePlugins;
}
Then you would use it like this instead.
Plugin.register('myPlugin', function() {
this.setAttribute('data-plugin-id', 'myPlugin');
});
Plugin('#pluginWrapper').myPlugin();
'use strict';
var Controller = function Controller() {};
Controller.init = function() {
if (!Controller.PROPERTIE.element || !Controller.PROPERTIE.indicator) {
return 'Expected to find [data-controller] and [data-controller-to]';
}
Controller._start();
};
Controller.PROPERTIE = {
element: $('[data-controller]'),
indicator: $('[data-controller-to]'),
state: false
};
Controller._start = function() {
Controller.PROPERTIE.indicator.bind('click', Controller._toggle);
};
Controller._toggle = function() {
Controller.PROPERTIE.element
.animate({
bottom: (Controller.PROPERTIE.state = !Controller.PROPERTIE.state) ? '-110' : '0'
});
};
apparently the elements in the object property does not exist, but they do exist ! Could someone tell me if I can not use javascript like that?
Maybe there is something with hoisting that is breaking the script?
I already try put the object before the init and the result is the same.
I know that i can extend prototype, but i have my reasons to use like this.
Thanks.
I can think of three possible causes:
The code is being initialized before the DOM has been parsed.
Your HTML does not actually contain elements that will match these selectors.
The desired elements are being created dynamically after this code is being initialized.
In this declaration:
Controller.PROPERTIE = {
element: $('[data-controller]'),
indicator: $('[data-controller-to]'),
state: false
};
the two jQuery expressions will be evaluated immediately as this code is parsed. If this code is not placed at the very end of the body, then those elements will likely not exist yet.
The usual solution to this is to not initialize those elements in a static declaration, but to initialize them by calling a function after the page has been loaded.
If you really intend for them to be globals, then you could just do this:
$(document).ready(function() {
Controller.PROPERTIE = {
element: $('[data-controller]'),
indicator: $('[data-controller-to]'),
state: false
};
});
But, you will also have to make sure that you don't try to use them until after this code runs.
The only other possible cause I can think of is that your DOM does not contain elements that match your two selectors. It seems like it's likely one of these two issues. Since you haven't shown us either the overall page structure of the HTML you intend to match, it's impossible for us to anything other than tell you what causes it might be.
I have a feeling you are thinking the jQuery object is not defined because of this:
if (!Controller.PROPERTIE.element || !Controller.PROPERTIE.indicator) {
return 'Expected to find [data-controller] and [data-controller-to]';
}
You must remember that jQuery constructor $() will always return an object. If you really want to test if there's a selected item you must use the length property of the jQuery object.
if (!Controller.PROPERTIE.element.length || !Controller.PROPERTIE.indicator.length) {
//element or indicator not there.
return 'Expected to find [data-controller] and [data-controller-to]';
}
In Ruby I think you can call a method that hasn't been defined and yet capture the name of the method called and do processing of this method at runtime.
Can Javascript do the same kind of thing ?
method_missing does not fit well with JavaScript for the same reason it does not exist in Python: in both languages, methods are just attributes that happen to be functions; and objects often have public attributes that are not callable. Contrast with Ruby, where the public interface of an object is 100% methods.
What is needed in JavaScript is a hook to catch access to missing attributes, whether they are methods or not. Python has it: see the __getattr__ special method.
The __noSuchMethod__ proposal by Mozilla introduced yet another inconsistency in a language riddled with them.
The way forward for JavaScript is the Proxy mechanism (also in ECMAscript Harmony), which is closer to the Python protocol for customizing attribute access than to Ruby's method_missing.
The ruby feature that you are explaining is called "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.
It's a brand new feature that is present only in some browsers like Firefox (in the spider monkey Javascript engine). In SpiderMonkey it's called "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
Please read this article from Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ for more details about the upcoming implementation.
Not at the moment, no. There is a proposal for ECMAScript Harmony, called proxies, which implements a similar (actually, much more powerful) feature, but ECMAScript Harmony isn't out yet and probably won't be for a couple of years.
You can use the Proxy class.
var myObj = {
someAttr: 'foo'
};
var p = new Proxy(myObj, {
get: function (target, methodOrAttributeName) {
// target is the first argument passed into new Proxy, aka. target is myObj
// First give the target a chance to handle it
if (Object.keys(target).indexOf(methodOrAttributeName) !== -1) {
return target[methodOrAttributeName];
}
// If the target did not have the method/attribute return whatever we want
// Explicitly handle certain cases
if (methodOrAttributeName === 'specialPants') {
return 'trousers';
}
// return our generic method_missing function
return function () {
// Use the special "arguments" object to access a variable number arguments
return 'For show, myObj.someAttr="' + target.someAttr + '" and "'
+ methodOrAttributeName + '" called with: ['
+ Array.prototype.slice.call(arguments).join(',') + ']';
}
}
});
console.log(p.specialPants);
// outputs: trousers
console.log(p.unknownMethod('hi', 'bye', 'ok'));
// outputs:
// For show, myObj.someAttr="foo" and "unknownMethod" called with: [hi,bye,ok]
About
You would use p in place of myObj.
You should be careful with get because it intercepts all attribute requests of p. So, p.specialPants() would result in an error because specialPants returns a string and not a function.
What's really going on with unknownMethod is equivalent to the following:
var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
This works because functions are objects in javascript.
Bonus
If you know the number of arguments you expect, you can declare them as normal in the returned function.
eg:
...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
I've created a library for javascript that let you use method_missing in javascript: https://github.com/ramadis/unmiss
It uses ES6 Proxies to work. Here is an example using ES6 Class inheritance. However you can also use decorators to achieve the same results.
import { MethodMissingClass } from 'unmiss'
class Example extends MethodMissingClass {
methodMissing(name, ...args) {
console.log(`Method ${name} was called with arguments: ${args.join(' ')}`);
}
}
const instance = new Example;
instance.what('is', 'this');
> Method what was called with arguments: is this
No, there is no metaprogramming capability in javascript directly analogous to ruby's method_missing hook. The interpreter simply raises an Error which the calling code can catch but cannot be detected by the object being accessed. There are some answers here about defining functions at run time, but that's not the same thing. You can do lots of metaprogramming, changing specific instances of objects, defining functions, doing functional things like memoizing and decorators. But there's no dynamic metaprogramming of missing functions as there is in ruby or python.
I came to this question because I was looking for a way to fall through to another object if the method wasn't present on the first object. It's not quite as flexible as what your asking - for instance if a method is missing from both then it will fail.
I was thinking of doing this for a little library I've got that helps configure extjs objects in a way that also makes them more testable. I had seperate calls to actually get hold of the objects for interaction and thought this might be a nice way of sticking those calls together by effectively returning an augmented type
I can think of two ways of doing this:
Prototypes
You can do this using prototypes - as stuff falls through to the prototype if it isn't on the actual object. It seems like this wouldn't work if the set of functions you want drop through to use the this keyword - obviously your object wont know or care about stuff that the other one knows about.
If its all your own code and you aren't using this and constructors ... which is a good idea for lots of reasons then you can do it like this:
var makeHorse = function () {
var neigh = "neigh";
return {
doTheNoise: function () {
return neigh + " is all im saying"
},
setNeigh: function (newNoise) {
neigh = newNoise;
}
}
};
var createSomething = function (fallThrough) {
var constructor = function () {};
constructor.prototype = fallThrough;
var instance = new constructor();
instance.someMethod = function () {
console.log("aaaaa");
};
instance.callTheOther = function () {
var theNoise = instance.doTheNoise();
console.log(theNoise);
};
return instance;
};
var firstHorse = makeHorse();
var secondHorse = makeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
This doesn't work for my use case as the extjs guys have not only mistakenly used 'this' they've also built a whole crazy classical inheritance type system on the principal of using prototypes and 'this'.
This is actually the first time I've used prototypes/constructors and I was slightly baffled that you can't just set the prototype - you also have to use a constructor. There is a magic field in objects (at least in firefox) call __proto which is basically the real prototype. it seems the actual prototype field is only used at construction time... how confusing!
Copying methods
This method is probably more expensive but seems more elegant to me and will also work on code that is using this (eg so you can use it to wrap library objects). It will also work on stuff written using the functional/closure style aswell - I've just illustrated it with this/constructors to show it works with stuff like that.
Here's the mods:
//this is now a constructor
var MakeHorse = function () {
this.neigh = "neigh";
};
MakeHorse.prototype.doTheNoise = function () {
return this.neigh + " is all im saying"
};
MakeHorse.prototype.setNeigh = function (newNoise) {
this.neigh = newNoise;
};
var createSomething = function (fallThrough) {
var instance = {
someMethod : function () {
console.log("aaaaa");
},
callTheOther : function () {
//note this has had to change to directly call the fallThrough object
var theNoise = fallThrough.doTheNoise();
console.log(theNoise);
}
};
//copy stuff over but not if it already exists
for (var propertyName in fallThrough)
if (!instance.hasOwnProperty(propertyName))
instance[propertyName] = fallThrough[propertyName];
return instance;
};
var firstHorse = new MakeHorse();
var secondHorse = new MakeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
I was actually anticipating having to use bind in there somewhere but it appears not to be necessary.
Not to my knowledge, but you can simulate it by initializing the function to null at first and then replacing the implementation later.
var foo = null;
var bar = function() { alert(foo()); } // Appear to use foo before definition
// ...
foo = function() { return "ABC"; } /* Define the function */
bar(); /* Alert box pops up with "ABC" */
This trick is similar to a C# trick for implementing recursive lambdas, as described here.
The only downside is that if you do use foo before it's defined, you'll get an error for trying to call null as though it were a function, rather than a more descriptive error message. But you would expect to get some error message for using a function before it's defined.
It seems that kendo's unobtrusive-javascript style event calls break this in my method context.
Say I have an object Foo, instantiated as bar = new Foo()
function Foo(){};
Foo.prototype.name = "Herring";
Foo.prototype.doSomething = function(e) {
alert(this.name);
};
bar = new Foo();
And attach the event using data-click for example
<a data-role="button" data-click="bar.doSomething">Click Me</a>
Object context to bar is replaced (not sure why, since we have the convenient element container.) and so this.name is undefined.
I've tried the old var self = this; in the object constructor, but it's not working, does anyone know what the best way to solve this is?
Update : Hacky Workaround
Since I really don't want to lose the benefits of wrapping up my modules as classes, I've created event call functions wrappers, which then call the methods on the appropriate object.
For example, connect the markup to a wrapper-function.
<a data-role="button" data-click="doSomething">Click Me</a>
and the wrapper function just calls the object.method.
function doSomething(e){ bar.doSomething(e) };
Now, this achieves the intended result, but it's quite horrible, each and every event called from markup must have a proxy function like the one above. So just imagine a scenario where you have 300 events... and you'll instantly see why this is horrible.
If there's no other solution, and I dearly hope there is. I'll post this workaround as an answer, but as far as I'm concerned, it's far from desirable.
Footnote
I'll be completely honest this seems like major architectural flaw in Kendo, since this method of calling events from markup is "the kendo way." Obviously it can't be patched out, because there's probably a fair bit of code already dealing with this as a reference to the html element.
Being able to override it, or being able to route these event calls through a generic handler which can pass the call on, essentially a generic proxy function, are possible ways this could be dealt with. It could also be a simple configurable value on the kendo. object.
Theoretical Solution
I'll post follow-up if this works, in theory it's possible to throw events at a generic proxy, and have it call the properly scoped function.
Say we use the event attribute to call the proxy and then create a separate attribute to convey the object/method call. For example.
<a data-role="button" data-click="prox" data-prox="o.eventHandler">Click Me</a>
The proxy function would pull prox from the attribute dataset:
method - using eval
Not because I'm evil, but needs must.
// sitting in global namespace
function prox(e){
var p = e.sender.element.data['prox'];
// make sure our delegate is a function.
if("function" == eval("typeof "+p)) {
eval(p + "(e)");
}
}
Obviously I'd like a better way to do this but, at least it's DRY.
(I'll cook a non-eval method in a moment...)
Begone Eval...
let's use the window context to locate the object / method.
function prox(e) {
var p = e.sender.element.data['prox'];
if(p.indexOf(".") == -1){
// global function : where *this* is window.
// check you've got the function if not ditch it.
if("function" == typeof window[p]) window[p](e);
} else {
// object/method (one level deep only.)
var s = p.split(".");
var o = s[0], m = s[1];
// check the object/method is a function before executing it.
if("function" == typeof window[o][p]) window[o][p](e);
}
}
Of course for global (window) scoped functions, this as the element is probably more useful, but in that case, you have a choice, I'd leave out the
version in use.
// dynamic proxy for retaining object context on methods called by
// data- attributes in Kendo.
//
// e.g.
//
// data-click="o.method"
//
// Would lose context with `o` - context would be set in the same
// way as JQuery handlers, which is an inconvenience.
//
// Alternatively we use the prox method
//
// data-click="prox"
//
// We'd then set `data-prox` on the same element, to the
// object.method pair.
//
// data-prox="o.method"
//
// This is read by prox, transformed into a method call, type
// checked and executed if it's a valid method.
//
// A `data-prox` value in any form other than `object.method` will
// be ignored, for example, `object.child.method` will fail. If
// you're doing that sort of thing, feel free to hack it.
//
// There's a backup eval() to locate the object if window doesn't
// own it. It should be possible to remove it under most
// circumstances, it's here for compatability with
// JSFiddle. (JSBin works without it.)
function prox(e) {
var p = this.element.data().prox;
if(p.indexOf(".") > -1){
var s = p.split("."); if(s.length > 2) return;
var o = s[0], m = s[1];
if("object" == typeof window[o]) {
o = window[o];
}
if("function" == typeof o[m]) o[m](e);
// comment this out in production:
l( "prox called " + s[0] + "::" + s[1] );
}
}
function l(s) { console.log(s); }
Caveats
If you have multiple handlers on the same element, prox() is unsuitable, for example, if you have data-init, data-show, etc. prox cannot differentiate, and will fail.
I'll probably update this, especially if this becomes a prevalent use-case for me.
I temporarily tried a third method, with a non-generic technique, which works like this.
Pseudo code:
MyObject {
method : function(e) {
if (this instanceof MyObject) {
// Do something with this
} else {
myInstance.method(e); // otherwise re-call the method to set this properly.
}
}
}
myInstance = new MyObject();
Not as flexible as the prox method, but suitable for my use case, and at least doesn't require a separate function proxy away from the method we want to use. We could make this more terse by doing the type check & re-call up front.
e.g.
MyObject = {
method : function(e) {
if (! this instanceof MyObject) myInstance.method(e); // re-call
// Method body...
}
}
myInstance = new MyObject();
It also meant I didn't need custom data- attributes in my markup.
Note: this method is problematic for objects which will have multiple instances, however, the objects I was applying to were single instances.
If you have handlers which need to be instance specific (which is the main reason I raised this question) the prox method is a much better fit than this, which is just a neater way of doing one-per-event proxy functions.
You may use jQuery Proxy (http://api.jquery.com/jQuery.proxy/).
function Foo(){};
Foo.prototype.name = "Herring";
Foo.prototype.doSomething = function(e) {
alert(this.name);
};
bar = new Foo();
$("btn").click($.proxy(bar.doSomething), bar);
or for inside using
$("btn").click($.proxy(this.doSomething), this);
I developed a proxy method using the JS Proxy Polyfill that simplify calling custom logic via parameters in a custon html data-* attribute.
Include https://raw.githubusercontent.com/GoogleChrome/proxy-polyfill/master/proxy.js
function makeGridTemplateEventProxy(o) {
return new Proxy(o, {
get(target, eventName) {
return function (options) {
return templateEventProxy(options, eventName);
}
}
});
}
templateEventProxy: function (options, attribute) {
if (!options.sender.element.attr('data-proxy-' + attribute)) {
throw new Error('Cannot find attribute data-proxy-' + attribute + ' on ' + options.sender.name + ' widget');
}
var proxyParams = JSON.parse(options.sender.element.attr('data-proxy-' + attribute));
method = $("#" + proxyParams.id).data(proxyParams.widget).element.data(proxyParams.method);
if (method && typeof method == 'function') {
return $.proxy(method, this)(options);
}
return null;
}
var eventproxy = makeGridTemplateEventProxy({});
for example for upload component
<input type=file ...
data-success="eventproxy.customsuccesshandler"
data-proxy-customsuccesshandler='{widget:"kendoGrid",method:"<myJqueryDataDefinedMethod>",id:"<gridId>"}'
....
/>
substitute myJqueryDataDefinedMethod and gridId with your parameters
as you see you can define in data-success an eventproxy with dynamic name
data-success="eventproxy.CUSTOMKEY"
and after define a custom attribute
data-proxy-CUSTOMKEY
data-proxy-CUSTOMKEY contains parameters ( JSON encoded ) you can use to implement a custom logic,
I suggested custom logic which can retrieve JS method stored on kendo widget grid via $.data
$("#" + proxyParams.id).data(proxyParams.widget).element.data(proxyParams.method)
You can bind method to grid for example with this
$('#my-grid-id').data("kendoGrid").element.data('methodName',function(e){
// my implementation
});
For instance this code:
function stuff() {
this.onlyMethod = function () {
return something;
}
}
// some error is thrown
stuff().nonExistant();
Is there a way to do something like PHP's __call as a fallback from inside the object?
function stuff() {
this.onlyMethod = function () {
return something;
}
// "catcher" function
this.__call__ = function (name, params) {
alert(name + " can't be called.");
}
}
// would then raise the alert "nonExistant can't be called".
stuff().nonExistant();
Maybe I'll explain a bit more what I'm doing.
The object contains another object, which has methods that should be accessible directly through this object. But those methods are different for each object, so I can't just route them, i need to be able to call them dynamically.
I know I could just make the object inside it a property of the main object stuff.obj.existant(), but I'm just wondering if I could avoid it, since the main object is sort of a wrapper that just adds some functionality temporarily (and makes it easier to access the object at the same time).
Well, it seems that with harmony (ES6), there will be a way, and it's more complicated compared to the way other programing languages do it. Basically, it involves using the Proxy built-in object to create a wrapper on the object, and modify the way default behavior its implemented on it:
obj = new Proxy({},
{ get : function(target, prop)
{
if(target[prop] === undefined)
return function() {
console.log('an otherwise undefined function!!');
};
else
return target[prop];
}
});
obj.f() ///'an otherwise undefined function!!'
obj.l = function() {console.log(45);};
obj.l(); ///45
The Proxy will forward all methods not handled by handlers into the normal object. So it will be like if it wasn't there, and from proxy you can modify the target. There are also more handlers, even some to modify the prototype getting, and setters for any property access yes!.
As you would imagine, this isn't supported in all browsers right now, but in Firefox you can play with the Proxy interface quite easy, just go to the MDN docs
It would make me happier if the managed to add some syntactic sugar on this, but anyway, its nice to have this kind of power in an already powerful language. Have a nice day! :)
PD: I didn't copy rosettacode js entry, I updated it.
There is a way to define a generic handler for calls on non-existant methods, but it is non-standard. Checkout the noSuchMethod for Firefox. Will let you route calls to undefined methods dynamically. Seems like v8 is also getting support for it.
To use it, define this method on any object:
var a = {};
a.__noSuchMethod__ = function(name, args) {
console.log("method %s does not exist", name);
};
a.doSomething(); // logs "method doSomething does not exist"
However, if you want a cross-browser method, then simple try-catch blocks if the way to go:
try {
a.doSomething();
}
catch(e) {
// do something
}
If you don't want to write try-catch throughout the code, then you could add a wrapper to the main object through which all function calls are routed.
function main() {
this.call = function(name, args) {
if(this[name] && typeof this[name] == 'function') {
this[name].call(args);
}
else {
// handle non-existant method
}
},
this.a = function() {
alert("a");
}
}
var object = new main();
object.call('a') // alerts "a"
object.call('garbage') // goes into error-handling code
It seems that you know your way around JS.
Unfortunately, I don't know of such feature in the language, and am pretty sure that it does not exist. Your best option, in my opinion is either using a uniform interface and extend it, or extend the prototypes from which your objects inherit (then you can use instanceof before going forward with the method call) or use the somewhat cumbersome '&&' operator in order to avoid the access of nonexistent properties/methods:
obj.methodName && obj.methodName(art1,arg2,...);
You can also extend the Object prototype with Anurag's suggestion ('call').
You can also check if the method exists.
if(a['your_method_that_doesnt_exist']===undefined){
//method doesn't exist
}