I am having a bit of trouble creating an instance of useful things
from another JS file, I would like to change the properties of the
instance and use the methods. Or whatever you suggest if this is not
possible. This extends and this construct are confusing me.
(function($) {
$.extend({
usefulThings: new function() {
this.defaults = {
prop_path:'/la/lala',
//OTHER STUFF
};
this.construct = function(options) {
return this.each(function() {
var setting;
this.settings = {};
settings = $.extend(this.settings, $.usefulThings.defaults, options);
$this = $(this);
//OTHER STUFF
$.data(this, "usefulThings",settings);
// FIRST METHOD CALL
//OTHER STUFF
});
};
//SOME METHODS
}
});
$.fn.extend({
usefulThings: $.usefulThings.construct
});
})(jQuery);
I have seen usefulThings called from script blocks like so:
$("#myDivName").usefulThings({
prop_path: '/la/lala' //these seems to get overwritten in the above
});
First, have a look at MDN's introduction to the this keyword. And in the jQuery docs for .each.
Next, check out how does jquery chaining work? to understand the construct method.
Then, you should notice that the use of the new keyword is absolutely inappropriate in this case. The expression should be replaced by a simple object literal:
{
defaults: {
prop_path:'/la/lala',
// …
},
construct: function(options) {
…
},
//SOME METHODS
}
Now, the jQuery.extend. The relevant sentence from the docs is
If only one argument is supplied to $.extend(), this means the target argument was omitted. In this case, the jQuery object itself is assumed to be the target. By doing this, you can add new functions to the jQuery namespace. This can be useful for plugin authors wishing to add new methods to JQuery.
And it's the same for $.fn.extend (which actually is === $.extend), it extends the jQuery.fn object which is a shortcut for jQuery's prototype object.
Related
I understand I can extend jQuery in two forms:
1)
jQuery.fn.extend({
variation1: function(){
...
}
});
I use this like $(selector).variation1();
2)
and custom function
jQuery.variation2 = {
something: function(){
var execute = function(){
...
}
}
}
I use this like $.variation2.something()
My questions are:
In the first case, I was unable to call functions without a selector; this code caused errors $.variation1(); Is this correct? meaning, is there a way to call these functions without an element selector?
In the second case, how could I initialize variation2 with options? The reason I ask this is because in MooTools, when creating a class, we can initialize the class like:
var jsClass = new Class({
Implements: [Options],
options: {
},
initialize: function(options){
this.setOptions(options);
}
});
we call it, and initialize with custom options like
new jsClass({option1: this, option2: that})
So in the MooTools example the class instance executes options by default. Going back to the question, is there a way to do similar in jQuery?
Thanks in advance
The two variations are distinct as you have noticed. jQuery.fn provides the set of functions that are applied to a jQuery QuerySet object (the result of applying a jQuery selector). jQuery.* could simply be thought of as global static objects, including (sometimes) functions that are accessible to anyone who has access to the jQuery object.
To initialize variation 2, I would do something like:
jQuery.variation2 = (function() {
var a = ...;
var b = ...; // other initialization
return {
something: function() {
...
}, ...
};
})();
You could use closures in similar ways to provide initialized code.
Update: I've updated the question to state that the code in question is for a subclass in Google Closure.
I'm looking through some JavaScript code that defines a subclass and saw that an object literal is being used to set the prototype. Note that this code is using Google Closure Library and will be compiled in advanced mode with Google Closure Compiler. (Not sure if this matters for this question.) The code looks like this:
company.app.MyClass = function(param) {
this.field = 0;
};
company.app.MyClass.prototype = {
function1: function() {
//Do stuff.
},
function2: function() {
//Do more stuff.
};
};
company.app.MySubClass = function(param) {
company.app.MyClass.call(this, param);
};
company.app.MySubClass.prototype = {
function3: function() {
//Do stuff.
},
function4: function() {
//Do more stuff.
};
};
goog.inherits(company.app.MySubClass, company.app.MyClass);
All of the samples I have seen for creating classes with Google Closure add fields and functions to the prototype instead of setting it to an entirely new object with an Object literal. So the code for MySubClass would look like this:
company.app.MySubClass.prototype.function3 = function() {
//Do stuff.
};
company.app.MySubClass.prototype.function4 = function() {
//Do more stuff.
};
I don't know exactly what is happening when goog.inherit is called, but I was wondering if setting the prototype of the sub class to a new object literal could cause problems with the inheritance of the base class, MyClass?
I don't know exactly what is happening when goog.inherit is called
Check the code of goog.inherits then. It should look familiar if you know the common Object.create shim and what it does.
As you can see, it completely overwrites the childCtor.prototype, not preserving any old properties on it - which can be problematic and indeed trahes the function3 and function4 methods you previously defined on it in your example. So you will need to inherit first, then extend the new prototype object.
If you still want to use an object literal, that is no problem:
company.app.MySubClass = function(param) {
company.app.MyClass.call(this, param);
};
goog.inherits(company.app.MySubClass, company.app.MyClass);
goog.mixin(company.app.MySubClass.prototype, {
function3: function() {
//Do stuff.
},
function4: function() {
//Do more stuff.
}
});
I've been reading alot about the advantages of using the jQuery.Widget Factory for my plugins. One of the capabilities touted is how the jQuery.widget creates a namespeace for your widget. This is attractive, as I can maintain my current namespacing (naturally) within the jQuery context.
THE PROBLEM:
I keep getting "$(".myWidget").namespace.newWay is not a function" error.
For the following element...
<div class="myWidget"></div>
THIS EXAMPLES CODE WORKS:
While nice...this is NOT what I am trying to achieve...as I still want my namespace to be honored.
var workingVersion = {
_init: function () { /* Do Something*/ }
};
$.widget("ui.workingVersion", workingVersion);
$(document).ready(function () {
$('.myWidget').workingVersion();
});
HOWEVER, MY CODE FAILS:
var namespace = namespace || { };
;namespace.newWay = (function ($, window, document, undefined) {
return function (options) {
var self = this;
this._create = function () {
// Do something
},
this._init = function() {
// Do something
},
this.publicFunction = function () {
// Do something
};
};
})(jQuery, window, document);
$(document).ready(function () {
$.widget("ui.namespace.newWay", namespace.newWay); //<-- Namespace does get appended
$('.myWidget').namespace.newWay({ type: 'testing' }); //<-- But still fails here
});
MY QUESTION IS:
Why does it fail?
RELATED READING:
Understanding jQuery UI widgets: A tutorial
Tips for Developing jQuery UI 1.8 Widgets
This answer comes a bit late, maybe, but I was struggling with the same thing and had to do some reading.
$() in General
The $() instance is a no-namespace shortcut list to different functions spread out over different namespaces. You can add more functions to this shortcut list by writing:
$.fn.myFunction = function() {
...
};
Which then will be accessed by calling
$("...").myFunction();
If you add two functions by the same name the latter one will overwrite the first one.
Widgets
When you create a widget with a namespace it's created with it's namespace, as you would expect, but to $() it's added without it's namespace, as a simplified shortcut to your real widget-with-shortcut. This is done using $.widget.bridge(). You can therefore create your own namespaced link to $() by writing like this:
$.widget.bridge("namespace_myFunction", $.namespace.myFunction );
And then access your widget like this:
$("#myDiv").namespace_myFunction();
To use a widget directly with it's original namespace you can instead invoke it like this:
$.namespace.myWidget(
{
option1: "",
option2: ""
},
$("#div")
);
Hope this will clarify a little...
I've sort if fell into this organization of javascript and was wondering if I'm missing the point somewhere here, or if there's a more elegant way of doing this.
Basically I'm wrapping everything in a function (object) and then setting up methods on that object, then instantiating an instance of the wrapper object and passing in any options and dependencies.
I have a hunch there's a way to automatically run .init() and a few other tweaks that could be made. Am I doing it right?
function AppModuleCore(){
var AppModuleCore = this; //keep internals sane
// Various global vars, objects
AppModuleCore.defaultOptions = {};
AppModuleCore.init = function(opts) {
// todo: that thing where you extend an options object a la juery
AppModuleCore.bindEvents();
};
AppModuleCore.bindEvents = function() {
// bind events here, send to functions within AppModuleCore.<FUNCTIONNAME>();
// Example:
$("a#clicker").unbind("click");
$("a#clicker").click(function(event){
AppModuleCore.handleClickerClick(event);
});
};
AppModuleCore.handleClickerClick = function(event){
alert("clicker was clicked");
};
}
// --------------------------------------------------------------------
// instantiate AppModuleCore object and initialize with opts,
// dependency injection
// --------------------------------------------------------------------
$(document).ready(function(){
AppModuleCore = new AppModuleCore;
var options = {};
AppModuleCore.init(options);
});
OK, some points
Having your code wrapped in a constructor only really makes sense if
You're going to instantiate more than one
You have "public" methods on the object that you are going to call
Your code doesn't exhibit these characteristics. I say this because your jQuery selectors a#clicker are hard coded so I'm assuming that you wouldn't want to bind the same events to them more than once?
You'd be better off using a function (perhaps your init) or an object literal to limit your scope..
function init( options ) {
var defaultsOptions = {};
var privateVar = 'only in this scope';
//extend your default options with options here
//using jquery
options = $.extend( defaultOptions, options );
// this function is completely private to this scope
function privatefunction() {
//do stuff
}
function handleClickerClick( event ){
alert("clicker was clicked");
}
// you don't need to wrap your handler in an anonymous function unless
// you're doing some work to the event before forwarding:- just give a
// reference to your handler
// the handler has access to other members of this scope, we're in a closure
$(options.selector).click( handleClickerClick );
//etc
}
init( {selector: 'a#clicker'} );
On a stylistic note: when you alias this with the same name as the constructor and then add methods to the alias, it looks at first glance like you are adding static methods to the constructor. This may be confusing to someone who looks at your code later and doesn't notice the alias.
function C() {
// a static method i.e a property of the constructor, C not objects created with it
// it is a bit wierd that it is defined in the constructor but not unheard of
C.staticMethod = function(){};
//quite plainly a method of objects of this type, easy to understand
this.method = function(){};
}
Almost all of the examples in the jQuery tutorials that I've read, usually use one major public function for their selecting plugin. When I say 'selecting' plugin, I mean one that is not simply a static function extended onto jQuery.
For example:
(function($) {
jQuery.fn.actionList = function(options) {
var opts = $.extend({}, $.fn.actionList.defaults, options);
return this.each(function(){
alert(this);
});
};
$.fn.actionList.defaults = {
listHtml: '<div>Set the list html</div>'
};
})(jQuery);
but not:
jQuery.log = function(message) {
if(window.console) {
console.debug(message);
} else {
alert(message);
}
};
This works fine for most things, but what I would like to do is be able to call a second function on the object returned from the first call.
var actionBox = $('actionBox').actionList(options);
//Many light-cycles later
actionBox.refreshData(data);
or maybe even:
$('actionBox').actionList(options);
// laaateerr
$('actionBox').actionList.refreshData(data);
I'm guessing one or both of these is not possible or, at least not advisable, but I'm only now getting into the deepest aspects of jQuery and javascript.
Could someone explain how to do this, or if it's not possible or advisable, why? and what they would do instead?
Thanks for reading!
I'm not quite sure what you're getting at, but you can call a second function on the object returned from the first function - in fact, it is very much encouraged to return a jQuery object from your plugins, and the reason why you can chain commands in jQuery.
Using your examples
var actionBox = $('actionBox').actionList(options);
//Many light-cycles later
actionBox.refreshData(data);
would work fine, so long as both .actionList() and .refreshData(data) commands both return a jQuery object.
And
$('actionBox').actionList.refreshData(data);
would need to be
$('actionBox').actionList().refreshData(data);
EDIT:
Looking at the jQuery source code,
jQuery.fn = jQuery.prototype = {
/*
Load of 'property' functions of jQuery object...
*/
}
so, adding properties (a.k.a plugins) to jQuery.fn extends the prototype of the jQuery object. When you call
$(selector, context);
a new jQuery object is returned, using the init property function of the jQuery object
jQuery = window.jQuery = window.$ = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
I think I've got a plugin that might be very useful for you. It allows you to apply any constructor/object to jQuery as it's own namespace AND you can use 'this' as you would normally with jQuery to refer to the object set. Using this[methodName] will call a method on your object, etc.
http://code.google.com/p/jquery-plugin-dev/source/browse/trunk/jquery.plugin.js
Some code samples are here:
http://groups.google.com/group/jquery-dev/browse_thread/thread/664cb89b43ccb92c/34f74665423f73c9?lnk=gst&q=structure+plugin+authoring#34f74665423f73c9
It's about halfway down the page.
I hope you find it useful!