I'm delving into writing plugins for jQuery and I'm trying to understand the distinction between $.f and $.fn.f
I've seen pluggin authors use both, or sometimes assign $.f = $.fn.f
Can someone explain this to me, reasoning, benefits, etc?
Looking at the jQuery source code will clear things up. By the way, jQuery and $ refer to the same object, and this is how the jQuery object is defined:
var jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context );
}
jQuery is a function and in Javascript, a function is also an object of the type Function. So jQuery.f or $.f attaches f to the jQuery Function object, or call it the jQuery class if you will.
if you look at jQuery's source, you'll see that jQuery.prototype has been assigned to jQuery.fn
jQuery.fn = jQuery.prototype
So, whenever you attach a method (or property) to jQuery.fn, as in jQuery.fn.f = .., or $.fn.f = .., or jQuery.prototype.f = .., or $.prototype.f = .., that method will be available to all instances of this jQuery class (again, there are no classes in Javascript, but it may help in understanding).
Whenever you invoke the jQuery() function, as in jQuery("#someID"), a new jQuery instance is created on this line:
return new jQuery.fn.init( selector, context );
and this instance has all the methods we attached to the prototype, but not the methods that were attached directly to the Function object.
You will get an exception if you try calling a function that wasn't defined at the right place.
$.doNothing = function() {
// oh noez, i do nuttin
}
// does exactly as advertised, nothing
$.doNothing();
var jQueryEnhancedObjectOnSteroids = $("body");
// Uncaught TypeError: Object #<an Object> has no method 'doNothing'
jQueryEnhancedObjectOnSteroids.doNothing();
Oh, and finally to cut a long thread short and to answer your question - doing $.f = $.fn.f allows you to use the function as a plugin or a utility method (in jquery lingo).
The $.f is a utility function whereas $.fn.f is a jQuery plugin / method.
A utility function is basically a function within the jQuery namespace that is useful for performing some operation, for example, $.isArray(obj) checks if an object obj is an array. It is useful to put functions in the jQuery namespace if you'll use them often and also to avoid global namespace pollution.
jQuery methods on the other hand operate on jQuery objects/wrapped sets. For example, $(document.body).append('<p>Hello</p>'); will append a paragraph containing Hello to the body element of the document. $.fn is a shorthand for $.prototype in later versions of jQuery (it wasn't always in earlier versions of the library). You would use this when writing your own plugins.
The $.fn.f is simply a shortcut to jQuery.prototype
By using the fn, you can add plugin methods without using the extend method:
jQuery.fn.myPlugin = function(opts) { ... }
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I'm using RequireJS while prototyping an application. I'm "faking" a real database by loading a json file via ajax.
I have several modules that need this json file, which I noticed results in multiple http requests. Since I'm already using RequireJS, I thought to myself "hey, why not load this json file as another module". Since a module can return an object, it seemed reasonable.
So I tried this:
// data.js
define(function(require){
const $ = require('jquery')
var data = {}
$.getJSON('/path/to/data.json', function(json_data){
data = json_data
})
// this does not work because getJSON is async
return data
})
// some_module.js
define(function(require){
const my_data = require('data')
console.log(data) // undefined, but want it to be an object
})
I understand why what I'm doing is not working. I'm not sure what the best way to actually do this would be though.
Things I don't want to do:
Change getJSON to async: false
add a while (data == null) {} before trying to return data
Is there an AMD-y want to accomplish what I'm trying to do? I'm sure there's a better approach here.
Edit
I just tried this. It works, but I'm not sure if this is a good or terrible idea:
// in data.js
return $.getJSON('/path/to/data.json')
// in some_module.js
const my_data = require('data')
my_data.then(function(){
console.log(my_data.responseText)
// do stuff with my_data.responseText
})
My concern is (1) browser support (this is a "promise", right?) and (2) if multiple modules do this at the same time, will it explode.
Because this question is specifically referring to using JQuery, you can actually do this without a native promise using JQuery's deferred.then().
// in data.js
return $.getJSON('/path/to/data.json')
// in some_module.js
const my_data = require('data') // this is a JQuery object
// using JQuery's .then(), not a promise
my_data.then(function(){
console.log(my_data.responseText)
// do stuff with my_data.responseText
})
Based on the description of then() in JQuery's docs, it looks like this is using a promise behind the scenes:
As of jQuery 1.8, the deferred.then() method returns a new promise that can filter the status and values of a deferred through a function, replacing the now-deprecated deferred.pipe() method. [...]
Callbacks are executed in the order they were added. Since deferred.then returns a Promise, other methods of the Promise object can be chained to this one, including additional .then() methods.
Since JQuery's .then() does work in IE, I guess they are polyfilling the promise for IE behind the scenes.
The only way I can properly explain what I'm trying to do is by example:
function CustomExtend(myParam: Object): Object {
return $.extend(myParam, { extraParameter: 'extraParam' });
}
I want intellisense when using the CustomExtend function to provide all the properties and methods of the object passed in plus the extraParameter property added inside the function. The closest I could get to getting this to work was by using generics and an interface:
interface IMyParamStructure {
extraParameter?: string;
}
function CustomExtend<TParam extends IMyParamStructure>(myParam: TParam): TParam {
myParam.extraParameter='extraParam';
return myParam;
}
This works to a degree, it has intellisense correctly of all the properties and methods of the object passed in (myParam) but it still doesn't have the extraParameter property on the result. I expect this is because I made it optional and it wasn't passed in (because I don't want to have to pass it in). Is this just not possible with the TypeScript compiler?
EDIT: Forgot to add that the generic parameter should be inferred, not given, i.e. it would be used in this way:
var result=CustomExtend({ param1: 'something', param2: 'something else' });
What I want is for intellisense to show param1, param2 and extraParameter.
In TypeScript all type inference needs to be at compile time. TypeScript cannot modify interface definitions based on calling context.
This would require the ability to write code that runs within the compiler context with access to the AST which is not a planned feature at the moment.
Here is a related stackoverflow question : Is it possible to model jQuery.fn.extend using TypeScript
Question could be obvious but I still cannot find appropriate solution for this.
Lets assume there is a controller with only one method:
class MyController extends Controller {
public static Result sum(int op1, int op2) {
return ok(op1 + op2);
}
}
Routes file is simple enough too:
GET /sum controllers.MyController.sum(op1: Integer, op2: Integer)
Well, now I can do call from templates:
#controllers.routes.MyController.sum(1, 2)
which will be translated to
localhost:9000/sum?op1=1&op2=2
, or directly paste this url in browser. This works pretty ok.
But everything goes bad when I decide to use ajax for doing this.
I am not js-guru, so I write small (and bad I think:) object using jQuery which adds onClick handler to button. Here it is:
entityController.setSumURL = function(sumURL) {
this.sumURL = sumURL;
}
entityController.bindSumButton = function(buttonId, op1, op2) {
$.get(entityController.sumURL, {op1: op1, op2, op2}, function(){
alert("Done!");
});
}
where entityController.sumURL should be url to /sum method.
Usually when I render page view I write something like this:
#()
....
entityController.setSumURL("#controllers.routes.MyController.sum()")
....
But I cannot do this because sum method has mandatory arguments and there is no way to get address only because binded url can rely on parametes passed to function defined in routes.
So the question is how to do get path only from url without arguments, or how to reorganize whole process to avoid such situations?
My solution is to remove arguments from function appearing in routes and query them directly from request, but my project is growing and sometimes it become too hard to understand which parameters are passed to method.
Check out the zen tasks sample application.
In particular:
the javascriptRoutes method in the controller
the references to jsRoutes in the coffeescript
the routes config
You may want to compile this app and look at the output javascript rather than the coffeescript if you're not familiar with coffeescript.
Also, if you're reloading parts of the page using ajax, you may want to bind your jQuery using
$('.somePermanentContainer').on('click', 'selectorForClickable', function()...)
otherwise you'll find it's no longer bound when that part of the DOM is reloaded.
When you create a class in the name space of example.
em.components.grid
em.components.grid.Popup = Class.create(
{
initialize: function(params){
...
},
show:function(){
// create or show
}
});
Does this mean in other classes I have access to the show method if I use the namespace path above.
// Another class in prototype
em.components.grid.Popup.show();
Or does your new class your trying to access show from have to be in the same namespace.
Is namespacing kind of like packages in other languages. So by giving a namespace you can keep all your classes related to for example grid in one name space and possible other classes unrelated to grid in another namespace.
Update
This raises 2 other questions, lets say i create my class like above with the same namespace. Then in another js document I instantiate the class
var popup = new em.components.grid.Popup()
Then popup would be a global variable not? which I don't want to have in my files if possible. Seen as I have went to all the trouble of giving it a unique name space. To then create an instance of the class on a global variable somewhere else in a js file.
So in the case of a popup is it best to have it global or would it be best to create it on a rollover event and remove it on a rollout event.
//pseudo code
$$('domelementClass').observe('mouseover', function(event) {
var popup= new em.components.grid.Popup(event.target);
})
the issue I see with above is I have no reference to remove it on the rollout.
$$('domelementClass').observe('mouseout', function(event) {
popup.remove();
})
Namespacing has the same purpose of packaging, avoiding collision. As your example above shows, in JavaScript, you namespace functions and variables by making them properties of an object.
Does this mean in other classes I have access to the show method if I
use the namespace path above.
// Another class in prototype em.components.grid.Popup.show();
In this case no because 'show()' is an instance method, it can only be called once you have a new Popup. You can use your namespaced Popup as an instance in another class or if you want to call show like a static method in Java then you would call Popup.prototype.show();
var Popup = Class.create({
initialize: function(params){
alert("I exist");
},
show:function(){
alert("show!");
}
});
// Popup.show(); // would error:
// Uncaught TypeError: Object function klass() {
// this.initialize.apply(this, arguments);
// } has no method 'show'
Popup.prototype.show();
foo = new Popup();
foo.show();
Some useful links:
http://michaux.ca/articles/javascript-namespacing
http://blog.anselmbradford.com/2009/04/09/object-oriented-javascript-tip-creating-static-methods-instance-methods/
I have been thrown in at the deep end with an existing VB.NET project at work. I have never used VB.NET before so I am struggling a little. Does anyone know how to solve the following.
I need to pass an instance to client side and then pass it to a shared method in order to access instance methods from when the shared method.
The starting point is a fileupload control within the HTML of my Contacts.aspx file:
<asp:FileUpload ID="DocUpload1" runat="server" onchange="CallMe();" />
The onchange event calls a javascript method, see below, this uses AJAX PageMethods to called a Shared method in my code behind
This is the script code which is in my Contact.aspx file
<script language="javascript">
function CallMe() {
// call server side method
PageMethods.GetContact(0, CallSuccess, CallFailed, null);
}
// set the destination textbox value with the ContactName
function CallSuccess(res, destCtrl) {
}
// alert message on some failure
function CallFailed(res, destCtrl) {
alert(res.get_message());
}
</script>
This is an example class of the type of thing I want to do, I think I need to use the "instance As Contacts" as an input parameter to the WebMethod function but I don't know how to pass the instance into it:
This is the class within my Contacts.aspx.vb file.
Partial Class Contacts
<System.Web.Services.WebMethod()> _
Public Shared Function GetContact(ByVal instance As Contacts) As String
Return instance.GetContactName() 'This is an instance class which I need to call.
End Function
'This is my instance class which I want to call from the Shared Class.
Public Shared Function GetContactName() As String
Return "Fred Bloggs"
End Function
End Class
If anyone knows the solution please could they update the code as I probably won't be able to understand if you just give a description. I just hope I am along the right tracks.
If I understand you correctly, you want to access a class (your instance) created in the ASP.Net page life-cycle from your PageMethod - eg created during initial page load or file upload etc.
This is not directly possible as PageMethods do not go through the full page life-cycle (they are essentially webservices). So you need to pass some sort of identifier to the client that, when passed back to the server in the PageMethod, can be used to re-create or retrieve your instance.
eg During the initial page load:
session("ContactID") = instance
Your PageMethod might look something like:
Public Shared Function GetContact(ByVal key As String) As String
Return HttpContext.Current.Session(key).GetContactName()
End Function
where the parameter key is the same key you used to store your instance in the session state.
In your javascript:
function CallMe() {
// call server side method
PageMethods.GetContact('ContactID', CallSuccess, CallFailed, null);
}