I'm creating two instances of this object on every page of my website. When it loads, it
the makes a fetch call to /cities.json for data, that it then stores in session storage.
Next time the object
is created (on another page), it checks session storage before calling the api endpoint, to prevent
needless data requests. However, when the object is first created it makes two calls to the /cities.json api - because two instances typehead exist.
Apart from moving the call to outside of the object creation, which I don't want to do, as other objects also require this same api
endpoint, and I'd like to find a pattern that could work for each.
Index.js
import Typeahead from '../typeahead'
var typeaheadElements = [].slice.call(document.getElementsByClassName('typeahead'));
if (typeaheadElements.length) {
typeaheadElements.map(function (element) {
new Typeahead(element);
});
}
Typeahead.js
import fetchPromise from '../utilities/fetchSetStorage.js';
function Typeahead(element) {
init.call(this);
function init() {
var input = element.getElementsByTagName('input')[0];
if (input) {
let citiesData = fetchPromise('./cities.json', 'citiesJson');
Promise.all([citiesData]).then(function(values) {
items = values[0];
element.getElementsByTagName('input')[0].removeAttribute('disabled');
});
input.addEventListener('input', handleChange.bind(this));
element.addEventListener('keydown', handleKeyDown.bind(this));
element.addEventListener('click', handleElementClick.bind(this));
document.addEventListener('click', handleDocumentClick.bind(this));
}
}
}
fetchSetStorage.js
export default function fetchPromise(url, storageKey) {
if(sessionStorage.getItem(storageKey)) {
let storageKeyVal = JSON.parse(sessionStorage.getItem(storageKey));
return storageKeyVal;
}
return fetch(url)
.then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
return response;
})
.then(response => response.json())
.then(response => {
sessionStorage.setItem(storageKey, JSON.stringify(response))
return response;
})
.catch(function(err) {
console.log('Fetch Error :-S', err);
})
}
function Typeahead(element) {
init.call(this);
function init() {
var input = true;
if (input) {
console.log('input')
let citiesData = fetchPromise('/echo/json/', 'citiesJson');
Promise.all([citiesData]).then(function(values) {
items = values[0];
console.log(values);
});
}
}
}
function fetchPromise(url, storageKey) {
if (sessionStorage.getItem(storageKey)) {
let storageKeyVal = JSON.parse(sessionStorage.getItem(storageKey));
return storageKeyVal;
}
return fetch(url)
.then(function(response) {
if (response.status !== 200) {
console.log('Looks like there was a problem. Status Code: ' +
response.status);
return;
}
return response;
})
.then(response => response.json())
.then(response => {
sessionStorage.setItem(storageKey, JSON.stringify(response))
return response;
})
.catch(function(err) {
console.log('Fetch Error :-S', err);
})
}
var typeaheadElements = [].slice.call(document.getElementsByClassName('typeahead'));
if (typeaheadElements.length) {
console.log('typeaheadfound');
typeaheadElements.map(function(element) {
new Typeahead(element);
});
}
<div class="typeahead">
Typeahead
</div>
<div class="typeahead">
Typeahead
</div>
My "dirty" solution:
Typeahead.js
import fetchPromise from '../utilities/fetchSetStorage.js';
let isFetched = false;
function Typeahead(element) {
init.call(this);
function init() {
var input = element.getElementsByTagName('input')[0];
if (input && !isFetched) {
isFetched = true;
let citiesData = fetchPromise('./cities.json', 'citiesJson');
Promise.all([citiesData]).then(function(values) {
items = values[0];
element.getElementsByTagName('input')[0].removeAttribute('disabled');
});
input.addEventListener('input', handleChange.bind(this));
element.addEventListener('keydown', handleKeyDown.bind(this));
element.addEventListener('click', handleElementClick.bind(this));
document.addEventListener('click', handleDocumentClick.bind(this));
}
}
}
Related
I am looping the array using bluebird map method and then using each row as a payload to my apicall function. Everything works perfectly but I want to incorporate timeout method when response from the api takes more than 10 seconds and also settimeout method to delay 2 seconds after each api call. Please let me know how can I acheive this. I am fine without using bluebird. Thanks in advance.
handleSubmit = () => {
Promise.map(this.props.data, row => {
return apiCall(api, row).then((response) => {
if(response){
console.log(response)
} else{
console.log("failed")
}
})
}, { concurrency: 1 } )
}
apiCall: (api, input ) => {
switch (process.env.NODE_ENV) {
case 'production': { // Production environment
return new Promise((resolve) => {
window.runApi(api, input, (response) => {
if (typeof response === 'string') {
const jsonResponse = JSON.parse(response);
resolve(jsonResponse);
} else {
resolve(response);
}
});
});
}
default: {
const requestBody = input;
if (input !== "") {
requestBody.username = "user";
requestBody.password = "password";
}
const requestUrl = `api`;
return fetch(requestUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
})
.then(res => res.json());
}
}
}
I´m using this code to retrieve my contacts list inside a firebase function following the
(https://github.com/googleapis/google-api-nodejs-client/blob/master/samples/people/contacts.js) example
const { google } = require('googleapis');
const clientAuth = require('./clientAuth');
exports.getGoogleContacts = functions.https.onCall(async (data, context) => {
console.log('getGoogleContacts- init function')
const contacts = google.people({
version: 'v1',
auth: clientAuth.oAuth2Client,
});
return new Promise((resolve, reject) => {
clientAuth
.authenticate(['https://www.googleapis.com/auth/contacts'])
.then(async () => {
console.log('after client auth')
contacts.people.connections.list({
resourceName: "people/me",
pageSize:200,
personFields: ['addresses',
'ageRanges',
'biographies',
'birthdays',
'braggingRights',
'coverPhotos'], // ... and many other fields
}, function (err, response) {
if (err) {
console.log('contacts.people.connections error')
console.log(err)
reject(new Error(err))
} else if (response) {
console.log('contacts.people.connections response')
console.log(response)
if (response.pageToken) {
// how could I continue to retrieve next page of contacts?
}
resolve(response)
}
})
})
})
})
If there is a nextPageToken, there is no working example I´ve was able to found.
edited - this code was able to solve the pagination with array concatenation
I was able to come up with this solution, even though now I´m facing a problem of pushing to the connections array... It´s not working
const listOptions = {
resourceName: "people/me",
pageSize: 200,
personFields: ['addre...']
}
async function getConnectionsList(contacts, nextPageToken) {
if (!nextPageToken) {
return contacts.people.connections.list(listOptions)
} else {
listOptions.pageToken = nextPageToken
return contacts.people.connections.list(listOptions)
}
}
let response = await getConnectionsList(contacts)
let nextPage = response.data.nextPageToken
let connections = response.data.connections
while (nextPage) {
nextPage = await getConnectionsList(contacts, nextPage)
connections.push(nextPage.data.connections) // not working
connections.concat(nextPage.data.connections) // also not working...
nextPage = nextPage.data.nextPageToken
console.log('hasNextPage?', nextPage)
}
console.log('connections',connections)
resolve(connections)
i have a function called 'updateProfile()' which has a condition which is if(emailChangeConfirm), this condition depends upon the value of variable 'emailChangeConfirm' , this variable gets the value returned by another function called 'updateEmailAllProcessing()'
the condition 'if(emailChangeConfirm)' is not getting satisfied at all because compiler is not waiting for the function 'updateEmailAllProcessing()' to return the value for variable 'emailChangeConfirm'.
I used async/await for this but that is also not working as i want
Desired Solution :
function 'updateProfile()' must wait for the function 'updateEmailAllProcessing()' to get the result in 'emailChangeConfirm' so that i can enter in the condition 'if(emailChangeConfirm)'.
I am using typescript and working on hybrid app with ionic 3 and angular 5.
async updateProfile(updatedData : Credentials,tarUser : Credentials)
{
// console.log(tarUser,'<<--->>>',updatedData)
let count : number = undefined;
let emailChangeConfirm : boolean;
if(updatedData.name)
{
if(tarUser.name != updatedData.name)
tarUser.name = updatedData.name;
else
count++;
}
if(updatedData.email)
{
if(tarUser.email != updatedData.email)
{
**emailChangeConfirm = await this.updateEmailAllProcessing();**
console.log(emailChangeConfirm)
**if(emailChangeConfirm)
tarUser.email = updatedData.email;**
}
else
count++;
}
if(updatedData.phoneNo)
{
if(tarUser.phoneNo != updatedData.phoneNo)
tarUser.phoneNo = updatedData.phoneNo;
else
count++;
}
if(updatedData.photoURL)
{
if(tarUser.photoURL != updatedData.photoURL)
tarUser.photoURL = updatedData.photoURL;
else
count++;
}
if(count)
this.mesService.presentToast('Nothing Updated!!')
else **if(emailChangeConfirm)**
{
this.dbServe.editUser(tarUser).then(() =>
{
console.log("User Edited Successfully with email change too");
this.authServ.updateEmail(tarUser.email).then(() =>
{
console.log('login email updated');
this.authServ.logout();
})
//this.close();
})
}
else
{
this.dbServe.editUser(tarUser).then(() =>
{
console.log("User Edited Successfully with No email change");
this.close();
})
}
}
**async updateEmailAllProcessing()**
{
let result : boolean;
let alert = this.mesService.emailChangeConfirmation();
alert.present();
alert.onDidDismiss((data) => {
console.log('data->',data);
if(data)
{
let alert1 = this.mesService.passwordPrompt();
alert1.present();
alert1.onDidDismiss(data1 =>
{
console.log('password->',data1);
if(data1)
{
this.authServ.reauthenticateUser(data1).then(() =>
{
console.log('User Reauth Done');
result = true;
})
}
else
result = false;
})
}
else
result = false;
})
**return result;**
}
You need updateEmailAllProcessing to return a promise so you can await on it. and resolve the promise with the result inside the callback.
async updateEmailAllProcessing()
{
return new Promise((resolve, reject) => {
let result: boolean;
let alert = this.mesService.emailChangeConfirmation();
alert.present();
alert.onDidDismiss((data) => {
console.log('data->', data);
if (data) {
let alert1 = this.mesService.passwordPrompt();
alert1.present();
alert1.onDidDismiss(data1 => {
console.log('password->', data1);
if (data1) {
this.authServ.reauthenticateUser(data1).then(() => {
console.log('User Reauth Done');
resolve(true);
})
}
else
resolve(false);
})
}
else
resolve(false);
})
});
}
I use axios with vue.js to fetch data as unlimited pagination. It works fine expect when there is no data to render:
fetchData() {
this.loading = true
this.page++;
axios.get(this.BASE_URL + '/api/jokes/'+'?page='+this.page).then(
response =>
//how to exist if there is no data in the response?
this.jokes = response.data).catch(function (error) {
console.log(error);
});
I'm wondering how to stop rendering when we reached to the last page and there is no more data to display?
I've looked at the docs but could not find my answer.
Maybe introducing some basic flag logics. I have taken the freedom to assume, but you can always define your logic
fetchData() {
this.loading = true;
if (this.page > 0) {
axios.get(this.BASE_URL + '/api/jokes/page=' + this.page)
.then(response => {
const isDataAvailable = response.data && response.data.length;
this.jokes = isDataAvailable ? response.data : [];
this.page = isDataAvailable ? (this.page + 1) : 0;
})
.catch(function(error) {
console.error(error);
});
}
}
You could throw a custom error that will be catch by the catch method.
fetchData() {
this.loading = true
this.page++;
axios.get(this.BASE_URL + '/api/jokes/'+'?page='+this.page).then(
response => {
//how to exist if there is no data in the response?
if (!response.data || response.data.length == 0) {
const emptyDataError = new Error('Invalid data');
emptyDataError.statusCode = 500;
throw emptyDataError;
}
this.jokes = response.data;
}).catch(function (error) {
console.log(error);
});
I want to display drive file metadata
"iconLink",
"thumbnailLink"
of each of the files in drive, but getting output for fields like kind, id, name, mimeType only. While other fields are not displayed.
function loadDriveApi() {
gapi.client.load('drive', 'v3', listFiles);
}
function listFiles() {
var request = gapi.client.drive.files.list({});
request.execute(function(resp) {
var files = resp.files;
if (files && files.length > 0) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
appendPre(file.iconLink);
}
} else {
appendPre('No files found.');
}
});
}
at least you will need the scope: https://www.googleapis.com/auth/drive.metadata.readonly + OAuth (API-Key & Client-ID)
you can test it at: https://developers.google.com/drive/v3/reference/files/list
in the "fields" input add: files(iconLink,thumbnailLink)
if you use https://apis.google.com/js/api.js, be sure to add your domain to API-Key -> HTTP-Referrer & Client-ID -> JavaScript-Source & Forwarding-URI (# https://console.developers.google.com/apis/credentials)
you can find a basic gapi usage sample here: https://github.com/google/google-api-javascript-client/blob/51aa25bed4f6c36d8e76fd3b9f7e280ded945c98/samples/loadedDiscovery.html
I promisified the gapi client a bit some time ago, because I disliked the mix of callbacks and thenables in the methods.. this worked for me (assuming api.js was already loaded), but only hold 100 file entries in the response.
window.gapiPromisified = {
apiKey: 'XXXXXXXXXXX',
clientId: 'XXXXX-XXXXXX.apps.googleusercontent.com'
}
window.gapiPromisified.init = function init () {
return new Promise(resolve => {
gapi.load('client:auth2', () => {
if (!document.getElementById('gapiAuthButton')) {
let authButton = document.createElement('button')
authButton.id = 'gapiAuthButton'
authButton.style.display = 'none'
authButton.style.marginLeft = 'auto'
authButton.style.marginRight = 0
document.body.insertBefore(authButton, document.body.firstChild)
authButton.addEventListener('click', () => {
let GoogleAuth = gapi.auth2.getAuthInstance()
if (GoogleAuth.isSignedIn.get()) {
GoogleAuth.signOut()
} else {
GoogleAuth.signIn()
}
})
}
gapi.client.setApiKey(this.apiKey)
gapi.auth2.init({ client_id: this.clientId })
.then(() => resolve())
})
})
}
window.gapiPromisified.signIn = function signIn () {
return new Promise(resolve => {
let GoogleAuth = gapi.auth2.getAuthInstance()
// Listen for sign-in state changes
GoogleAuth.isSignedIn.listen(isSignedIn => {
let authButton = document.getElementById('gapiAuthButton')
if (isSignedIn) {
authButton.textContent = 'Sign-out'
resolve()
} else {
authButton.textContent = 'Sign-in'
}
})
// Handle the initial sign-in state
let authButton = document.getElementById('gapiAuthButton')
let isSignedIn = GoogleAuth.isSignedIn.get()
authButton.textContent = (isSignedIn) ? 'Sign-out' : 'Sign-in'
document.getElementById('gapiAuthButton').style.display = 'block'
if (isSignedIn) {
resolve()
} else {
GoogleAuth.signIn()
}
})
}
window.gapiPromisified.getReady = function getReady () {
if (!gapi.hasOwnProperty('auth2')) {
return this.init()
.then(() => this.signIn())
} else {
if (gapi.auth2.getAuthInstance().isSignedIn.get()) {
return Promise.resolve()
} else {
return this.signIn()
}
}
}
window.gapiPromisified.getScopes = function getScopes (scopes) {
return new Promise((resolve, reject) => {
let GoogleUser = gapi.auth2.getAuthInstance().currentUser.get()
if (GoogleUser.hasGrantedScopes(scopes)) {
resolve()
} else {
// method returns goog.Thenable
GoogleUser.grant({ 'scope': scopes })
.then(onFulfilled => {
resolve(onFulfilled)
}, onRejected => {
reject(onRejected)
})
}
})
}
window.gapiPromisified.loadAPI = function loadAPI (urlOrObject) {
return new Promise((resolve, reject) => {
// method returns goog.Thenable
gapi.client.load(urlOrObject)
.then(onFulfilled => {
resolve(onFulfilled)
}, onRejected => {
reject(onRejected)
})
})
}
window.gapiPromisified.metadata = function metadata () {
return new Promise((resolve, reject) => {
this.getReady()
.then(() => this.getScopes('https://www.googleapis.com/auth/drive.metadata.readonly'))
.then(() => this.loadAPI('https://www.googleapis.com/discovery/v1/apis/drive/v3/rest'))
.then(() => {
gapi.client.drive.files.list({
fields: 'files(iconLink,thumbnailLink)'
})
.then(onFulfilled => {
resolve(onFulfilled)
}, onRejected => {
reject(onRejected)
})
})
})
}
window.gapiPromisified.metadata()
.then(res => {
res.result.files.forEach(file => {
console.log(file.iconLink)
console.log(file.thumbnailLink)
})
})
In the v3 you need to specify which fields you want included in the metadata response. See the fields= parameter