Trouble with jQuery Ajax timing - events

I have a very simple javascript class that does an ajax call to a web service of mine via jquery. It returns the data successfully, but I am unable to retrieve it via a variable I set the data to. I don't think it is a matter of the ajax call being asynchronous or not because I have set up event handlers for all the ajax events, but some of them do not fire. I have no idea what is wrong. Here is the complete code:
Javascript:
function testClass(){
this.returnData = "";
this.FireAjax = function(){
$.getJSON("http://localhost/mywebapp/webservices/service.asmx/Initialize?userID=12&jsoncallback=?",
function(data){
this.returnData = data.d;
alert(data.d);
}
);
}
}
HTML page:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<script type="text/javascript" src="http://localhost/mywebapp/js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="testClass.js"></script>
<script type="text/javascript">
$(document).ready(function(){
var obj = new testClass();
$("#debug").ajaxError(function(event, request, settings){
$(this).append("<b>Ajax Error!</b><br />"); //this does not fire
});
$("#debug").ajaxSend(function(evt, request, settings){
$(this).append("<b>Ajax Send!</b><br />"); //this does not fire!?
});
$("#debug").ajaxStop(function(){
$(this).append("<b>Ajax Stopped</b><br />"); //this fires
});
$("#debug").ajaxComplete(function(event,request, settings){
$(this).append("<b>Ajax Completed!</b><br />"); //this fires
$(this).append("<h2>" + obj.returnData + "</h2>"); //this returns an empty string!!!!!!
});
$("#debug").ajaxSuccess(function(evt, request, settings){
$(this).append("<b>Ajax Successful!</b><br />"); //this fires
});
$("#debug").ajaxStart(function(){
$(this).append("<b>Ajax Started!</b><br />"); //this fires
});
obj.FireAjax();
});
</script>
</head>
<body>
<div id="debug">
</div>
</body>
</html>
Additional Info:
If I remove the complete event in my html page and place the call to obj.returnData in my stop event (thinking that perhaps my html complete event overwrites my testClass complete function), i get the same results.

Your problem is here:
this.returnData = data.d;
this inside the anonymous function refers to the jQuery Options object, not the instance of your object.
Try this:
function testClass(){
this.returnData = "";
var that = this;
this.FireAjax = function(){
$.getJSON("http://localhost/mywebapp/webservices/service.asmx/Initialize?userID=12&jsoncallback=?",
function(data){
that.returnData = data.d;
alert(data.d);
}
);
}
}

Related

Rx.js observable subscribe - button click not caught

I'm trying a very basic example with Rx.js observable-subscribe. Here is my code:
HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"></meter>
<title></title>
</head>
<body>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.4.0/Rx.js"></script>
<script src="https://code.jquery.com/jquery-3.4.0.min.js" integrity="sha256-BJeo0qm959uMBGb65z40ejJYGSgR7REI4+CW1fNKwOg=" crossorigin="anonymous"></script>
<script src="index.js"></script>
<button id='testButton'>Click</button>
</body>
</html>
JS
var button = $('#testButton');
var btn$ = Rx.Observable.fromEvent(button, 'click');
btn$.subscribe(function(e){
console.log(e);
});
But there is nothing in the console when I click the button. Did I miss something?
fromEvent requires the first argument to be DOM element. $ probably refers to jQuery which returns a jQuery object.
So you should pull the native element from button before passing it to fromEvent:
var button = $('#testButton');
var btn$ = Rx.Observable.fromEvent(button[0], 'click'); // or .get(0) instead of [0]
Live demo: https://stackblitz.com/edit/rxjs-lpvsng?file=index.ts
I wrapped my JS code to jquery '$( document ).ready()' handler and it started working:
$( document ).ready(function() {
var button = $('#testButton');
var btn$ = Rx.Observable.fromEvent(button, 'click');
btn$.subscribe(function(e){
console.log(e);
});
});

Updating Vue Data Object Off AJAX response

