Convert $.each and $.getJSON into vanilla javascript - javascript

I am creating a web application to collect the details from the GitHub API and display them on the screen.
I want to convert the $.getJSON and $.each into vanilla JavaScript.
I don't want to use any part of jquery in my code, hence either Ajax can be used or vanilla JavaScript.
const execute = () => {
let uname= document.getElementById('username').value;
//api for the username
let apiCall1 = `https://api.github.com/users/${uname}`;
//api for repository
let apicall2 = `https://api.github.com/users/${uname}/repos`;
$.getJSON(apiCall1, (json) => {
let fullname = json.name;
let username = json.login;
let aviurl = json.avatar_url;
let profileurl = json.html_url;
let followersnum = json.followers;
let followingnum = json.following;
let reposnum = json.public_repos;
if(fullname == undefined) {
fullname = username;
}
document.getElementById('view').style.display = 'block';
document.getElementById('result').innerHTML = `
<h1 class="text-center pt-2">USER DETAILS</h1>
<div class="row p-3">
<div class="col-md-3">
<img class="profile mt-3" src=${aviurl} alt="porfile image">
</div>
<div class="col-md-9">
<h3>FULL NAME: <span>${fullname}</span></h3>
<h3>USER NAME: <span>${username}</span></h3>
<h3>PROFILE URL: <span>${profileurl}</span></h3>
<h3>FOLLOWERS: <span>${followersnum}</span></h3>
<h3>FOLLOWING: <span>${followingnum}</span></h3>
<h3>NUMBER OF REPOSITORIES: <span>${reposnum}</span></h3>
</div>
</div>`;
let repositories, outhtml;
$.getJSON(apicall2, function(json){
repositories = json;
outputPageContent();
});
function outputPageContent() {
if(repositories.length == 0) {
outhtml = '<h1>No Repositories!</h1>';
}
else {
outhtml = `<h1>LIST OF REPOSITORIES</h1><ul>`;
$.each(repositories, function(index) {
outhtml = outhtml + `<li>${repositories[index].name}</li>`;
});
outhtml = outhtml + '</ul>';
}
document.getElementById('repo').innerHTML = outhtml;
}
})
.fail(() => {
alert("No such username exists!");
document.getElementById('username').value = "";
document.getElementById('view').style.display = 'block';
document.getElementById('result').innerHTML = "Not Available";
document.getElementById('repo').innerHTML = "Not Available";
});
};
I am unable to convert the code to vanilla JavaScript.

