I hope everyone is safe and healthy given the current situation.
I have a question in regards to a project with google apps script. I have a web app and I have been able to figure out routing with doGet() using links etc.
//global variables
const sheetId = "foo";
const Route = {};
Route.path = function(route, callback){
Route[route] = callback;
}
function doGet(e){
Route.path("newAccountForm",loadNewForm);
Route.path("updateBrandForm", loadUpdateForm);
if(Route[e.parameters.v]) {
return Route[e.parameters.v]();
} else {
return render("home")
}
};
function loadNewForm() {
const sheetActive = SpreadsheetApp.openById(sheetId);
const mySheet = sheetActive.getSheetByName("Sheet1");
const title = "title";
const index = "index";
return render("addNewAccount",{title: title, index: index});
}
function loadUpdateForm () {
const sheetActive = SpreadsheetApp.openById(sheetId);
const mySheet = sheetActive.getSheetByName("Sheet1");
return render("updateBrand");
}
function render(file,argsObject) {
const tmp = HtmlService.createTemplateFromFile(file);
if(argsObject) {
const keys = Object.keys(argsObject);
keys.forEach(function(key){
tmp[key] = argsObject[key];
})
} // END IF
return tmp.evaluate();
}
The links..
Add New Brand
Update Exisiting Brand
Analytics / Reports
Now I am a bit stuck on handling responses and errors. I have tried using doPost() which works to render a new HTML page. My problem is I am unsure how to tell if the request was successful in the doPost. Is there a way to check that? I can get all the parameters through the event object but not a status.
<form id="myForm" onsubmit="handleNewAccountFormSubmit(this);" method="post" action="<?= ScriptApp.getService().getUrl(); ?>">
I have also been trying to handle it with the included .withFailureHandler() but am unsure how to get it to fire or if it is possible to call back a function from my .GS
I have tried also having the onFail() function outside the FormSubmit function.
function handleNewAccountFormSubmit(formObject) {
google.script.run.withFailureHandler(onFail).withSuccessHandler().processNewAccountForm(formObject);
function onFail(error) {
Logger.log(error)
console.log(error)
return google.script.run.onError();
}
}
I basically want to show if the function ran successfully for user experience but am unsure of best practise or how or even if it is possible(I am sure it is!)
I look forward to any ideas, corrections, and if something is unclear I will do my best to provide more info.
Thanks again.
Use success or failure handlers to alert the user:
function handleNewAccountFormSubmit(formObject) {
alert("Please wait..!")
google.script.run
.withFailureHandler(e => {
console.error(e.message);
alert("Unexpected error! Contact support!")
})
.withSuccessHandler(e => alert("Form submitted successfully!"))
.processNewAccountForm(formObject);
}
Related
I am making a game and am working on the accounts. When i have JSON stringify the object and save it to a file, it sometimes (occasionally and at random) writes:
{"example#domain.cm":{"email":"example#domain.cm","password":"myPassword","token":"c26a2a66-77f8-43d7-aa92-14da81979386"} >}< "example#domain.com":{"email":"example#domain.com","password":"myPassword","token":"209758d0-9a6e-4e99-835a-21595b822796"}}
when i am expecting:
{"example#domain.cm":{"email":"example#domain.cm","password":"myPassword","token":"c26a2a66-77f8-43d7-aa92-14da81979386"} >,< "example#domain.com":{"email":"example#domain.com","password":"myPassword","token":"209758d0-9a6e-4e99-835a-21595b822796"}}
My Code:
const fs = require('fs');
const { v4: newUuid } = require('uuid');
function save() {
fs.writeFile('save_info/users.json', JSON.stringify(User.USERS), err => {});
}
class User {
static USERS = {};
constructor(email, password, token = newUuid()) {
this.email = email;
this.password = password;
this.token = token;
User.USERS[email] = this;
save();
}
}
What is going on?
EDIT
I am using nodemon. Whenever i save a file (except users.json), it automatically stops and starts it. I am also using express because this script is for the server part. (This is a private project, not looking to make it perfect, just learn)
Thank you, #jabaa
they have pointed out a warning that using fs.writeFile() multiple times without waiting for the callback is unsafe. Here is the solution:
var CAN_SAVE = true;
var PENDING_SAVE = false;
function save() {
if (!CAN_SAVE) {
PENDING_SAVE = true;
return;
}
PENDING_SAVE = false;
CAN_SAVE = false;
fs.writeFile('save_info/users.json', JSON.stringify(User.USERS), err => {
CAN_SAVE = true;
if (PENDING_SAVE) save();
});
}
Another solution is to use fs.writeFileSync(), which might actually be better in my case... (Thank you, #Steven Spungin)
I've never created a Javascript module/library before so this is a bit new to me so apologizes for my lack of knowing what to google.
I'm creating a library that will hold information from a URL that is provided by a user. I want to parse the URL's path (the part that comes after the domain) as well as retain a header value that's provided by the URL's response.
It's basic but here's what I have so far:
function Link(someURL) {
this.url = someURL;
this.urlPath = "";
this.uuid = "";
this.getPath = function (someURL) {
// do regexp parsing and return everything after the domain
};
this.getUUID = function (someURL) {
// fetch the URL and return what is in the response's "uuid" header
}
}
Ideally, I'd the module to automatically get all the information upon construction:
var foo = new Link("http://httpbin.org/response-headers?uuid=36d09ff2-4b27-411a-9155-e82210a100c3")
console.log(foo.urlPath); // should return "uuid"
console.log(foo.uuid); // should return the contents in the "uuid" header in the response
How do I ensure the this.urlPath and this.uuid properties get initialized along with this.url? Ideally, I'd only fetch the URL once (to prevent rate limiting by the target server).
After a lot of trial and error, I ended up doing something more like this:
class Link {
constructor (url_in) {
const re = RegExp("^https://somedomain.com\/(.*)$");
this.url = re[0];
this.linkPath = re[1];
}
async getUUID() {
const res = await fetch("https://fakedomain.com/getUUID?secret=" + this.linkPath);
this.uuid = res.uuid;
}
async getJSON() {
const res = await fetch("https://fakedomain.com/getJSON?uuid=" + this.uuid);
this.json = await res.json();
}
async initialize() {
await this.getUUID();
await this.getJSON();
}
}
const someLinkData = new Link("https://reallydumbdomain.com/2020/10/4/blog");
someLinkData.initialize()
.then(function() {
console.log(this.json); // this now works
});
I think a future iteration of this will require me to send a promise with the initialize function but for now, this works.
I'm trying to pass some values into another screen, it worked the first time when I tried it with one value, using async storage set for a single item, however, now I am trying it with multiple and it keeps crashing every time I press the item I want to get the data from.
Storing the data when I press on an item from a FlatList
fetchOnPressOpacity = async item => {
this.state.totalCalories += item.food.nutrients.ENERC_KCAL;
this.state.totalFat += item.food.nutrients.FAT;
this.state.totalCarbs += item.food.nutrients.CHOCDF;
this.state.totalProtein += item.food.nutrients.PROCNT;
const firstPair = ["totalCalories", JSON.stringify(this.state.totalCalories)];
const secondPair = ["totalCarbs", JSON.stringify(this.state.totalCarbs)];
const thirdPair = ["totalProtein", JSON.stringify(this.state.totalProtein)];
const fourthPair = ["totalFat", JSON.stringify(this.state.totalFat)];
try {
this.setState({});
await AsyncStorage.multiSet(firstPair, secondPair, thirdPair, fourthPair);
} catch (error) {
console.log(error);
}
};
getData() method, I am not too sure how to store the data:
getData = async () => {
try {
const values = await AsyncStorage.multiGet([
"totalCalories",
"totalCarbs",
"totalProtein",
"totalFat"
]);
} catch (e) {
// read error
}
console.log(values);
};
So, right now my main problem is that the application crashes when I press an item.
I get the below error, but do not think this is the issue.
VirtualizedList: missing keys for items, make sure to specify a key or
id property on each item or provide a custom keyExtractor.
I am also able to write to the console the value before the app crashes.
Could you please advise me?
Simple solution
var items = [['key1', 'value1'], ['key2', 'value2']]
AsyncStorage.setItem("DATA_KEY", JSON.stringify(items))
// or
AsyncStorage.multiSet(items, () => {
//to do something
});
For your code
var items = [firstPair, secondPair, thirdPair, fourthPair];
AsyncStorage.setItem("DATA_KEY", JSON.stringify(items))
Get data
AsyncStorage.multiGet(["key1", "key2"]).then(response => {
//to do something
})
Not really a fix to your code but if it's just to pass data to another screen, you could consider to pass data with navigation.
like:
const { navigation } = this.props;
navigation.navigate('YourNextScreen',
{
totalCalories: this.state.totalCalories,
totalCarbs: this.state.totalCarbs,
totalProtein: this.state.totalProtein,
totalFat: this.state.totalFat,
});
and retrieve them in:
const {
totalCalories,
totalCarbs,
totalProtein,
totalFat
} = this.props.route.params;
in case you don't want to specifically save those data for later...
https://reactnavigation.org/docs/params/
I am currently develloping a little app with angularJS;
the users have to go through a login form and then they can click on a few links who display data fetched from a server.
The login form only takes the user's input and it is then "stored" into a factory
app.factory('IdFormHolder', function () {
var that = this;
that.setId = function (data) {
that = data;
};
that.getId = function () {
return that;
};
return that;
});
Which works fine I need to keep it stored because the server requires the login form to be sent each time I do a $http.get as a header.
Each controller takes the login form from the factory and uses it to get the data from the server.
This works fine until someone decides to refresh the page, at which point it seems the factory is "emptied" from its login form, and the web-app then fails to show anything.
Is there a way to store this login info so that it doesn't get erased so easily ?
You can use this code after youve installed sessionStorage:
app.factory('IdFormHolder', ['$sessionStorage', function ($sessionStorage) {
var that = this;
that.setId = function (data) {
$sessionStorage.id = data;
that = data;
};
that.getId = function () {
return $sessionStorage.id;
};
return that;
}]);
Download Link: https://github.com/gsklee/ngStorage
In order to persist data you'd have to use some kind of local DB || LocalStorage || SessionStorage at least. When initializing the Factory you could check and attempt to retrieve from DB/LS that data and hold it as a variable if it does exist.
Something like
app.factory('IdFormHolder', function () {
this.heldId = attemptToGetSavedDataFromSomewhere(); // would be null otherwise
this.setId = (id) => {
this.heldId = id;
};
this.getId = () => this.heldId;
return this;
});
Using angular-local-storage you can access to the browsers local storage:
app.factory('IdFormHolder', function(localStorageService) {
return {
setId: function(data) {
return localStorageService.set('loggedUserData', data);
},
getId: function() {
return localStorageService.get('loggedUserData');
}
};
});
I'm trying to convert my basic crud operations into an API that multiple components of my application can use.
I have successfully converted all methods, except the update one because it calls for each property on the object to be declared before the put request can be executed.
controller
$scope.update = function(testimonial, id) {
var data = {
name: testimonial.name,
message: testimonial.message
};
dataService.update(uri, data, $scope.id).then(function(response) {
console.log('Successfully updated!');
},
function(error) {
console.log('Error updating.');
});
}
dataService
dataService.update = function(uri, data, id) {
var rest = Restangular.one(uri, id);
angular.forEach(data, function(value, key) {
// needs to be in the format below
// rest.key = data.key
});
// needs to output something like this, depending on what the data is passed
// rest.name = data.name;
// rest.message = data.message;
return rest.put();
}
I tried to describe the problem in the codes comments, but to reiterate I cannot figure out how to generate something like rest.name = data.name; without specifying the name property because the update function shouldn't need to know the object properties.
Here is what the update method looked like before I started trying to make it usable by any of my components (this works)
Testimonial.update = function(testimonial, id) {
var rest = Restangular.one('testimonials', id);
rest.name = testimonial.name;
rest.message = testimonial.message;
return rest.put();
}
How can I recreate this without any specific properties parameters hard-coded in?
Also, my project has included lo-dash, if that helps, I don't know where to start with this problem. Thanks a ton for any advice!
Try like
angular.extend(rest,testimonial)
https://docs.angularjs.org/api/ng/function/angular.extend