this.IsMounted() is not a function - ajax

I'm trying to build a simple React App. It retrieves data from an ajax call and renders it to the page. The issue I'm having it setting the state of this.props after the ajax call. I'm receiving this error:
Uncaught TypeError: this.isMounted is not a function
I've been going through tutorials and looking at some example code, like this page on loading information through ajax on the react site https://facebook.github.io/react/tips/initial-ajax.html, but I don't see what would be causing this error. Here's my code:
var ANiceReminderApp = React.createClass({
getInitialState: function(){
return {
quotes: []
};
},
componentDidMount: function(){
$.ajax({
headers: { 'X-Mashape-Key':'xxxxxx'},
url: 'https://healthruwords.p.mashape.com/v1/quotes/',
type: 'GET',
dataType: 'JSON',
success: function(data){
var quote = data[0].media;
if(this.isMounted()){
this.setState({
quotes: quote
});
}
}
});
},
render: function() {
return (
<div className="container">
hello world
<img src={this.state.quotes}/>
<button>Need more inspiration?</button>
</div>
);
}
});
React.render(<ANiceReminderApp />, document.body);
Thanks in advance!

In event handlers, this refers to the object that raised the event. In your case, that would be the jqXHR object, which indeed lacks the .isMounted() method.
To deal with this situation you need to either keep a reference to the outer this and use that reference within the event handler, or use function.bind() to force the function to retain the outer context.
Here is an example of how to do the latter method:
$.ajax({
...
success: function(data) {
var quote = data[0].media;
if (this.isMounted()){
this.setState({
quotes: quote
});
}
}.bind(this); // Note the use of .bind(this) here
});

#gilly3's answer explains the issue. However, I prefer a different solution: React will efficiently auto-bind class methods, meaning that this will refer properly to the instance. So I generally use methods as callbacks:
React.createClass({
componentDidMount: function(){
$.ajax({
// the method is already bound to the component
success: this.onDataReceived
});
},
onDataReceived: function(data) {
var quote = data[0].media;
if(this.isMounted()){
this.setState({
quotes: quote
});
}
},
// ...
});
This has a couple of advantages:
In theory at least, React's binding is more efficient than using .bind. This is particularly true if you'd have to call .bind repeatedly for multiple calls.
It makes the callback more easily testable on its own.
It makes it easier to invoke the callback logic through some other code path (e.g. if you also want to accept data provided via props).
It's also worth seeing this discussion, which suggests that isMounted may be deprecated in the future - the suggested path in this case is to save a reference to the AJAX request and abort it on componentWillUnmount.

Related

Async AJAX calls overwriting each other

I've got a dashboard page, and am using jQuery to update each graph with a single ajax call.
If it run AJAX with async:false then everything works, but it's obviously slow as the calls are made one after another.
When I run async:true, the queries execute but they all output to the same element and overwrite each other.
How can I ensure that the jQuery selector in the success and error functions remain pointed to their original desintation and do not all point to the final box?
My code:
//update sparklines on dashboard page
$(".convobox7").each(function() {
id = $(this).attr('id');
$("#convobox-7-"+id).prepend("<img src='img/ajax_loader.gif'/>");
$.ajaxQueue({
url: '_ajax/getFunnelReport',
type: "POST",
dataType: "json",
async: true,
data: {funnel:$(this).attr('id'), dimension:'date'},
timeout: 50000,
success: function(json) {
var data = json;
if (data.success=='true') {
$("#convobox-7-"+id).html(data.htmlconv+"<br/><small>Past week</small>");
gebo_peity.init();
}
},
error: function(x, t, m) {
$("#convobox-7-"+id).html("");
}
})
});
Note I'm using the ajaxQueue plugin here but the same thing happens without it.
You need to localise id :
var id = $(this).attr('id');
There may be other things to fix but that one is a certainty.
EDIT
Try this :
$(".convobox7").each(function() {
var id = $(this).attr('id');
var $el = $("#convobox-7-"+id).prepend("<img src='img/ajax_loader.gif'/>");
$.ajaxQueue({
url: '_ajax/getFunnelReport',
type: "POST",
dataType: "json",
data: {funnel:id, dimension:'date'},
timeout: 50000,
success: function(data) {
if (data.success == 'true') {
$el.html(data.htmlconv+"<br/><small>Past week</small>");
gebo_peity.init();
}
},
error: function(x, t, m) {
$el.html("");
}
});
});
This has to do with function closures because you declared the variable outside the success/error function. A better approach is to use the $(this) reference in the error/success functions instead of assigning it outside the handlers.
Edit: In the context of the error/success handler for ajaxQueue, I'm not absolutely certain what $(this) refers to, you may need to navigate to a parent element. I didn't see any definitive documentation offhand. This is one of my biggest pet peeves with javascript documentation, $(this) is sometimes not what you would think it'd be and isn't documented :/
silly question, but since you already send the element id to the service, is there a reason it cannot send it back? then you can simply use that as a selector, ensuring that you have the item you need.