What I am trying to do is loading a template using AJAX and then building a data model from an AJAX JSON response. I want it to be reactive. From what I've read on the Vue documentation, all the instance object properties have to be set at initialization for them to be reactive. I was curious how I could go about doing that.
I am generating the object model based off an array of dot notation strings, converting a csv file to JSON, then parsing that data into the new model object. This is how I envision the process
var vm = new Vue({
el: '#app',
data: function() {
return {
model: {}
};
}
});
/**
-Load template,
-build model object,
-update Vue data,
-have template react to new data
*/
$(function() {
// Load in html
$(#model).load('./template.html', function(response, status, xhr) {
var modelStructureAsArray = [
'meta',
'data',
'data.details'
];
// Update Vue data for model
vm.model = buildObjectByArray(modelStructureAsArray);
/**
// Expected structure
vm.model = {
meta: {
},
data: {
details: {
}
}
}
**/
// This would be where I set all the data, im using a function to parse and
// return a full model, below is simplified for brevity
vm.model.meta.name = 'Daniel';
vm.nextTick(function() {
vm.$el.textContent === 'Daniel';
};
}
});
Loaded HTML
<!-- template.html -->
<template v-if="model.meta">
<header>
<h1>{{ model.meta.name }}</h1>
</header>
</template>
I'm not understanding how to use vue.nextTick() to update the vue model and make it reactive. The documentation shows setting the property, then immediately calling Vue.nextTick() to update it.
I'm not sure what the textContent property of $el is and cannot find it simply googling. Is it updating all text within brackets within the root element?
Edit: Added HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue: Reactive</title>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
</head>
<body>
<header>
<h1>Reactive Vue</h1>
</header>
<main id="app"></main>
<script src="./app.js" charset="utf-8"></script>
</body>
</html>
textContent gets and sets the text contents of a DOM node. You should not need to directly manipulate the DOM for what you are doing.
Your assignment
vm.model = buildObjectByArray(modelStructureAsArray);
should be sufficient to update the component. Since model is a data item, it is reactive, and assignment to it is a reactive operation. Setting its members, though, as in
vm.model.meta.name = 'Daniel';
runs into a change detection caveat since name wasn't present in the assigned structure. So instead, use
vm.$set(vm.model.meta, 'name', 'Daniel');
and the update will be reactive. You don't need to do anything with $nextTick for this. An example is below. Incidentally, you might as well put your code in the created hook rather than the jQuery $.
var vm = new Vue({
el: '#app',
data: function() {
return {
model: {}
};
},
created() {
setTimeout(() => {
vm.model = {
meta: {},
data: {
details: {}
}
}
vm.$set(vm.model.meta, 'name', 'Daniel');
}, 500);
}
});
<script src="//unpkg.com/vue#latest/dist/vue.js"></script>
<div id="app">
<template v-if="model.meta">
<header>
<h1>{{ model.meta.name }}</h1>
</header>
</template>
</div>

Ajax busy indicator

How can I add a busy indicator before the Ajax popup appears on screen? I have been trying to follow several examples, but they are overly complex and very confusing for what seems should be an easy fix. Can anyone please help? Very new to Ajax. Thank you!
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
$('.button').click(function(){
var clickBtnValue = $(this).val();
var ajaxurl = 'auto.php',
data = {'action': clickBtnValue};
$.post(ajaxurl, data, function (response) {
alert(response);
});
});
});
</script>
</head>
<body>
<input type="submit" class="button" name="Start" value="Start" />
</body>
</html>
You have to add the indicator when starting the ajax call and remove it when the call returns.
But you should fix your html (input element is only valid inside a form tag) and then you have to prevent the form submission. If you follow this rules your code looks like this:
$(document).ready(function(){
$('.button').click(function(evt){
// preventing the form submission
evt.preventDefault();
var clickBtnValue = $(this).val();
var ajaxurl = 'auto.php',
data = {'action': clickBtnValue};
// add indicator here (before the ajax request starts)
var $indicator = $('<div>Ajax in progress...</div>').appendTo('body');
$.post(ajaxurl, data, function (response) {
// removing the indicator inside the success handler of the ajax call.
$indicator.remove();
alert(response);
});
});
});

jQuery Mobile ajax request

