I have a javascript function deleteSomething(), within which the load() function in jQuery is called.
function deleteSomething(a, b){
deleteIt(a, b, function(success){
if(success){
$("#table").load(...);
}
})
}
Now I want to test deleteSomething() with Jasmine to see if load() is being called. But got Error: Expected a spy, but got Function
describe("deleteSomething", function() {
beforeEach(function () {
...
});
it("should call load() when deleteIt returns true", function() {
spyOn($('#table'), 'load'));
deleteIt.and.callFake(function(a, b, callback){
callback(true);
});
deleteSomething(a, b);
expect($('#table').load).toHaveBeenCalled();
});
});
I'm new to Jasmine, how should I do this?
You need to spy on jQuery prototype, it is available here: $.fn.
So your code should look something like this:
describe("deleteSomething", function() {
it("Should call load", function() {
//$.fn is where the load function is defined
// $("...") returns a new jQuery.fn instance that has load function
// from jQuery.fn.prototype
spyOn($.fn, 'load');
$("#anything").load();
expect($.fn.load).toHaveBeenCalled();
});
});
Some more information about the difference of object own members and prototype members (inherited) can be found here.
Related
I'm playing around with jQuery plugin development, and I'd like to chain methods. I read in the jQuery tutorial (https://learn.jquery.com/plugins/basic-plugin-creation/) that you can chain methods by adding return this; to the end of the method, and that works for the first method (test 1). How can I do that for the second method (test 2), which uses console.log? Can all methods be chained?
// test 1
$.fn.greenify = function () {
this.css('color', 'green');
return this;
};
// test 2
$.fn.console = function () {
this.on('click', function () {
console.log('hello world');
});
};
$('a').greenify().console();
The second method should return the jQuery instance. The fact that the event handler uses console.log function has nothing to do with the returned value of that method. As on returns the jQuery object you can code:
$.fn.console = function () {
return this.on('click', function () {
console.log('hello world');
});
};
Now the console method is chainable!
I have something like this: $('#select1').on('change', function() {}) and it works fine. However, I need to run another function after the function has completed.
I thought about taking the function contents, and putting it in a named function, then taking the second function and doing the same, and placing them in the anonymous function:
$('#select1').on('change', function() {
function1 ();
function2 ();
});
However, I was hoping there was another way. The above seems inconsistent with jQuery.
Though you could nest functions with callbacks, generally the best practice in jQuery is to use jQuery.Deferred.
var function1 = function() {
var deferred = $.Deferred();
//Do your thing. When finished, call deferred.resolve()
return deferred;
}
var function2 = function() {
//Function 2 code
}
$('#select1').on('change', function() {
function1().then(function2);
});
why dont you try jquery change function insted javascript bind
$( "#select1" ).change(function() {
function1();
function2()
});
Try this:
$('#select1').on('change', function() {
function1(someVariable, function() {
function2(someOtherVariable);
});
});
function function1(param, callback) {
//...do stuff
callback();
}
Here is where I found that:
Call a function after previous function is complete
I am having a hard time understanding jasmine spyOn function.
I wrote a simple function and test if my method was called:
function myView() {
myLinks();
}
Here are my tests:
describe('#myView', function() {
it('updates link', function() {
var spyEvent = spyOn(window, 'myLinks');
expect(spyEvent).toHaveBeenCalled();
});
});
This returns the following failure:
Expected spy myLinks to have been called
What am i doing wrong here?
You need to call the myView() function so the myLinks() have been called.
function myLinks(){
//some tasks
}
function myView() {
myLinks();
}
This two function above are declared in window object, then you create a spy object pointing to the window.
describe('#myView', function() {
myView();//Call the method so the myLinks was called too
it('updates link', function() {
var spyEvent = spyOn(window, 'myLinks');
expect(spyEvent).toHaveBeenCalled();
});
});
I need for a function to be executable only after an object is defined, I'm currently working in a fascade pattern and one method is dependent on another method. in this case 'addNewLayer' fails because 'setFullMap' hasn't finished executing. is there a solution? I'm using jquery and vanilla js so most any solution would be helpful at this point:
var jen = (function(){
function setFullMap(mapID){
jen.map = new Map(mapID);
}
function setLayer(opt){
//execute code here after jen.map is defined
}
return{
samp: function(id, opt){
setFullMap(id);
addNewLayer(opt);
}
};
})();
Thanks
solution:
var jen = (function(){
function setFullMap(mapID, callback) {
jen.map = new Map(mapID);
if(jen.map){
callback();
}
}
return {
samp: function(id, opt){
setFullMap(id, function(){
addNewLayer(opt);
}.bind(this));
}
};
})();
You will have to pass a callback function to setFullMap, and execute it once the function has completed (at the very end, before the closing }).
var jen = (function(){
function setFullMap(mapID, callback){
jen.map = new Map(mapID);
callback();
}
function setLayer(opt){
//execute code here after jen.map is defined
}
return{
samp: function(id, opt){
setFullMap(id, function() {
addNewLayer(opt);
}.bind(this));
}
};
})();
Do not forget using .bind(this) - it is very important in order to keep the original this in your callback function.
Edit:
Actually that would not work work if the Map constructor is a-synchronous. If you do not have access to the constructor and/or you cannot pass it a callback, then presumably the only (and sad) option would be to use a setTimeout or (easier) setInterval, continuously checking at defined intervals if the operation has been completed, and then fire the callback.
You could use a callback parameter:
function setFullmap(mapId,callback) {
jen.map = new Map(mapId);
callback();
}
....
samp: function(id, opt){
setFullMap(id,function() {
addNewLayer(opt);
});
}
When u dont have a way to manipulate the Map Object then u need to use a loop:
var loop=self.setInterval(function(){
if(jen.map) {
//execute code here after jen.map is defined
console.log(typeof jen.map);
window.clearInterval(loop);
}
},50);
Check jsfiddle:
http://jsfiddle.net/9yv5t/1/
I have checked the docs and it seems that there are various events you could listen to.
For example:
var m = new Map(...);
m.on('load', function () {
//execute code when the first layer is ready
});
var l = new Layer(...);
l.on('load', function () {
//execute code when the layer has been initialized
});
It's also carefully stated for the Layer.load event:
fires after layer properties for the layer are successfully populated.
This event must be successful before the layer can be added to the
map.
I have the following JavaScript code:
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable);
function2(someOtherVariable);
}
else {
doThis(someVariable);
}
});
How can I ensure that function2 is called only after function1 has completed?
Specify an anonymous callback, and make function1 accept it:
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable, function() {
function2(someOtherVariable);
});
}
else {
doThis(someVariable);
}
});
function function1(param, callback) {
...do stuff
callback();
}
If you're using jQuery 1.5 you can use the new Deferreds pattern:
$('a.button').click(function(){
if(condition == 'true'){
$.when(function1()).then(function2());
}
else {
doThis(someVariable);
}
});
Edit: Updated blog link:
Rebecca Murphy had a great write-up on this here: http://rmurphey.com/blog/2010/12/25/deferreds-coming-to-jquery/
Try this :
function method1(){
// some code
}
function method2(){
// some code
}
$.ajax({
url:method1(),
success:function(){
method2();
}
})
This answer uses promises, a JavaScript feature of the ECMAScript 6 standard. If your target platform does not support promises, polyfill it with PromiseJs.
Promises are a new (and a lot better) way to handle asynchronous operations in JavaScript:
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable).then(function() {
//this function is executed after function1
function2(someOtherVariable);
});
}
else {
doThis(someVariable);
}
});
function function1(param, callback) {
return new Promise(function (fulfill, reject){
//do stuff
fulfill(result); //if the action succeeded
reject(error); //if the action did not succeed
});
}
This may seem like a significant overhead for this simple example, but for more complex code it is far better than using callbacks. You can easily chain multiple asynchronous calls using multiple then statements:
function1(someVariable).then(function() {
function2(someOtherVariable);
}).then(function() {
function3();
});
You can also wrap jQuery deferrds easily (which are returned from $.ajax calls):
Promise.resolve($.ajax(...params...)).then(function(result) {
//whatever you want to do after the request
});
As #charlietfl noted, the jqXHR object returned by $.ajax() implements the Promise interface. So it is not actually necessary to wrap it in a Promise, it can be used directly:
$.ajax(...params...).then(function(result) {
//whatever you want to do after the request
});
Or you can trigger a custom event when one function completes, then bind it to the document:
function a() {
// first function code here
$(document).trigger('function_a_complete');
}
function b() {
// second function code here
}
$(document).bind('function_a_complete', b);
Using this method, function 'b' can only execute AFTER function 'a', as the trigger only exists when function a is finished executing.
you can do it like this
$.when(funtion1()).then(function(){
funtion2();
})
This depends on what function1 is doing.
If function1 is doing some simple synchrounous javascript, like updating a div value or something, then function2 will fire after function1 has completed.
If function1 is making an asynchronous call, such as an AJAX call, you will need to create a "callback" method (most ajax API's have a callback function parameter). Then call function2 in the callback. eg:
function1()
{
new AjaxCall(ajaxOptions, MyCallback);
}
function MyCallback(result)
{
function2(result);
}
If method 1 has to be executed after method 2, 3, 4. The following code snippet can be the solution for this using Deferred object in JavaScript.
function method1(){
var dfd = new $.Deferred();
setTimeout(function(){
console.log("Inside Method - 1");
method2(dfd);
}, 5000);
return dfd.promise();
}
function method2(dfd){
setTimeout(function(){
console.log("Inside Method - 2");
method3(dfd);
}, 3000);
}
function method3(dfd){
setTimeout(function(){
console.log("Inside Method - 3");
dfd.resolve();
}, 3000);
}
function method4(){
console.log("Inside Method - 4");
}
var call = method1();
$.when(call).then(function(cb){
method4();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
If function1 is some sync function that you want to turn into an async one because it takes some time to complete, and you have no control over it to add a callback :
function function1 (someVariable) {
var date = Date.now ();
while (Date.now () - date < 2000); // function1 takes some time to complete
console.log (someVariable);
}
function function2 (someVariable) {
console.log (someVariable);
}
function onClick () {
window.setTimeout (() => { function1 ("This is function1"); }, 0);
window.setTimeout (() => { function2 ("This is function2"); }, 0);
console.log ("Click handled"); // To show that the function will return before both functions are executed
}
onClick ();
The output will be :
Click handled
...and after 2 seconds :
This is function 1
This is function 2
This works because calling window.setTimeout () will add a task to the JS runtine task loop, which is what an async call makes, and because the basic principle of "run-to-completion" of the JS runtime ensures that onClick () is never interrupted before it ends.
Notice that this as funny as it makes the code difficult to understand...