AJAX avoid repeated code

I'm using Symfony2.1 with Doctrine2.1
I'd like to use AJAX for many features on my site , editing a title , rate an article , create an entity on the fly , etc.
My question is simple :
Do I need to create a JQuery function for each functionnality , like this :
$('#specific-functionality').bind('click', function(e){
var element = $(this);
e.preventDefault();
// the call
$.ajax({
url: element.attr('href'),
cache: false,
dataType: 'json',
success: function(data){
// some custom stuff : remove a loader , show some value, change some css
}
});
});
It sounds very heavy to me, so I was wondering if there's any framework on JS side, or a specific method I can use to avoid this. I was thinking about regrouping items by type of response (html_content , boolean, integer) but maybe something already exists to handle it nicely !
From what I understand, you are asking for lighter version of JQuery ajax method. There are direct get/post methods instead of using ajax.
$.get(element.attr('href'), {'id': '123'},
function(data) {
alert(data);
}
);
To configure error function
$.get(element.attr('href'), {'id': '123'}, function(data) {alert(data);})
.error(function (XMLHttpRequest, textStatus, errorThrown) {
var msg = jQuery.parseJSON(XMLHttpRequest.responseText);
alert(msg.Message);
});
Also, you can pass callback function to do any synchronous operations like
function LoadData(cb)
{
$.get(element.attr('href'), { 'test': test }, cb);
}
And call
LoadData(function(data) {
alert(data);
otherstatements;
});
For progress bar, you use JQuery ajaxStart and ajaxStop functions instead of manually hiding and showing it. Note, it gets fired for every JQuery AJAX operation on the page.
$('#progress')
.ajaxStart(function () {
//disable the submit button
$(this).show();
})
.ajaxStop(function () {
//enable the button
$(this).hide();
});
Instead of $('#specific-functionality').bind('click', function(e){, try this:
$(".ajax").click(function(){
var url = $(this).attr("href") ;
var target = $(this).attr("data-target") ;
if (target=="undefined"){
alert("You forgot the target");
return false ;
}
$.ajax(....
And in html
<a class="ajax" href="..." data-target="#some_id">click here </a>
I think it is the simplest solution. If you want some link to work via ajax, just give it class "ajax" and put data-target to where it should output results. All custom stuff could be placed in these data-something properties.

How to create stub for ajax function using Jasmine BDD

I'm struggling to find any examples on how to fake an ajax call using Jasmine BDD?
I have a custom ajax function that works like so...
ajax({
url: 'JSON.php',
dataType: 'json',
onSuccess: function(resp) {
console.log(resp);
}
});
...and I've no idea how to create a stub to fake calling the actual ajax function.
I want to avoid calling the ajax function as it could slow down my test suite if a real ajax call to the server takes some time to respond and I've loads of specs in my test suite.
I've heard that you can use spyOn(namespace, 'ajax') but that is annoying straight away as it requires me to wrap my ajax function in an object just to use the spyOn function (but regardless I wasn't able to follow along as I couldn't find any specific examples to fake an ajax call).
I've also heard that you can use createSpy() but again the documentation isn't very helpful (neither is the corresponding wiki on GitHub).
Any help explaining how to use spies to create a fake ajax call would be greatly appreciated!
You can use SinonJS mocking framework, which has a build in fake server. You can easily use it with jasmine:
beforeEach(function() {
server = sinon.fakeServer.create();
server.respondWith([200, { "Content-Type": "text/html", "Content-Length": 2 }, "OK"])
});
Btw. if your ajax function is in the global namespace why not call spyOn(window, 'ajax')
Regarding a single function, you may use 'createSpy':
/*var */ajax = createSpy('foo');
var is absent because you want to redefine it, but it is then required that the block where you define this spy is bound to the same scope where real ajax was defined. Or, if you confused with that, use spyOn(window, foo), because you are anyway testing it in the browser.
See this answer for details.
Regarding the ajax call, see Asynchronous Support section in new docs, or better use Clock:
window.ajax = function() {};
var response;
var ajaxSpy = spyOn(window, 'ajax').andCallFake(function(url, callback) {
setTimeout(function() { callback({ 'foo': 'bar' }); }, 1000);
});
jasmine.Clock.useMock();
function callback(resp) { response = resp; }
ajax('fake.url', callback);
jasmine.Clock.tick(1500);
expect(ajaxSpy).toHaveBeenCalled();
expect(response).toBeDefined();
expect(response.foo).toBeDefined();
If you're ok with not using spies, but instead the add-on jasmine-ajax. To mock for a single spec use withMock:
it("allows use in a single spec", function() {
var onSuccess = jasmine.createSpy('success');
jasmine.Ajax.withMock(function() {
ajax({
url: 'JSON.php',
dataType: 'json',
onSuccess: onSuccess
});
expect(onSuccess).not.toHaveBeenCalled();
jasmine.Ajax.requests.mostRecent().respondWith({
"status": 200,
"responseText": '{"some": "json"}'
});
expect(onSuccess).toHaveBeenCalledWith('{"some": "json"}');
});
});
The response is only sent when you use respondWith. The link above has some directions how to install

jquery bind functions and triggers after ajax call

function bindALLFunctions() {
..all triggers functions related go here
};
$.ajax({
type: 'POST',
url: myURL,
data: { thisParamIdNo: thisIdNo },
success: function(data){
$(".incContainer").html(data);
bindALLFunctions();
},
dataType: 'html'
});
I am new to ajax and JQuery.
I have the above ajax call in my js-jquery code. bindALLFunctions(); is used to re-call all the triggers and functions after the ajax call. It works all fine and good as expected. However, I have read somewhere that is better to load something after the initial action is finished, so I have tried to add/edit the following two without any success.
Any ideas?
1) -> $(".incContainer").html(data, function(){
bindALLFunctions();
});
2) -> $(".incContainer").html(data).bindALLFunctions();
Perhaps you should have a look to the live and delegate functions. You can set a unique event handler at the beggining of your app and all your loaded ajax code will be automatically binded:
$("table").delegate("td", "hover", function(){
$(this).toggleClass("hover");
});
But if you prefer to use Jquery.ajax call you have to do something like this:
$.ajax({
type: 'POST',
url: myURL,
data: { thisParamIdNo: thisIdNo },
success: function(data){
$(".incContainer").html(data);
bindALLFunctions(".incContainer");
},
dataType: 'html'
});
and transform bindALLFunctions as:
function bindALLFunctions(selector) {
..all triggers functions related go here. Example:
$('#foo', selector).bind('click', function() {
alert('User clicked on "foo."');
});
};
that will only bind events "under" the given selector.
Your initial code was fine. The new version does not work because html() function does not have a callback function.
It's hard to tell from your question just what you intend to ask, but my guess is that you want to know about the ready function. It would let you call your bindALLFunctions after the document was available; just do $(document).ready(bindALLFunctions) or $(document).ready(function() { bindALLFunctions(); }).

jQuery - AJAX request using both native XHR and flXHR (Flash) fallback - how to minimize code duplication?

I need to retrieve data via cross-domain XMLHttpRequest. To make this work in (almost) all browsers, I use native XHR first and, if that fails, flXHR.
The (working) code I currently have for this is as follows:
jQuery.support.cors = true; // must set this for IE to work
$.ajax({
url: 'http://site.com/dataToGet',
transport : 'xhr',
success: function(data) {
console.log('Got data via XHR');
doStuff(data);
},
error: function(xhr, textStatus, error) {
console.log('Error in xhr:', error.message);
console.log('Trying flXHR...');
$.ajax({
url: 'http://site.com/dataToGet',
transport : 'flXHRproxy',
success: function (data) {
console.log('Got data via flXHR');
doStuff(data);
},
error: function (xhr, textStatus, error) {
console.log('Error in flXHR:', error.message);
console.log('Both methods failed, data not retrieved.');
}
});
}
});
This feels like a lot of code duplication to me, especially in the success handlers. Is there a more efficient way to do this? I'd really prefer to make one $.ajax call that would try both transports in turn, instead of having to use the error handler to make the call a second time. It's not too bad in this example, but rapidly gets more complicated if the success handler is longer or if the success handler has to itself issue another $.ajax call.
I've created a jquery-specific and slimmed-down fork of flxhr that simplifies your code sample above. You can see an example of usage in the "Usage" section in the README.
https://github.com/b9chris/flxhr-jquery-packed
In particular, you don't want to waste time waiting for a standard CORS request to fail. It's easy to determine whether flxhr is necessary by testing $.support.cors upfront (no need to override it). Then just use flxhr explicitly where necessary.
Why don't you just wrap this in a function by itself? That's after all, how you end up reusing code. You can even pass functions as arguments to make sure that you don't have to repeat this code more than once.
To me this is pretty straight forward but maybe I've misunderstood.
function xhr(success) {
$.ajax({
success: success,
error: function() {
$.ajax({ success: success })
}
});
}
Then just pass the success handler once
xhr(function(data){/*magic*/});
Or if you wanna basically avoid redundant configuration of the ajax call use the first object as a template, like this:
function xhr(success) {
var ajaxParams = { success: success };
ajaxParams.error = function() {
$.ajax($.extend(ajaxParams, { transport: 'xhr' }));
}
$.ajax(ajaxParams);
}
I simplified the whole thing a bit, but I hope you get the point.
Edit
Reading that last bit, maybe this will give you some ideas... it's a variation of that last snippet.
function xhr(success) {
var ajaxParams = { success: success };
ajaxParams.error = function() {
var newParams = $.extend(ajaxParams, { transport: 'xhr' });
newParams.success = function() {
// do something
// arguments is a special array, even if no parameters were
// defined in any arguments where passed they will be found
// in the order they were passed in the arguments array
// this makes it possible to forward the call to another
// function
success.apply(this, arguments);
}
$.ajax(newParams);
}
$.ajax(ajaxParams);
}

Resources