How to return $(this) - javascript

I'm using jQuery and made a plugin for some in house work that basically builds URLs for our internal API. Anyways, I want to return $(this) and im not getting the right thing and im getting a createdocumentfragment error?
Plugin code:
$.get(base_url,{
agenda_id:defaults.id,
action:defaults.action+defaults.type,
output:defaults.output
},function(html){
defaults.callback(html);
});
That works fine, but i want to add return obj like so:
$.get(base_url,{
agenda_id:defaults.id,
action:defaults.action+defaults.type,
output:defaults.output
},function(html){
defaults.callback(html);
return obj;
});
Obj is set at the start of my plugin and obj works fine throughout the plugin. It's set as obj=$(this);
In my script, which uses the plugin, I have:
$('#agenda-live-preview').agenda({action:'get',type:'agenda',id:window.location.href.split('/').pop(),callback:function(html){
$(this).empty().append($(html).html());
}});
However, it doesn't work and returns:
Error: doc.createDocumentFragment is not a function
Source File: http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js
Line: 4373
In the console error logs. Any ideas how to return $(this) AND run the callback?

I (finally) left a comment in your other question. :o) I'm pretty sure you need to do this in your plugin:
defaults.callback.call(this,html);
instead of:
defaults.callback(html);

It sounds like you want to return the object to the original caller.
agenda = function(opts, callback) {
$.get(base_url,{
agenda_id:defaults.id,
action:defaults.action+defaults.type,
output:defaults.output
},function(html){
defaults.callback(html);
});
return obj;
}
I'm guessing the idea is to enable chaining, so that you can say something like
$('#id').agenda(opts).show();
or whatever. Of course, this will execute just after the $.get is issued and not after it is completed, but this is normal and probably what you want.

Related

Wrapping `console.log` and retaining call stack

In my logging helper class, I have the following:
this.myInfo = console.info.bind(console);
When I call my myInfo function from elsewhere, the calling object and line number are correctly retained and logged in the Chrome devtools.
When I run myInfo though, I also want to run another local function in addition to the console.info. Hence, I figured I could just wrap the above and it would work. I've come up with the following:
var obj = this;
this.myInfo = (function() {
console.info.apply(this, arguments);
myOtherFunc.apply(obj, arguments);
}).bind(console);
The problem is that unlike my first example, I lose the calling context for console.info, and the wrong line number and file are logged in the devTools.
How can I wrap the first example and retain the proper context for the console.info?
You can use getter. In getter you call your other function and then return console.info.bind(console) to caller.
Object.defineProperty(this, "myInfo", { get: function () {
myOtherFunc();
return console.info.bind(console);
}});
In case of passing arguments. You can define following function:
this.myInfo = function()
{
myOtherFunc.apply(null, arguments);
return console.bind.apply(console, arguments);
}
// example of call
this.myInfo(1,2,3)();
I've new solution. You can implement your console.log wrapper in separate JS file or evaluate it with sourceURL then go to Chrome DevTools settings and add "console-wrapper.js" url to blackbox pattern or blackbox this script by link when first message is arrived to console.
When script become blackboxed then all messages will have correct location in source code.
It works in last Google Chrome Canary build and will be available in stable in around two months.
eval("\
function myAwesomeConsoleLogWrapper() {\
console.log.call(console, arguments);\
makeAnotherWork();\
}\
//# sourceURL=console-wrapper.js");
Alexey Kozyatinskiy's approach is cool. However, if not-pretty code like this.myInfo(1,2,3)() is a more serious problem than ugly console output, you could use the wrapper you posted in your question and print needed filename and line number manually having it extracted from new Error().stack. I'd personnaly use Alexey's method unless there was a team working on this project.

jQuery Plugin public methods not working when applied to multiple elements

