I have over 3200 rows in a Google Sheet. I need a dropdown with each value on a web app.
I have this in Apps Script:
function doGet(e) {
var htmlOutput = HtmlService.createTemplateFromFile('CensusWebApp2');
var streets = getStreets();
var businessNames = getbusinessNames();
htmlOutput.message = '';
htmlOutput.streets = streets;
htmlOutput.businessNames = businessNames;
return htmlOutput.evaluate();
}
function getbusinessNames(){
var ss= SpreadsheetApp.getActiveSpreadsheet();
var StreetDataSheet = ss.getSheetByName("businessNames");
var getLastRow = StreetDataSheet.getLastRow();
var return_array = [];
return_array= StreetDataSheet.getRange(2,1,getLastRow-1,1).getValues();
return return_array;
}
This is the HTML code
<select type="select" name="IntestazioneTari" id="IntestazioneTari" class="form-control" >r>
<option value="" ></option>
<? for(var i = 0; i < businessNames.length; i++) { ?>
<option value="<?= businessNames[i] ?>" ><?= businessNames[i] ?></option>
<? } ?>
</select><be>
I'm creating an app similar to surveys forms, but this dropdown will be the same for every entry.
Is there a way to load this only once and not every time the form is submitted and got again for a new survey entry? (from the same operator/device)
I believe your goal as follows.
You want to use the value of businessNames retrieved from Google Spreadsheet at HTML side.
The value of businessNames is not changed. So you want to load the value only one time.
In this case, how about declaring the value in the tag of <script> as a global? When this point is reflected to your script it becomes as follows.
Modified script:
In this case, your HTML side is modified.
<select type="select" name="IntestazioneTari" id="IntestazioneTari" class="form-control" >r>
<option value="" ></option>
<? for(var i = 0; i < businessNames.length; i++) { ?>
<option value="<?= businessNames[i] ?>" ><?= businessNames[i] ?></option>
<? } ?>
</select>
<input type="button" value="ok" onclick="test()">
<script>
const value = JSON.parse(<?= JSON.stringify(businessNames) ?>); // Here, the value of "businessNames" is retrieved.
function test() {
console.log(value);
}
</script>
In this modification, when the HTML is loaded, the value of businessNames is added to the HTML by evaluate() method. At that time, businessNames is given to HTML and Javascript. By this, const value = JSON.parse(<?= JSON.stringify(businessNames) ?>); has the value of businessNames. In order to confirm this value, when you click a sample button of <input type="button" value="ok" onclick="test()">, you can see the value at the console. By this, you can use the value of businessNames at the Javascript side after HTML is loaded.
Reference:
HTML Service: Templated HTML
As the values from the spreadsheet won't change, I created a really long text row with all the options and pasted them directly in the HTML.
I made the same with the other information. Load time decreased enormously.
This is the code I use to generate the values:
return_array= "<option>" + businessNamesSheet.getRange(2,1,getLastRow-1,1).getValues().join("</option><option>")+"</option>";
We are talking about performance, and there are 3 things you need to do when doing so:
Make measurements
Make measurements again
And make some more measurements
A change in the code could have a negative impact for a reason that you didn't expect (it's hard to keep every single little detail in mind). When making a Google Apps Script web app, you have 3 reported times:
The timings in your browser. How much did it really take to load the entire page
The running time on Google Apps Script execution log.
Small timing inside your application using console.time (reference), console.timeLog (reference) and console.timeEnd (reference) (collectively called console timers).
Note that the first 2 may change without you changing a thing, probably because of the inner working in Google.
So let's start doing what I said: measuring. I'd measure:
The entire doGet function
The getStreets()
The getbusinessNames()
The template.evaluate()
How much time it takes to load the page (browser)
This will give me a rough idea on what takes most of the time. Knowing that, you can try the following ideas.
Note that I don't have your code so I can't tell how it will effect your times, so your mileage may vary. Also note that most ideas could be implemented simultaneously, this doesn't mean it's a good idea and can even slow what a single idea could have achieved.
Idea 1: Copy the generated options into the template
If you don't need to load the options from somewhere (like I suppose you are doing), you could generate the template once, copy the generated options and paste it to the HTML. This will obviously avoid the problem of having the request the list of options and evaluating them every time, but you lose flexibility.
Idea 2: Having the options in code instead of somewhere else
If the options won't change or you will be changing them, you could add them into your code:
const BUSINESS_NAMES = `
business 1
hello world
another one
and another one
`
function getbusinessNames() {
return BUSINESS_NAMES
.split('\n')
.filter(v => !!v) // remove empty string
}
It's similar to idea 1 but it's easier to change the values when needed, specially when using the V8 support for multi line strings.
Idea 3: Use a Google Apps Script cache
If what's taking time is querying the options, you could use CacheService (see reference). This would allow you to only query the options every X seconds (up to 6 hours) instead of every time.
function doGet(e) {
// [...]
const cache = CacheService.getScriptCache()
let businessNames = cache.get('businessNames')
if (businessNames == null) {
businessNames = getbusinessNames()
cache.put('businessNames', businessNames, 6*60*60)
}
// use businessNames
// [...]
}
In this case I've only done it with businessName but it can also be use in streets.
having a 6 hour cache means that it could take up to 6 hours for a change in the list to propagate. If you add the options manually you could add a function to force the reloading it:
function forcecacheRealod() {
cache.put('streets', getStreets(), 6*60*60)
cache.put('businessNames', getbusinessNames(), 6*60*60)
}
Idea 4: Improve how you load the data
Is very common for new Google Apps Script users to iterate the rows one by one getting the value. It's way more efficient to get the proper range with all the rows and columns and call getValues (reference).
Idea 5: do a fetch instead of submitting the form
If what is happening is that it takes time to load after sending the data, it might be a good idea to use google.script.run (reference) instead of making a form and submitting it, since it could prevent reloading the entire page again.
Idea 6: SPA web app
The result of doubling down on the last idea. Same benefits and you could load the necessary data in the background while the user lands on the home page.
Idea 7: Load the options dynamically
Use google.script.run (reference) to load the options once the page has already been loaded. May actually be slower but you can give faster feedback to the user.
Idea 8: Save the options in localStorage
Requires idea #7. Save the dynamically loaded options into localStorage(see reference) so the user only needs to wait once. You may need to load them once in a while to make sure they are up-to-date.
References
Console timer (MDN)
CacheService (Google Apps Script reference)
Range.getValues() (Google Apps Script reference)
Class google.script.run (Client-side API) (Google Apps Script reference)
Window.localStorage (MDN)
Related
I want to make 3 dependents drop down list, each drop down dependent to the previous drop down, so when I select an item from first drop down , all data fetch from database and add to second drop down as item.
I know how to do this in a normal php page using ajax, but as opencart uses MVC I don't know how can I get the selected value
Basically, you need two things:
(1) Handling list changes
Add an event handler to each list that gets its selected value when it changes (the part that you already know), detailed tutorial here in case someone needed it
Just a suggestion (for code optimization), instead of associating a separate JS function to each list and repeating the code, you can write the function once, pass it the ID of the changing list along with the ID of the depending list and use it anywhere.
Your HTML should look like
<select id="list1" onchange="populateList('list1', 'list2')">
...
</select>
<select id="list2" onchange="populateList('list2', 'list3')">
...
</select>
<select id="list3">
...
</select>
and your JS
function populateList(listID, depListID)
{
// get the value of the changed list thorugh fetching the elment with ID "listID"
var listValue = ...
// get the values to be set in the depending list through AJAX
var depListValues = ...
// populate the depending list (element with ID "depListID")
}
(2) Populating the depending list
Send the value through AJAX to the appropriate PHP function and get the values back to update the depending list (the part you are asking for), AJAX detailed tutorial here
open cart uses the front controller design patter for routing, the URL always looks like: bla bla bla.bla/index.php?route=x/y/z&other parameters, x = folder name that contains a set of class files, y = file name that contains a specific class, z = the function to be called in that class (if omitted, index() will be called)
So the answer for your question is:
(Step 1) Use the following URL in your AJAX request:
index.php?route=common/home/populateList
(Step 2) Open the file <OC_ROOT>/catalog/controller/common/home.php , you will find class ControllerCommonHome, add a new function with the name populateList and add your logic there
(Step 3) To use the database object, I answered that previously here
Note: if you are at the admin side, there is a security token that MUST be present in all links along with the route, use that URL:
index.php?route=common/home/populateList&token=<?php echo $this->session->data['token'] ?> and manipulate the file at the admin folder not the catalog
P.S: Whenever the user changes the selected value in list # i, you should update options in list # i + 1 and reset all the following lists list # i + 2, list # i + 3 ..., so in your case you should always reset the third list when the first list value is changed
P.P.S: A very good guide for OC 1.5.x => here (It can also be used as a reference for OC 2.x with some modifications)
I'm loading some posts though AJAX and my WordPress pagination is using the following function to calculate paging:
get_pagenum_link($paged - 1)
The issue is that the pagination is getting created through AJAX so it's making this link look like: http://localhost:1234/vendor_new/wp-admin/admin-ajax.php
However the actual URL that I'm trying to achieve is for this:
http://localhost:1234/vendor_new/display-vendor-results
Is there a way to use this function with AJAX and still get the correct URL for paging?
I can think of three options for you:
To write your own version of get_pagenum_link() that would allow you to specify the base URL
To overwrite the $_SERVER['REQUEST_URI'] variable while you call get_pagenum_link()
To call the paginate_links() function, return the whole pagination's HTML and then process that with JS to only take the prev/next links.
#1 Custom version of get_pagenum_link()
Pros: you would have to change a small amount of your current code - basically just change the name of the function you're calling and pass an extra argument.
Cons: if the function changes in the future(unlikely, but possible), you'd have to adjust your function as well.
I will only post the relevant code of the custom function - you can assume everything else can be left the way it's in the core version.
function my_get_pagenum_link( $pagenum = 1, $escape = true, $base = null ) {
global $wp_rewrite;
$pagenum = (int) $pagenum;
$request = $base ? remove_query_arg( 'paged', $base ) : remove_query_arg( 'paged' );
So in this case, we have one more argument that allows us to specify a base URL - it would be up to you to either hard-code the URL(not a good idea), or dynamically generate it. Here's how your code that handles the AJAX request would change:
my_get_pagenum_link( $paged - 1, true, 'http://localhost:1234/vendor_new/display-vendor-results' );
And that's about it for this solution.
#2 overwrite the $_SERVER['REQUEST_URI'] variable
Pros: Rather easy to implement, should be future-proof.
Cons: Might have side effects(in theory it shouldn't, but you never know); you might have to edit your JS code.
You can overwrite it with a value that you get on the back-end, or with a value that you pass with your AJAX request(so in your AJAX request, you can have a parameter for instance base that would be something like window.location.pathname + window.location.search). Difference is that in the second case, your JS would work from any page(if in the future you end-up having multiple locations use the same AJAX handler).
I will post the code that overwrites the variable and then restores it.
// Static base - making it dynamic is highly recommended
$base = '/vendor_new/display-vendor-results';
$orig_req_uri = $_SERVER['REQUEST_URI'];
// Overwrite the REQUEST_URI variable
$_SERVER['REQUEST_URI'] = $base;
// Get the pagination link
get_pagenum_link( $paged - 1 );
// Restore the original REQUEST_URI - in case anything else would resort on it
$_SERVER['REQUEST_URI'] = $orig_req_uri;
What happens here is that we simply override the REQUEST_URI variable with our own - this way we fool the add_query_arg function into thinking, that we're on the /vendor_new/display-vendor-results page and not on /wp-admin/admin-ajax.php
#3 Use paginate_links() and manipulate the HTML with JS
Pros: Can't really think of any at the moment.
Cons: You would have to adjust both your PHP and your JavaScript code.
Here is the idea: you use paginate_links() with it's arguments to create all of the pagination links(well - at least four of them - prev/next and first/last). Then you pass all of that HTML as an argument in your response(if you're using JSON - or as part of the response if you're just returning the HTML).
PHP code:
global $wp_rewrite, $wp_query;
// Again - hard coded, you should make it dynamic though
$base = trailingslashit( 'http://localhost:1234/vendor_new/display-vendor-results' ) . "{$wp_rewrite->pagination_base}/%#%/";
$html = '<div class="mypagination">' . paginate_links( array(
'base' => $base,
'format' => '?paged=%#%',
'current' => max( 1, $paged ),
'total' => $wp_query->max_num_pages,
'mid_size' => 0,
'end_size' => 1,
) ) . '</div>';
JS code(it's supposed to be inside of your AJAX success callback):
// the html variable is supposed to hold the AJAX response
// either just the pagination or the whole response
jQuery( html ).find('.mypagination > *:not(.page-numbers.next,.page-numbers.prev)').remove();
What happens here is that we find all elements that are inside the <div class="mypagination">, except the prev/next links and we remove them.
To wrap it up:
The easiest solution is probably #2, but if someone for some reason needs to know that the current page is admin-ajax.php while you are generating the links, then you might have an issue. The chances are that no one would even notice, since it would be your code that is running and any functions that could be attached to filters should also think that they are on the page you need(otherwise they might mess something up).
PS: If it was up to me, I was going to always use the paginate_links() function and display the page numbers on the front-end. I would then use the same function to generate the updated HTML in the AJAX handler.
This is actually hard to answer without specific details of what and how is being called. I bet you want to implement that in some kind of endless-sroll website, right?
Your best bet is to get via AJAX the paginated page itself, and grab the related markup.
Assume you have a post http://www.yourdomain.com/post-1/
I guess you want to grab the pagination of the next page, therefore you need something like this:
$( "#pagination" ).load( "http://www.yourdomain.com/post-1/page/2 #pagination" );
This can easily work with get_next_posts_link() instead of get_pagenum_link().
Now, in order for your AJAX call to be dynamic, you could something like:
$( "#pagination" ).load( $("#pagination a").attr('href') + " #pagination" );
This will grab the next page's link from your current page, and load its pagination markup in place of the old.
It's also doable with get_pagenum_link() however you'd need to change the $("#pagination a").attr('href') selector appropriately, in order to get the next page (since you'd have more than one a elements inside #pagination
I have HTML in this format:
<form name="fruit_name">
<input id="fruit-name" type="hidden" name="Banana">
</form>
I have Dart querying the fruit name like this:
var fruitName = query('#fruit-name').attributes.values.last;
This works great in Chrome and Safari. But In Firefox, the attributes come back in a different order, so name is no longer last. What's the best way to grab the attribute I'm after without relying on the browser so much?
attributes is a Map, so this should work:
var fruitName = query('#fruit-name').attributes['name'];
You can use :
var fruitName = query('input#fruit-name').name;
The result of the query is in fact a InputElement and you have more member that in a simple Element.
By prepending #fruit-name with input you will tell to the analyzer that the result of query is an InputElement. Without that you would get a warning ( There is no such getter 'name' in 'Element' ).
Finally, from a performance point of view, the best way to do this is with document.getElementById(id) because getElementById is really faster than querySelector ) :
InputElement fruitNameElement = document.getElementById('fruit-name');
var fruitName = fruitNameElement.name;
Here, the first line allows to type fruitNameElement to prevent warning when calling fruitNameElement.name.
I have the following snippet code hooked up to a FormIt email form:
$tv = "taken" . (int)$hook->getValue('datetime');
$docID = $modx->resource->get('id'); //get the page id
$page = $modx->getObject('modResource', $docID);
$current = (int)$page->getTVValue($tv);
if (!$page->setTVValue($tv, $current + 1)) {
$modx->log(xPDO::LOG_LEVEL_ERROR, 'There was a problem saving your TV...');
}
$modx->setPlaceholder('successMessage','<h2 class="success">'.$current.'</h2>');
return true;`
It increments a template variable every time it is run and outputs a success message (although right now I'm using that functionality to output a debug message instead). The problem is, it only increments the TV once after saving the snippet, thereby refreshing the cache. Normally I would call the snippet without cache by appending ! to its name, but that doesn't appear to work for FormIt hooks. How can I get this code to work? Right now I'm running the entire page as uncacheable, but that is obviously suboptimal. Perhaps, there's a way to hook a snippet in an uncached manner? Call a snippet from within a snippet as uncached?
I'm doing something similar - but to count page loads, it looks to me like you are missing the last little bit: $current->save();
<?php
$docID = $modx->resource->get('id');
$tvIdm = 32;
$tvm = $modx->getObject('modTemplateVar',$tvIdm );
$tvm->setValue($docID, $tvm->getValue($docID) + 1 );
$tvm->save();
Try add this before you save $tv object
$tv->_processed = false;
It's derived from modElement's property it extends.
I have been working on a django project that requires a large amount of user input and processing and am sick of hardcoding the data in the view in order to save it to my models as seen below.
mymodel = TheModel.objects.get(id=model.id)
mymodel.name = request.POST.get('name')
mymodel.zip = request.POST.get('zip')
...
mymodel.save()
Except instead of two model attributes like I used above there are sometimes up to 25 that need to be saved.
I am using ajax to serialize the forms and send them to my views where they are saved. I am looking for the cleanest way possible to get around this problem. Less code the better and I am willing to reformat my models if there is a way that significantly shortens the number of lines of code I have now.
Thanks
you may want to have a look at ModelForms
This method will work, although you have to be careful to add a model field / ajax parameter at the sametime for it to work
Given:
Form1
<form method="post">
<input name="parameter1" />
<input name="parameter2" />
<input name="parameter3" />
</form>
Write javascript code so the data going across the wire looks like this JSON (form serialization
will probably not work)
{ parameter1 : "some data", "parameter2" : "some data", parameter3 : "some data" }
Then, you have a django model that looks like this
class MyModel(models.Model):
parameter1 = models.StringField()
parameter2 = models.StringField()
parameter3 = models.StringField()
You can save/update with code like this:
params = dict(request.POST)
m = MyModel.objects.create(**params)
or
m = MyModel.objects.get(id=ID)
m.update(force_update=False,**params)
If your parameters do not line up the code will fail though.