In an HTML page, having two buttons (EDIT, DELETE), I do an AJAX call then I parse the returned JSON and I do perform an EDIT or a DELETE based on the returned ID. All this inside of a
$( document ).ready(function() {
...
$('button.icons8.icons8-edit-file').on("click", function(){
var key = $(this).attr('key');
$.ajax({
url: '/ede/' + key + '/json',
method: 'POST',
success: function (response) {
// PARSING THE JSON, EXTRACTING THE ID FROM IT
// AND PERFOMING THE EDIT
}
});
})
...
$('button.icons8.icons8-delete-file').on("click", function(){
var key = $(this).attr('key');
$.ajax({
url: '/ede/' + key + '/json',
method: 'POST',
success: function (response) {
// PARSING THE JSON, EXTRACTING THE ID FROM IT
// AND PERFOMING THE DELETE
}
});
})
...
});
What I would like to have is a global function what gets the key value, does the AJAX call and returns a value according to a pattern.
$( document ).ready(function() {
...
$('button.icons8.icons8-edit-file').on("click", function(){
var key = $(this).attr('key');
var value = GLOBAL_FUNCTION(key, pattern_edit);
// PERFOMING THE EDIT
})
...
$('button.icons8.icons8-delete-file').on("click", function(){
var key = $(this).attr('key');
var value = GLOBAL_FUNCTION(key, pattern_delete);
// PERFOMING THE DELETE
})
...
});
Where sould I put that GLOBAL_FUNCTION(p_key, p_pattern) fuction? Inside or outside of the ready function? How do I return the response from this global function? What if the result of parsing the response produces a list of values?
Where should I put that GLOBAL_FUNCTION(p_key, p_pattern) function? Inside or outside of the ready function?
You can put it anywhere in scope of all the places you want to call it. Global is one possibility but should be avoided where possible. Instead, I'd suggest namespacing your common functions in to their own object. This way you can then extract them to a separate JS file for easy re-use.
How do I return the response from this global function?
If the function is making an AJAX request you can't return anything, as the call should be asynchronous. You can work around this by providing a callback function to execute when the request completes.
With those points in mind, the code would look something like this:
// common.js
var common = (function() {
return {
global_function: function(key, callback) {
$.get('/ede/' + key + '/json', function(response) {
// perform common actions here, if needed..
callback && callback(response);
}
}
};
})();
// in your page:
$('button.icons8.icons8-edit-file').on("click", function() {
var key = $(this).data('key');
common.global_function(key, function(response) {
// Edit response.id...
});
});
$('button.icons8.icons8-delete-file').on("click", function() {
var key = $(this).data('key');
common.global_function(key, function(response) {
// Delete response.id...
});
});
It would also make sense to extract the delete and edit logic to the common library, but I'll leave that to you based on your own logic.
Finally, note the use of data() here, as creating your own non-standard attributes will make your HTML invalid. Use data-key instead.
You can put a function just in the jQuery ready state and use them in both callbacks. It's not really global, but it's in the right scope to use in both.
But GLOBAL_FUNCTION function can't return the value, because $.ajax is async. But you can pass a callback to the function, executed after finish.
And you can tell jQuery that the response is in JSON format, so it will parse it automatically for you.
$( document ).ready(function() {
function GLOBAL_FUNCTION(key, type, callback) {
$.ajax({
url: '/ede/' + key + '/json',
method: 'POST',
dataType: 'json,
success: function (response) {
// EXTRACTING THE ID FROM RESPONSE
var id = response.id;
if (type === 'edit') {
// PERFOMING THE EDIT
}
if (type === 'delte') {
// PERFOMING THE DELETE
}
// EXECUTE CALLBACK ON FINISH
callback(id, type);
}
});
};
$('button.icons8.icons8-edit-file').on("click", function(){
var key = $(this).attr('key');
GLOBAL_FUNCTION(key, 'edit', (id, type) => {
console.log(type + ' of id #' + id + ' finished');
});
})
$('button.icons8.icons8-delete-file').on("click", function(){
var key = $(this).attr('key');
GLOBAL_FUNCTION(key, 'delete', (id, type) => {
console.log(type + ' of id #' + id + ' finished');
});
})
});
Related
I have a series of nested Ajax requests to external APIs, which is very ugly but it was the only way I could figure out how to make calls in a specified order with each call utilizing some values brought back from the previous call. (I attempted this but couldn't get it to work, so I reverted to the advice here.)
Anyway, this works well to a point. All my calls work in succession, and I end up with an array called people, which is just a list of names: ["name1","name2","name3"].
My problem is that I don't seem to be able to do anything with this array from my javascript code. I can't append them to a div, nor can I alert them, or even console.log them during code execution. However, once my code completes, I can type people into the browser console and there they all are, as expected.
I am guessing this has something to do with the scope of the variable - I have tried making it global and moving the placement of its declaration, but the only way I can access people from the runnable code is from within the final AJAX loop, and then I get lots of repeating values because it's looping and adding to the array incrementally.
The goal here is to get people from that final API call and list them in HTML.
Here's my code. Any suggestions greatly appreciated.
HTML to trigger event:
<input type='file' accept='image/*' onchange='openFile(event)'>
<!--process is triggered by file upload-->
javascript:
var openFile = function(event) {
//... Some UI stuff happens here.
//... When finished, just call getGraph(); below
performances = new Array(); // global scope
people = new Array(); // global scope
getGraph(); // call function below
console.log(people); // retrieve array; doesn't work
};
function getGraph(){
$.ajax({
url:'http://...' + document.getElementById('prDate').value,
dataType:'json',
success: function(response){
$.each(response, function(i, item) {
var programID = item.id;
$.ajax({
url:'http://...'+ programID',
dataType:'json',
success: function(response){
$.each(response, function(i, item) {
performances.push( item.id );
});
$.each(performances, function(index, value){
$.ajax({
url:'http://...' + this.valueOf() +'/persons/',
dataType:'json',
success: function(response){
$.each(response, function(i, item) {
people.push( item.firstname + ' ' + item.lastname ); // the magic moment
});
}
});
});
}
});
});
}
});
}
From your code it is visible that people variable will be create only once you call openfile function. If you want it be created even when the openfile method is not called then declare it outside of all the functions and then it will be accessible or else declare it in the place where you intend to use it like above the ajax call, then use it.
Have you tried putting it inside a IIFE closure ?
(function(){
var OpenFile = function() {
if ( !(this instanceof OpenFile) ) {
return new OpenFile();
}
var performances = new Array(); // closure Scope
var people = new Array(); // closure Scope
function getGraph(){
$.ajax({
url:'http://...' + document.getElementById('prDate').value,
dataType:'json',
success: function(response){
$.each(response, function(i, item) {
var programID = item.id;
$.ajax({
url:'http://...'+ programID',
dataType:'json',
success: function(response){
$.each(response, function(i, item) {
performances.push( item.id );
});
$.each(performances, function(index, value){
$.ajax({
url:'http://...' + this.valueOf() +'/persons/',
dataType:'json',
success: function(response){
$.each(response, function(i, item) {
people.push( item.firstname + ' ' + item.lastname ); // the magic moment
});
}
});
});
}
});
});
}
});
}
return {
get performances() : { return performances;},
get people() : { return people; },
getGraph : getGraph
};
};
window.OpenFile = OpenFile;
})();
which you can then call by doing something like
var myOpenFile = new OpenFile();
var people = myOpenFile.people;
myOpenFile.getGraph();
console.log(people);
with the added benefit that the OpenFile object is immediately available after the code loads. All the variables inside the code are only scoped to the object OpenFile and don't pollute the global namespace and you can choose what you wish to expose to others by putting them in the return statement at the end.
I have this pretty long script which is right now using a second AJAX call to fetch the data as a temporary Workaround.
Where there suggestion call is made /admin/locations/suggest.json the result is an array of objects. A list is displayed with the data from these objects. When one of them is clicked a second AJAX call is made to /admin/locations/view/' + locationId + '.json to fetch the location data - again. This data is already there but in the data that was returned from the first AJAX call.
My issue is now accessing the Locations variable from inside the on.click() code. I've already got the index there and everything but locations doesn't have the data present.
How can I populate the locations after my first call and use them in the on.click() event?
SuggestLocation = function() {
var locations = null;
$('#NewLocation').hide();
function suggestLocation(locations) {
$.ajax({
url: '/admin/locations/suggest.json',
type: 'POST',
data: $("#AgendaItemAdminAddForm, #AgendaItemAdminEditForm").serialize(),
success: function (data) {
var htmlString = '';
for (var p in data.data) {
htmlString = htmlString + '<li data-index="' + p + '" data-id="' + data.data[p].Location.id + '">' + data.data[p].Location.display_name + '</li>';
}
$('#LocationSuggestions').html(htmlString);
locations = data.data;
console.log(locations);
},
dataType: 'json'
});
};
$(document).on('click', '#LocationSuggestions li', function(event) {
locationIndex = ($(this).data('index'));
locationId = $(this).data('id');
$.ajax({
url: '/admin/locations/view/' + locationId + '.json',
type: 'GET',
data: null,
success: function(data) {
$('#SelectedLocation').html(
Mustache.render($('#LocationTemplate').html(), data.data)
);
},
dataType: 'json'
});
$('#AgendaItemLocationId').val(locationId);
$('#AgendaItemLocationId').val(locationId);
$('#LocationFormFields').hide();
$('#LocationSuggestions').html('');
$('#NewLocation').show();
});
$('#LocationFormFields input, #LocationFormFields textarea').keydown(function() {
suggestLocation();
});
$('#LocationFormFields select').change(function () {
suggestLocation();
});
$('#NewLocation').click(function(event) {
event.preventDefault();
$('#AgendaItemLocationId').val($(this).data(''));
$('#LocationFormFields').show();
$('#SelectedLocation').hide();
suggestLocation();
$(this).hide();
return false;
});
};
SuggestLocation();
Where you have:
var locations = null;
creates locations in the outer scope (the assignment of null is redundant, it does nothing useful), however when you do:
function suggestLocation(locations) {
that creates a locations variable that is local to suggestLocation, so later when you do:
locations = data.data;
the data is assigned to that locations variable, not the outer one. None of the calls to suggestLocation pass a parameter to the function, so simply get rid of locations as a formal parameter, i.e. make it:
function suggestLocation() {
so the value is assigned to the outer locations that is available to all functions within SuggestLocation.
Just remember that the AJAX call is asynchronous so make sure the callback has been called and the value assigned before you try to access it.
Also, function names starting with a capital letter are, by convention, reserved for constructors so SuggestLocation is not appropriate. Nor is it a good idea to have two functions whose name is identical except for the capitalisation of a single letter.
I tried to use this loop to read some urls to read their modified time:
var arr = [];
//... fill arr with push
for (var e in arr) {
nodename=arr[e].hostname;
node_json="/nodes/"+nodename;
html +='data';
xhr = $.ajax({
url: node_json,
success: (function(nn) {
$('#host_'+nn).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
})(nodename)
});
This already works a bit i I comment out the success line: I get calls to all node-files, and in Firebug, I can see the different modified times in the header of the calls.
At first I had a closure, (see How to generate event handlers with loop in Javascript?) And I only got the last line modified with all results. that's why I try to put the action in a separate function.
But that gives:
ReferenceError: xhr is not defined
$('#host_'+nn).append("last modified: " + xhr.getResponseHeader("Last-Modified")...
How do I get xhr into that function?
I aslo tried:
...
xhr[e] = $.ajax({
url: node_json,
success: add_result_to_info(nodename, e)
});
}
}
// outside loop
function add_result_to_info(nn, e) {
$('#host_'+nn).append("last modified: " + xhr[e].getResponseHeader("Last-Modified"));
}
source of the AJAX call: Get the modified timestamp of a file with javascript
If arr is truly an array, just use .forEach or even better .map (with a shim on older browsers) to encapsulate each iteration's scope without the need for additional closures:
var xhrs = arr.map(function(e) {
var nodename = e.hostname;
var node_json = "/nodes/" + nodename;
html +='data';
return $.ajax({
url: node_json
}).done(function(data, status, xhr) {
$('#host_'+nodename).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
});
});
The reason to use var xhrs = arr.map() instead of .forEach is that you then (for free) get the ability to call yet another callback once every AJAX request has completed:
$.when.apply($, xhrs).then(function() {
// woot! They all finished
...
});
your are directly executing the method and passing its result as the callback for the success callback.
the xhr is already passed as the 3rd argument so try
success: function(nn,status, xhr) {
$('#host_'+nn).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
}
if you have to pass the nodename as well, the you need to use a function that returns a function
success: (function(nn){
return function(data ,status, xhr) {
// you can use nodename here...
$('#host_'+nn).append("last modified: " + xhr.getResponseHeader("Last-Modified"));
};
})(nodename)
Is it possible to put a function into a parameter for Jquery Ajax like below. dataType and data are given as functions. dataType returns a value of JSON if the returntype is JSON and text if isJson is false.
dataVal and dataVar are arrays containing the parameter names and values used to construct the data paramater. The result of the data: function would be a string as:
{dataVar[0]:dataVal[0],dataVar[1]:dataVal[1],.....,}
I'm getting an error when I try this, so, just wanted to know if this method was possible.
function getAjaxResponse(page, isJson, dataVar, dataVal, dfd) {
$.ajax(page, {
type: 'POST',
dataType: function () {
if (isJson == true) {
return "JSON";
} else {
return "text";
}
},
data: function () {
var dataString = '{';
for (var i = 0; i < dataVar.length; i++) {
dataString = dataString + dataVar[i] + ':' + dataVal[i] + ',';
}
console.log(dataString);
return dataString + '}';
},
success: function (res) {
dfd.resolve(res);
}
});
}
Edit
As per answers and comments, made the changes. The updated function is as below. This works:
function getAjaxResponse(page, isJson, dataVar, dataVal, dfd) {
$.ajax(page, {
type: 'POST',
dataType: isJson ? "JSON" : "text",
data: function () {
var dataString ="";
for (var i = 0; i < dataVar.length; i++) {
if (i == dataVar.length - 1) {
dataString = dataString + dataVar[i] + '=' + dataVal[i];
} else {
dataString = dataString + dataVar[i] + '=' + dataVal[i] + ',';
}
}
return dataString;
}(),
success: function (res) {
dfd.resolve(res);
}
});
}
And my original question is answered. But apparently, data is not getting accepted.
The return value of the data function is just treated as the parameter name and jquery just adds a : to the end of the request like so:
{dataVar[0]:dataVal[0]}:
So, my server is unable to pick up on the proper paramater name.
From the manual:
data
Type: PlainObject or String
So no.
Call the function. Use the return value.
data: function () { ... }();
// ^^ call the function
Not that way. But it will work with a little change:
(function () {
if (isJson == true) {
return "JSON";
} else {
return "text";
}
})()
That should work. You just call the function immidiately after you created it. This way, dataType is a String and the script will work.
Same with data. Also use the (function(){})()-notation here
jquery just adds a : to the end of the request like so:
{dataVar[0]:dataVal[0]}:
No, your devtools display does. However, as you're data string does not contain a = sign, and you send the content as application/x-www-form-urlencoded, the whole body is interpreted as if it was a parameter name.
For sending JSON, you should:
use contentType: "application/json"
use data: JSON.stringify(_.object(dataVar, dataVal))1
to ensure valid JSON is sent with the correct header (and correctly recognised as such at the server).
1: _.object is the object function from Underscore.js which does exactly what you want, but you can use an IEFE as well:
JSON.stringify(function(p,v){var d={};for(var i=0;i<p.length;i++)d[p[i]]=v[i];return d;}(dataVar, dataVal))
You need to call the function with parenthesis like below:
function func1(){
//func1 code in here
}
function func2(func1){
//func2 code in here
//call func1 like this:
func1();
}
So, you can use like this:
data: function () {
//your stuff her
}(); // which mean you are having data()
I want to execute this function and use the variable outside the function, but inside an each function. How can I get this to work?
$('.social').each(function() {
url = "http:www.google.com";
bit_url(url);
$(element).append(urlshortened);
});
function bit_url(url) {
var url = url;
var username = "...";
// bit.ly username
var key = "...";
$.ajax({
url : "http://api.bit.ly/v3/shorten",
data : {
longUrl : url,
apiKey : key,
login : username
},
dataType : "jsonp",
success : function(v) {
urlshortened = v.data.url;
}
});
}
The "A" in Ajax stands for Asynchronous code won't work like that, you need callbacks.
function bit_url(url) {
[...]
//return the Deffered object
return $.ajax({ [...]
}
$('.social').each(function() {
[...]
//attach a `done` callback to the returned $.ajax's Deferred instance
bit_url(url).done(function(v) {
$(element).append(v.data.url);
});
});
Deferred.done is an equivalent to $.ajax's success, I've attached the done handler inside the .each scope so your callback can access all variables from the .each scope.
Though, if $(element) is always the same element, you will be fine with #JohnJohnGa's answer by putting the append inside the success handler.
I assume you want to replace anchors' href inside the .each due to the question nature, so you'd store a reference to the link inside of the .each and use that reference inside the callback.
You can append the result in the success function
success: function (v) {
urlshortened = v.data.url;
$(element).append(urlshortened)
}
So your code will be:
$('.social').each(function () {
url = "http:www.google.com";
bit_url(url);
});
function bit_url(url) {
var url = url;
var username = "...";
// bit.ly username
var key = "...";
$.ajax({
url: "http://api.bit.ly/v3/shorten",
data: {
longUrl: url,
apiKey: key,
login: username
},
dataType: "jsonp",
success: function (v) {
$(element).append(v.data.url);
}
});
}