I posted a similar issue earlier, but it was flagged as a duplicate. However, this referenced article did not answer my question, so I'll try this again, this time using the solution of said article in my example.
The solution provided in this article creates the same issue I had before: when there is more than one element, I cannot call any of the public methods of the plugin.
Since no working example was provided, let's start with the code the article gave:
(function($){
$.fn.myPlugin = function(options) {
// support multiple elements
if (this.length > 1){
this.each(function() { $(this).myPlugin(options) });
return this;
}
// private variables
var pOne = '';
var pTwo = '';
// ...
// private methods
var foo = function() {
// do something ...
}
// ...
// public methods
this.initialize = function() {
// do something ...
return this;
};
this.bar = function() {
// do something ...
};
return this.initialize();
}
})(jQuery);
I LOVE the internal loop so that it's applied to each instance of the element, but I feel the repeated "return this" is redundant. I think if we removed every single one of them, this plugin would work exactly the same. But, for the sake of argument, I'm going to leave them in my working example.
As you can see in this jsfiddle example, it works fine when there is only one element. The public method runs fine.
However, if I were to comment the other 4 elements back in like here, it throws an error in the console: "undefined is not a function". This, of course, makes sense since I'm attempting to run the public method on a reference to all elements on not an individual element.
Well, then I use .eq(0) to run the method only on the first instance of the element here, but I get the exact same error in the console.
So, why isn't calling the public method on the individual element working? Is this a scoping issue?
Please advise. Thanks!
Ok, so I think I've answered my own question. The issue is that I'm not applying a jQuery plugin to a DOM element. I'm applying it to a jQuery element. So, if I were to apply the jQuery plugin to a jQuery element, referenced like $element or $('.element'), I can then run any public methods because the scope is the same. But, if I were to reference it in a different way, like say $parentelement.eq(0), I'm using a difference reference, one that did not get the plugin applied to it, so naturally, it would not have the defined method. I think I'm getting the concept right. Still a little shaky on this. Maybe someone else can explain it better.
Nevertheless, while the above code does technically work, public methods are not practical on a jQuery plugin. I suggest instead using a Custom Event to make the jQuery plugin do something. Like this:
(function($) {
$.fn.extend({
myTestPlugin: function() {
if (this.length > 1) {
this.each(function() { $(this).myTestPlugin(); });
}
this.done = function() {
$(this).html('Done!');
};
var alsoDone = function() {
$(this).html('Also done!');
};
this.html('Replace me!');
this.on('alsoDone', alsoDone);
}
});
})(jQuery);
Here is an example where I am using trigger to make the plugin do something on an individual element, which works, but the method still fails as expected.
I hope this helps other people with similar issues.

Why is .active() not working with typehead

We are using typehead for autocomplete with bootstrap 2.3, bootstrap-typeahead.js. Though typeahead is a jQuery plugin, jQuery.active is always 0 even when it is trying to get suggestions from server.
Does jQuery.active working only for jQuery ajax requests? Is there any alternative to jQuery.active if I use any plugins rather than jQuery.ajax to get information from server.
This is untested, but from the code on github it appears to me that this is the function responsible for retrieving and returning the data. It is found in the dropdown.js file of the plugin.
getDatumForSuggestion: function getDatumForSuggestion($el) {
var datum = null;
if ($el.length) {
datum = {
raw: Dataset.extractDatum($el),
value: Dataset.extractValue($el),
datasetName: Dataset.extractDatasetName($el)
};
}
return datum;
}
give that is the case you could probably utilize jquery's .active method to get you when the data is being extracted and when its been returned by modifying this function like so,
getDatumForSuggestion: function getDatumForSuggestion($el) {
var datum = null;
$.active++
if ($el.length) {
datum = {
raw: Dataset.extractDatum($el),
value: Dataset.extractValue($el),
datasetName: Dataset.extractDatasetName($el)
};
}
$.active--
return datum;
}
Personally, I wouldn't suggest using $.active but creating your own global variable for this specific purpose. The reason why is you can't always be sure, especially if you're using jquery plugins, when you might actually be calling a jQuery.ajax method and active will update at that time, perhaps not giving you what you're looking for, unless you are wanting to see other connections to the server along with this plugin, in which case use $.active

Meteor: Template is displayed although collection is not loaded yet