I am trying to retrieve information from a javascript file in my jQuery mobile website. Ajax is enabled by default, yet when I try xmlHttpRequest.send(), the responseText is the source code for the page rather than a json structure. The initialize() function is run at pageinit, so my thinking is that the json it is retrieving should exist when called. Also, initialize() works fine on the non-mobile variant of the site so I think it has something to do with how JQM handles ajax requests. Thanks in advance for any assistance.
<!DOCTYPE html>
<html>
<head>
var xmlHttpRequest;
var json;
<script type="text/javascript">
function initialize()
{
xmlHttpRequest = (window.XMLHttpRequest) ? new XMLHttpRequest() :
new ActiveXObject("Msxml2.XMLHTTP");
if (xmlHttpRequest == null)
return;
xmlHttpRequest.open("GET", "pick.js", false);
xmlHttpRequest.send();
json = eval('('+ xmlHttpRequest.responseText +')');
}
</script>
......
</head>
<body>
<div data-role="page" id="map-page">
<script type="text/javascript">
$('#map-page').live('pageinit',function(){
initialize();
});
</script>
.....
</div>
</body>
</html>
Since you're using jQuery Mobile (and thusly, jQuery), you should consider using jQuery.ajax -- it handles all of the 'hard stuff' like creating XHR object for you.
For your situation your code would look like this:
function initialize() {
$.get("pick.js", function(data, status, jqXHR) {
//when the call succeeds, do something with the 'data' param
console.log(data);
}, "script");
}

How to inject dependencies in jasmine test for an angular item

Here is the test spec file:
describe('Test main controller', function(){
it('Should initialize value to Loading', function(){
$scope = {}
ctrl = new mainNavController($scope)
expect($scope.wksp_name).toBe('Loading')
})
})
Here is the controller file
function mainNavController($scope) {
$scope.wksp_name = 'Loading...'
$scope.$on('broadCastWkspNameEvent', function (e, args) {
$scope.wksp_name = args
})
}
mainNavController.$inject=['$scope']
But my test fails saying Object #<Object> has no method '$on'
I am using the basic setup of jasmine.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="shortcut icon" type="image/png" href="testlib/jasmine-1.2.0/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="testlib/jasmine-1.2.0/jasmine.css">
<script type="text/javascript" src="testlib/jasmine-1.2.0/jasmine.js"></script>
<script type="text/javascript" src="testlib/jasmine-1.2.0/jasmine-html.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="/static_files/js/test-specs/main-nav-spec.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="/static_files/js/common/jquery/latest.js"></script>
<script type="text/javascript" src="/static_files/js/common/angular/angular-1.0.1.min.js"></script>
<script type="text/javascript" src="/static_files/js/common/angular/angular-resource-1.0.1.min.js"></script>
<script type="text/javascript" src="/static_files/js/section/main-nav-controller.js"></script>
<script type="text/javascript">
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();
</script>
</head>
<body>
</body>
</html>
What is it that I am doing wrong? I am not able to understand how this thing is supposed to work :)
The main problem with your test code is that it tries to create a controller's instance "by hand" using the new operator. When doing so AngularJS has no chance to inject dependencies. What you should be doing is to allow AngularJS inject dependencies:
var $scope, ctrl;
//you need to inject dependencies first
beforeEach(inject(function($rootScope) {
$scope = $rootScope.$new();
}));
it('Should initialize value to Loading', inject(function($controller) {
ctrl = $controller('MainNavController', {
$scope: $scope
});
expect($scope.wksp_name).toBe('Loading...');
}));
Here is the link to a complete jsFiddle: http://jsfiddle.net/pkozlowski_opensource/7a7KR/3/
There are 2 things worth noting in the above example:
You can use the inject() method from the ngMock module to inject dependencies: https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
To create a controller instance (that supports dependency injection) you would use the $controller service: http://docs.angularjs.org/api/ng.$controller
As the last remark: I would advise naming controllers starting with an uppercase letter - this way we won't confuse them with variable names.
Great answer by #pkozlowski.opensource. To elaborate a bit more... Sometimes it could be also handy to assert that $scope.$on was really called by your controller. In this case you can spy on $scope.$on as pointed out below:
beforeEach(inject(function($rootScope) {
$scope = $rootScope.$new();
spyOn($scope, '$on').andCallThrough();
}));
And then you can assert that $on was called with your event name and some function as arguments:
it('Should bind to "broadCastWkspNameEvent"', inject(function($controller) {
ctrl = $controller('MainNavController', {
$scope: $scope
});
expect($scope.$on).toHaveBeenCalledWith('broadCastWkspNameEvent', jasmine.any(Function));
}));
I agree with pkozowski's response, but to answer your question more directly, you need to stub out '$on'
Your example would pass if your $scope looked like:
$scope = {
$on: function() {}
}

Resources