$.getJSON
$.getJSON('https://your.url.com', function(data) {
});
becomes:
var request = new XMLHttpRequest();
request.open('GET', 'https://your.url.com', true);
request.onload = function() {
if (this.status >= 200 && this.status < 400) {
// Success!
var data = JSON.parse(this.response);
} else {
// error
}
};
request.send();
or
fetch('https://your.url.com')
.then(response => response.json())
.then(data => // do something with data now)
.catch(() => // error)
$.each
$.each(collection, (item) => console.log(item));
becomes:
collection.forEach(item => console.log(item));
A great resource for migrating from jQuery to vanilla is http://youmightnotneedjquery.com/.

$.each can be replaced with forEach with arrays and Object.entries.forEach when enumerating objects. $.getJSON is likewise easily replaced with the Fetch API.
forEach
const repositoriesArray = [
"reactjs",
"angular",
"foo",
"bar"
];
repositoriesArray.forEach(repo => {
console.log(repo)
});
const repositoriesObject = {
reactjs: {
company: 'facebook'
},
angular: {
company: 'google'
}
};
Object.entries(repositoriesObject).forEach(repo => {
let key = repo[0];
let value = repo[1];
// Do something with the key/value
console.log(value.company);
});
Fetch
$.getJSON(apicall2, function(json){
repositories = json;
outputPageContent();
});
would, for example, become:
const apicall2 = 'https://jsonplaceholder.typicode.com/users';
fetch(apicall2).then(res => res.json()).then(json => {
console.log(json[0].name);
});

Related

People.People.searchDirectoryPeople() query with multiple email addresses

In a Google Apps Script I need to query the Google user profile picture URL of many coworkers.
Here is a working example for a single user:
searchDirectoryPeople('jimmy.neutron#example.com');
function searchDirectoryPeople(query) {
const options = {
query: query,
readMask: 'photos,emailAddresses',
sources: ['DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE']
}
const people = People.People.searchDirectoryPeople(options);
if(people && people.people) {
Logger.log('size: '+people.people.length);
people.people.forEach(person => {
let url = '';
let email = '';
if(person) {
if(person.photos && person.photos[0]) {
url = person.photos[0].url;
}
if(person.emailAddresses && person.emailAddresses.length) {
person.emailAddresses.forEach(item => {
if(item.metadata && item.metadata.sourcePrimary) {
email = item.value;
}
});
}
}
Logger.log('email: '+email+': '+url);
//Logger.log('person: %s', JSON.stringify(person, null, 2));
});
} else {
Logger.log('no people.people');
}
}
I found out that I can query all jimmy people:
searchDirectoryPeople('jimmy');
I have the email address of all employees. I could loop through a big list of 1000+ employees one by one, but this is not practical. I am looking for a way to query multiple email addresses. The docs at https://developers.google.com/people/api/rest/v1/people/searchDirectoryPeople are cryptic for the query. I tried many things like these but nothing works:
'jimmy.neutron#example.com, carl.wheezer#example.com, cindy.vortex#example.com'
'jimmy.neutron#example.com OR carl.wheezer#example.com OR cindy.vortex#example.com'
I am looking for a query by list of email addresses as input, such as:
[ 'jimmy.neutron#example.com', 'carl.wheezer#example.com', 'cindy.vortex#example.com' ]
Is it possible to have an OR query in People.People.searchDirectoryPeople()?
UPDATE 2022-05-31
I tried looping through all emails and ran either into a quota limit or a script runtime limit.
#Lorena Gomez's answer is correct: First use the People.People.listDirectoryPeople() to get the resource names of all email address, followed by People.People.getBatchGet() to get the profile picture URL by resource names. The former limits to 1000 employees per call, the latter limits to 200. This works in our case where we have 1k+ email addresses as input, and 20k+ employees returned by listDirectoryPeople().
Working code:
const emails = [
'jimmy.neutron#example.com',
'carl.wheezer#example.com',
'cindy.vortex#example.com'
];
let emailToUrl = getGoogleProfilePictureUrls(emails);
Logger.log('emailToUrl: %s', JSON.stringify(emailToUrl, null, 2));
// expected output:
// emailToUrl: {
// "jimmy.neutron#example.com": "https://lh3.googleusercontent.com/a-/xxxx=s100",
// "carl.wheezer#example.com": "https://lh3.googleusercontent.com/a-/xxxx=s100",
// "cindy.vortex#example.com": "https://lh3.googleusercontent.com/a-/xxxx=s100"
// }
function getGoogleProfilePictureUrls(emails) {
let options = {
readMask: 'emailAddresses',
sources: ['DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE'],
pageSize: 1000
}
let run = 1;
let resourceNameToEmails = {};
let result = {};
while(run === 1 || result.nextPageToken) {
if(result.nextPageToken) {
options.pageToken = result.nextPageToken;
}
result = People.People.listDirectoryPeople(options);
Logger.log('request #' + (run++) + ', got '+result.people.length+' resource names');
result.people.forEach(person => {
if(person.emailAddresses) {
person.emailAddresses.forEach(obj => {
if(obj.metadata && obj.metadata.sourcePrimary) {
let email = obj.value
if(emails.indexOf(email) >= 0) {
resourceNameToEmails[person.resourceName] = email;
}
}
});
}
});
Utilities.sleep(200);
}
run = 1;
let emailToUrl = {};
let resourceNames = Object.keys(resourceNameToEmails);
let resourceNameBatch = resourceNames.splice(0, 200);
while(resourceNameBatch.length) {
options = {
personFields: 'photos',
resourceNames: resourceNameBatch,
sources: [ 'READ_SOURCE_TYPE_PROFILE' ]
};
result = People.People.getBatchGet(options);
if(result && result.responses) {
Logger.log('request #' + (run++) + ', got '+result.responses.length+' urls');
result.responses.forEach(person => {
let primaryUrl = '';
let url = '';
if(person.person && person.person.photos) {
person.person.photos.forEach(photo => {
if(photo.metadata && photo.metadata.source && photo.metadata) {
url = photo.url;
if(photo.metadata.source.type === 'PROFILE' && photo.metadata.primary) {
primaryUrl = url;
}
}
});
}
let email = resourceNameToEmails[person.person.resourceName];
emailToUrl[email] = primaryUrl || url;
});
}
Utilities.sleep(200);
resourceNameBatch = resourceNames.splice(0, 200);
}
return emailToUrl;
}
It looks like with Method: people.searchDirectoryPeople you can only specify one person at a time.
Another option could be People.People.getBatchGet() which will require an extra step but provides you information about a list of the people you specify. The request would look something like this:
const options = {
personFields: 'photos,emailAddresses',
resourceNames: [
'people/account_id',
'people/account_id',
'people/account_id'
],
sources: [
'READ_SOURCE_TYPE_PROFILE'
]
}
const people = People.People.getBatchGet(options);
You can get the user's account_id with Method: people.listDirectoryPeople
How about this?
function searchDirectoryPeople(query) {
const options = {
query: query,
readMask: 'photos,emailAddresses',
sources: ['DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE']
}
const people = People.People.searchDirectoryPeople(options);
if(people && people.people) {
Logger.log('size: '+people.people.length);
people.people.forEach(person => {
let url = '';
let email = '';
if(person) {
if(person.photos && person.photos[0]) {
url = person.photos[0].url;
}
if(person.emailAddresses && person.emailAddresses.length) {
person.emailAddresses.forEach(item => {
if(item.metadata && item.metadata.sourcePrimary) {
email = item.value;
}
});
}
}
return {"imgurl":url,"email":email}
});
}
}
function searchPlus(emailArray) {
let oA = [];
emailArray.forEach(e => {
oA.push(searchDirectoryPeople(e))
});
if(oA && oA.length) {
return oA;
}
}

Async call in javascript For Loop not working

I have a callback function inside a loop here for (var res in results) {
but it seems the loop is not waiting for the async call. When I am calling self.callTestOutputData(test_output_url) here, the loop is not waiting fpor the response but continuing for the next iteration and I am losing out the value to push into obj.requistion_number = testOutputResponse.value;
Please note : var results = response.results Here results is an array of Json objects.
Edit 1 : I tried forEach but that didn't work .
results.forEach(res => {
var obj = {}
obj.ferp = res.name;
// your code...
})
Original Code:
self.downloadDailyExcelProcurement = function (filters, excelTmpArr) {
self.disableExcelDownloadProcurement(true);
$('.useCaseExcelButtonProcurement .oj-button-button .oj-button-label')[0].style.backgroundColor = "gray";
$('.useCaseExcelButtonProcurement .oj-button-button .oj-button-label .demo-download-icon-24')[0].style.color = "#D8D8D8";
var payload = {};
if (typeof filters === "string") {
var fill = filters;
} else {
var fill = self.sendFilters();
if(self.app() === "fusion"){
fill += '&module=Procurement';
}else if (self.app() === "o2r"){
fill += '&module=O2r';
}
}
if(fill.includes("%3A")){
fill = fill.replace(/%3A/g, ':');
}
payload.Endpoint = 'executions/testcollection/' + fill;
//console.log(payload.Endpoint)
payload.BeforeSend = function (xhr) {
xhr.setRequestHeader('Authorization', 'Basic ' + btoa('guest:oracle123'));
$(".custom-loader-circle").show();
};
payload.OnSuccess = function (response) {
var results = response.results;
for (var res in results) {
var obj = {}
obj.ferp = results[res].name;
obj.po = "NA"
obj.receipt_no = "NA"
var test_output_url = results[res].reference_test_cases[0].automation_tests[0].test_outputs[0]
$.when(self.callTestOutputData(test_output_url)).done(function (testOutputResponse) {
if(testOutputResponse)
obj.requistion_number = testOutputResponse.value;
else {
obj.requistion_number = "NA";
}
self.excelTmpArr().push(obj);
});
}
else {
self.excelTmpArr().push(obj);
}
}
if (response.next) {
filters = ((response.next).split('testcollection'))[1];
if (filters[0] === "/") {
var test = filters.slice(1, filters.length);
}
self.downloadDailyExcelProcurement(test, self.excelTmpArr());
} else {
if (results.length === 0) {
$(".custom-loader-circle").hide();
self.disableExcelDownloadProcurement(false);
$('.useCaseExcelButtonProcurement .oj-button-button .oj-button-label')[0].style.backgroundColor = "#4d0000";
$('.useCaseExcelButtonProcurement .oj-button-button .oj-button-label .demo-download-icon-24')[0].style.color = "white";
showMessage(self.messages, "No Data to Download", '', 'info');
} else {
self.formatForExcel(self.excelTmpArr(), fill, "Procurement");
}
}
};
payload.OnError = function (data) {
showMessage(self.messages, data.status, data.statusText, 'error');
$(".custom-loader-circle").hide();
};
getData(payload);
}
Try using async and await :
async function asyncCall () {
// call here
}
for (var res in results) {
const response = await asyncCall();
}
var results = response.results;
if(result.length > 0){
results.map((data,index)=>{
//write your code here
})
}
This will help you ..
Use forEach() to iterate since it creates its own function closure:
results.forEach(res => {
var obj = {}
obj.ferp = res.name;
// your code...
})

Can't get API results using search bar

I am trying to get a user's input from a search bar, and use it to get the drink information from a cocktail API, but I am not getting the results back.
If I use a real value for example, Margarita, instead of the placeholder ${drinkName} in my query URL I do get the data I'm looking for in the object form I'm looking for, but something about how I'm getting/using the user input is wrong. I do get the user's input from the search in the console when I console log drinkName, but it doesn't seem to pass that into the searchByName function, and console logging cocktail after the searchByName function returns my empty cocktail object.
let cocktail = {};
const searchByName = (drinkName) => {
fetch(`https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${drinkName}`)
.then(
function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return data;
}
response.json().then(function(data) {
console.log(data);
getDrinkName(data);
getIngredients(data);
getDirections(data);
getImage(data);
buildCocktail(data);
console.log(cocktail);
});
}
)
.catch(function(err) {
console.log('Fetch Error :-S', err);
});
}
const getImage = (data) => {
const imageUrl = (data.drinks[0].strDrinkThumb);
return imageUrl;
}
const getDrinkName = (data) => {
const name = (data.drinks[0].strDrink);
return name;
}
const getIngredients = (data) => {
let ingredientList = [];
for (let i = 1; i < 16; i++) {
if (data.drinks[0][`strIngredient${i}`] == null){
break;
} else {
const ingredients = ((data.drinks[0][`strMeasure${i}`]) + ': ' + data.drinks[0][`strIngredient${i}`]);
ingredientList.push(ingredients);
}
}
return ingredientList;
}
const getDirections = (data) => {
const directions = (data.drinks[0].strInstructions);
return directions;
}
const buildCocktail = (data) => {
cocktail.image = getImage(data);
cocktail.name = getDrinkName(data);
cocktail.ingredients = getIngredients(data);
cocktail.directions = getDirections(data);
console.log(cocktail);
}
$("#search").keypress(function(event) {
if (event.which === 13) {
const drinkName = $('#search').val();
console.log(drinkName);
searchByName(drinkName);
console.log(cocktail);
}
});