Hi fellow Meteor friends!
Please note: I am using Tom's router!
So I'm trying to only display my template when the mongo collection is ready but for some reason it does not work! :(
I first followed this post: LINK
So I have my publish functions in the server.js and I subscribe to these functions inside my router, so no Deps.autorun() involved here (btw: is this the right approach? Deps.autorun() did not work for me properly):
So I have something like:
'/myroute': function(bar) {
Meteor.subscribe("myCollection", bar, function() {
Session.set('stuffLoaded', true);
});
return 'stuffPage';
}
In the template, where the data loaded from "myCollection" is displayed, I will have something like this:
<template name="stuffPage">
{{#if stuffLoaded}}
<!-- Show the stuff from the collection -->
{{else}}
<p>loading!</p>
{{/if}}
</template>
For some reason "loading!" is never displayed.
Also, for a couple of milliseconds, the "old data" from the last time the same template was displayed (but with another "bar" value provided to the publish function --> different data) is displayed.
This of course is not good at all because for a couple of ms the user can see the old data and suddenly the new data appears.
To avoid this "flash" I want to display "loading!" until the new data is loaded but again: this does not work for me! :-(
What am I doing wrong?
Thx in advance for your help!
EDIT:
Ok so the problem with the answer in the first post provided by #user728291 is the following:
For some reason the router stuff get's called AFTER the Deps.autorun() ... what is wrong here? :( (please note: eventsLoaded == stuffLoaded.)
Where do you guys put your Deps.autorun() for the subscriptions or in other words: What's your code mockup for this?
I actually really think that my code mockup is just plain wrong. So how do you make different subscriptions based on the route (or in other words: based on the template which is currently shown)?
AND: Where do you put the Deps.autorun()? Inside the router.add() function? Or just inside of (Meteor.isClient)?
I think #user728291's answer is pretty spot on, I'd just add that Meteor.subscribe returns a handle that you can use to check readiness:
Keep a reference to the handle
Deps.autorun(function() {
stuffHandle = Meteor.subscribe(Session.get('bar'));
});
Then check it in your template:
{{#if stuffHandle.ready}}
...
{{/if}}
Template.barTemplate.helpers({stuffHandle: stuffHandle});
And control it via the session:
'/myroute': function(bar) {
Session.set('bar', bar);
return 'barTemplate';
}
Better to put the subscription in a Deps.autorun and use Session variable to pass arguments from the router. Also, make sure you are setting stuffLoaded to false before the subscribe runs. Otherwise it just keeps its old value.
'/myroute': function(bar) {
if ( ! Session.equals( "bar", bar ) ) {
Session.set( "stuffLoaded", false); //subscription needs to be run
Session.set( "bar", bar ); // this change will trigger Dep.autorun
}
return 'stuffPage';
}
Deps.autorun ( function (){
Meteor.subscribe("myCollection", Session.get( "bar" ), function() {
Session.set("stuffLoaded", true);
});
});
You might need some initial default values for the Session variables if you are not getting what you want on the first time the page loads.
First off, you may be missing the actual function name for the callback as demonstrated in this post.
Meteor.subscribe("myCollection", bar, function onComplete() {
Session.set('stuffLoaded', true);
});
Which seems to be great practice. I don't usually miss a beat using this method.
Secondly, I'm not sure subscriptions inside routes work well? I'd rather do the following:
'/myroute': function(bar) {
Session.set("myCollectionParam", bar)
return 'stuffPage';
}
So then the subsciption finally looks like this:
Meteor.subscribe("myCollection", Session.get("myCollectionParam"), function onComplete() {
Session.set('stuffLoaded', true);
});
OR (not sure which works correctly for you, depending on your publish function):
Meteor.subscribe("myCollection", {bar: Session.get("myCollectionParam")}, function onComplete() {
Session.set('stuffLoaded', true);
});
Good luck!
EDIT
Just mentioning something about the publish function:
While Session.get("myCollectionParam") could return null, you can ensure the behaviour a bit more by using the following publish method:
Meteor.publish("myCollection", function(myCollectionParam) {
check(myCollectionParam, String);
return MyCollection.find({_id: myCollectionParam});
});

Unable to re-define a function in my javascript object

I have an object defined using literal notation as follows (example code used). This is in an external script file.
if (RF == null) var RF = {};
RF.Example= {
onDoSomething: function () { alert('Original Definition');} ,
method1 : function(){ RF.Example.onDoSomething(); }
}
In my .aspx page I have the following ..
$(document).ready(function () {
RF.Example.onDoSomething = function(){ alert('New Definition'); };
RF.Example.method1();
});
When the page loads the document.ready is called but the alert('Original Definition'); is only ever shown. Can someone point me in the right direction. I basically want to redefine the onDoSomething function. Thanks, Ben.
Edit
Thanks for the comments, I can see that is working. Would it matter that method1 is actually calling another method that takes the onDoSomething() function as a callback parameter? e.g.
method1 : function(){
RF.Example2.callbackFunction(function() {RF.Example.onDoSomething();});
}
Your code as quoted should work (and does: http://jsbin.com/uguva4), so something other than what's in your question is causing this behavior. For instance, if you're using any kind of JavaScript compiler (like Closure) or minifier or something, the names may be being changed, which case you're adding a new onDoSomething when the old one has been renamed. Alternately, perhaps the alert is being triggered by something else, not what you think is triggering it. Or something else may have grabbed a reference to the old onDoSomething (elsewhere in the external script, perhaps) and be using it directly, like this: http://jsbin.com/uguva4/2.
Thanks for the response .. in the end the answer was unrelated to the code posted. Cheers for verifying I wasn't going bonkers.

Categories