AngularJS JSONP not updating key value to object

I was trying to update the channel.status object with the //Second JSONP callback function.
angular.element(document).ready(function() {
channels = [];
channel = {};
followersAPI = followersAPI.replace(/theUser/, user);
followersAPI = $sce.trustAsResourceUrl(followersAPI);
// First JSONP
$http.jsonp(followersAPI).then(function (response) {
var follows = response.data.follows;
for (var i = 0; i < follows.length; i ++) {
var currentChannel = follows[i].channel;
if (currentChannel.status == null) {
currentChannel.status = "offline";
}
channel = {
name: currentChannel.name,
display_name: currentChannel.display_name,
logo: currentChannel.logo,
url: currentChannel.url,
status: "loading"
};
streamAPI = "https://wind-bow.glitch.me/twitch-api/streams/";
streamAPI += channel.name;
streamAPI = $sce.trustAsResourceUrl(streamAPI);
// Second JSONP
$http.jsonp(streamAPI).then(function (response) {
data = response.data;
if (data.stream == null) {
channel["status"] = "offline";
} else {
channel["status"] = data.stream.channel.status;
}
});
channels.push(channel);
}
});
$scope.channels = channels;
console.log($scope.channels);
});
There's no error message, but only the last channel{} object in channels[] array was updated.
Here's the HTML part for channel.status:
<div id="channel-status" class="col-md-6">
<a
class="btn"
href="{{channel.url}}"
target="_blank">
{{channel.status}}
</a>
</div>
For asynchronous call $http.jsonp in for-loop, in its callback you will only get instance of last defined channel.as a solution, you should move all things about channel into the Second JSONP.
var currentChannel = follows[i].channel;
streamAPI = "https://wind-bow.glitch.me/twitch-api/streams/";
streamAPI += channel.name;
streamAPI = $sce.trustAsResourceUrl(streamAPI);
// Second JSONP
$http.jsonp(streamAPI).then(function (response) {
data = response.data;
if (currentChannel.status == null) {
currentChannel.status = "offline";
}
channel = {
name: currentChannel.name,
display_name: currentChannel.display_name,
logo: currentChannel.logo,
url: currentChannel.url,
status: "loading"
};
if (data.stream == null) {
channel["status"] = "offline";
} else {
channel["status"] = data.stream.channel.status;
}
channels.push(channel);
});

Add a loading animation while the promise loads and displays the JSON

I finished the functionality side of this simple app but now I want to add some good UX aswell so I want to add a loading animation (a spinner) while the JSON loads and before it displays the result of the promise, but I cannot seem to find a solution by googling.
Here is the pen: https://codepen.io/kresimircoko/pen/ZLJjVM.
Here is the JavaScript code:
const API_KEY = '?api_key=625023d7336dd01a98098c0b68daab7e';
const root = 'https://www.warcraftlogs.com:443/v1/';
const zonesBtn = document.querySelector('#zones');
const responseList = document.querySelector('#response');
console.clear();
const requestJSON = objType => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
try {
resolve(JSON.parse(this.responseText));
}
catch (e) {
reject(e);
}
};
xhr.onerror = reject;
xhr.open('GET', root + objType + API_KEY);
xhr.send();
});
};
function displayBosses(zoneID) {
let bosses = document.querySelectorAll(`.bosses[data-zoneid="${zoneID}"]`);
requestJSON('zones')
.then(data => {
let output = '';
data.find(zone =>
zone.id === parseInt(zoneID, 10)
).encounters.map(encounter => {
output += `<li class="boss" data-zoneid="${zoneID}">${encounter.name}</li>`;
bosses.forEach(boss => {
boss.innerHTML = output;
});
}).join('');
});
}
function displayZones() {
let output = '';
requestJSON('zones')
.then(zones => {
return zones.map(zone => {
output += `
<ul data-zoneid="${zone.id}" class="zones">
<span>${zone.name}</span>
<ul data-zoneid="${zone.id}" class="bosses"></ul>
</ul>`;
response.innerHTML = output;
}).join('');
})
.then(responseList.style.display = 'flex');
}
zonesBtn.addEventListener('click', displayZones);
responseList.addEventListener('click', evt => {
const target = evt.target.parentElement;
const zoneID = target.dataset.zoneid;
displayBosses(zoneID);
if (target.classList.contains('clicked'))
target.classList.remove('clicked');
else
target.classList.add('clicked')
});
The spinner is a FontAwesome icon wrapped in a spinner div for which we control the display property to show up when the button is clicked but hide when the promise has resolved.
function displayZones() {
if (!this.classList.contains('open')) {
spinner.style.display = 'block';
this.classList.add('open');
}
let output = '';
requestJSON('zones')
.then(zones => {
spinner.style.display = 'none';
return zones.map(zone => {
output += `
<ul data-zoneid="${zone.id}" class="zones">
<span>${zone.name}</span>
<ul data-zoneid="${zone.id}" class="bosses"></ul>
</ul>`;
response.innerHTML = output;
}).join('');
})
.then(responseList.style.display = 'flex');
}

Categories