I'm using the ag-grid component in my code and would like to ensure that date columns are formatted according to the customers needs when exported as CSV. A default format for a js Date object is used currently. The code can be found here:
https://github.com/ceolter/ag-grid/blob/master/src/ts/csvCreator.ts
I could make the following change to the code directly, but this is obviously bad practice. I'm fairly new to JavaScript and was wondering if there is a standard way to extend/override functionality in a library like this.
Proposed change (Note this shows the change I made to the js version not the github version which uses ts):
--- Common/scripts/agGrid/ag-grid.js (revision b0e7d54e61e6371b0cab94428cb4329f9f62db11)
+++ Common/scripts/agGrid/ag-grid.js (revision )
## -1848,7 +1848,11 ##
+ var exportDateAs = function(dt){if (dt instanceof Date)
+ return dt.getFullYear() + "/" + (dt.getMonth()+1) + "/" + dt.getDate();
+ };
## -1883,6 +1887,9 ##
+ if (valueForCell instanceof Date){
+ valueForCell = exportDateAs(valueForCell);
+ }
It looks like this functionality was added in a newer version:
https://www.ag-grid.com/javascript-grid-export/
My solution was to update the function in the ag grid component manually as follows. It appears to work, but not sure if it is best practice. Anyone willing to comment?
Replace function in object with my own:
ag.grid.CsvCreator.prototype.getDataAsCsv = agCustom.getDataAsCsv;
New function
var agCustom;
(function (agCustom) {
// This is a modified version of the getDataAsCsv from
// agGrid to allow date formatting in csv export
agCustom.getDataAsCsv = function (params) {
var LINE_SEPARATOR = '\r\n';
...
var exportDateAs = function(dt){if (dt instanceof Date)
return dt.getFullYear() + "/" + (dt.getMonth()+1) + "/" + dt.getDate();
...
else {
valueForCell = _this.valueService.getValue(column.colDef, node.data, node);
if (valueForCell instanceof Date){
valueForCell = exportDateAs(valueForCell);
}
...
})(agCustom || (agCustom = {}));
Related
Disclaimer: I fully understand the risks/downsides of using eval but this is one niche case where I couldn't find any other way.
In Google Apps Scripting, there still is no built-in capability to import a script as a library so many sheets can use the same code; but, there is a facility built-in where I can import text from a plaintext file.
Here's the eval-ing code:
var id = [The-docID-goes-here];
var code = DocsList.getFileById(id).getContentAsString();
var lib = eval(code);
Logger.log(lib.fetchDate());
Here's some example code I'm using in the external file:
{
fetchDate: function() {
var d = new Date();
var dateString = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
return dateString;
}
}
What I'm aiming for is to drop a big object literal (containing all the library code) onto a local variable so I can reference it's properties/functions like they're contained in their own namespace.
Replace var lib = eval(code); with:
var lib = eval('(' + code + ')');
When the parens are omitted, the curly braces are being interpreted as markers of a block of code. As a result, the return value of eval is the fetchData function, instead of a object containing the function.
When the function name is missing, the code inside the block is read as a labelled anonymous function statement, which is not valid.
After adding the parens, the curly braces are used as object literals (as intended), and the return value of eval is an object, with the fetchData method. Then, your code will work.
You cannot evaluate
{
fetchDate: function() {
var d = new Date();
var dateString = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
return dateString;
}
}
Because it is not a valid expression (Object literals on their own are interpreted as blocks. fetch: function () { } is not a valid expression).
Try
var myLibName = {
fetchDate: function() {
var d = new Date();
var dateString = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
return dateString;
}
};
I have a Date format like this
2014-11-18T20:50:01.462Z
I need to convert to the custom format like "20:50 2014-18-11" using Javascript date function
I need result like
20:50 2014-18-11
How to get this , Thanks in Advance :)
Assuming you're able to include new libraries on your project, I'd highly recommend moment.js (MIT license) instead of writing this yourself. It solves problems like zero padding etc. for you.
Example
<script src="http://momentjs.com/downloads/moment.min.js"></script>
<script>
// Use an existing date object
var date = new Date("2014-11-18T20:50:01.462Z");
console.log(moment(date).format('HH:mm YYYY-DD-MM'));
// or use string directly
console.log(moment.utc("2014-11-18T20:50:01.462Z").format('HH:mm YYYY-DD-MM'));
</script>
Note by default moment will use your current timezone for output, this can be overridden using the zone() function
console.log(moment.utc("2014-11-18T20:50:01.462Z").zone(0).format('HH:mm YYYY-DD-MM'));
console.log(moment.utc("2014-11-18T20:50:01.462Z").zone('UTC+05:30').format('HH:mm YYYY-DD-MM'));
Output
20:50 2014-18-11
Try moment js its very nice plugin to play around dates and times
so all you need to do is import moment js and put this line in your js code
using moment.js will also help you in future for your code
moment.utc("2014-11-18T20:50:01.462Z").format("HH:mm YYYY-DD-MM")
Use this Demo JsFiddler
var d = new Date,
dformat = [ d.getHours().padLeft(), d.getMinutes().padLeft()].join(':')
+ ' ' +
[d.getFullYear(), d.getDate().padLeft(), (d.getMonth()+1).padLeft()].join('-')
;
Date.prototype._padding = function(v, w) {
var f = "0000" + v;
return ("0000" + v).substr(f.length-w, f.length)
}
Date.prototype.MyDateString = function() {
return this._padding(this.getUTCHours(), 2) + ":" + this._padding(this.getUTCMinutes(), 2) + " " + this.getUTCFullYear() + "-" + this._padding(this.getUTCDate(), 2) + "-" + this._padding((this.getUTCMonth() + 1), 2);
}
console.log(new Date('2014-11-18T20:50:01.462Z').MyDateString())
console.log(new Date('2014-11-08T02:05:01.462Z').MyDateString())
getUTCMonth return 10, as the month is 0 based.
I was interested in writing a twitter bot to help out some friends at a local ski resort. I found this tutorial from Amit Agarwal which gave me enough to get started (it did take me more than 5 minutes since I did a lot of modifying). I host the script on google docs.
FIRST I think this is javascript (my understanding is that google apps script uses javascript...) and when I have had problems with the code so far, google searches for javascript-such-and-such have been helpful, but if this is not actually javascript, please let me know so I can update the tag accordingly!
I have no prior experience with javascript, so I am pretty happy that it's actually working. But I want to see if I'm doing this right.
The start function initiates the trigger, which kicks off the fetchTweets() function every interval (30 minutes). In order to avoid duplicates (the first errors I encountered) & potentially being flagged as spam, I needed a way to ensure that I was not posting the same tweets over and over again. Within the start() function, the initial since_id value is assigned:
ScriptProperties.setProperty("SINCE_TWITTER_ID", "404251049889759234");
Within the fetchTweet() function, I think I am updating this property with the statement:
ScriptProperties.setProperty("SINCE_TWITTER_ID", lastID + '\n');
Is this a good way to do this? Or is there a better/more reliable way? And if so, how can I be sure it's updating the property? (I can check the log file and it seems to be doing it, so I probably just need to create a permanent text file for the logger).
Any help is greatly appreciated!!
/** A S I M P L E T W I T T E R B O T **/
/** ======================================= **/
/** Written by Amit Agarwal #labnol on 03/08/2013 **/
/** Modified by David Zemens #agnarchy on 11/21/2013 **/
/** Tutorial link: http://www.labnol.org/?p=27902 **/
/** Live demo at http://twitter.com/DearAssistant **/
/** Last updated on 09/07/2013 - Twitter API Fix **/
function start() {
Logger.log("start!" + '\n')
// REPLACE THESE DUMMY VALUES
// https://script.google.com/macros/d/18DGYaa-jbaAK9rEv0HZ2cMcWjFGgkvVcvr6TfksMNbbu2Brk3gZeZ46R/edit
var TWITTER_CONSUMER_KEY = "___REDACTED___";
var TWITTER_CONSUMER_SECRET = "___REDACTED___";
var TWITTER_HANDLE = "___REDACTED___";
var SEARCH_QUERY = "___REDACTED___" + TWITTER_HANDLE;
// Store variables
ScriptProperties.setProperty("TWITTER_CONSUMER_KEY", TWITTER_CONSUMER_KEY);
ScriptProperties.setProperty("TWITTER_CONSUMER_SECRET", TWITTER_CONSUMER_SECRET);
ScriptProperties.setProperty("TWITTER_HANDLE", TWITTER_HANDLE);
ScriptProperties.setProperty("SEARCH_QUERY", SEARCH_QUERY);
ScriptProperties.setProperty("SINCE_TWITTER_ID", "404251049889759234");
// Delete exiting triggers, if any
var triggers = ScriptApp.getScriptTriggers();
for(var i=0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
// Setup trigger to read Tweets every 2 hours
ScriptApp.newTrigger("fetchTweets")
.timeBased()
.everyMinutes(30)
//.everyHours(2)
.create();
}
function oAuth() {
//Authentication
var oauthConfig = UrlFetchApp.addOAuthService("twitter");
oauthConfig.setAccessTokenUrl("https://api.twitter.com/oauth/access_token");
oauthConfig.setRequestTokenUrl("https://api.twitter.com/oauth/request_token");
oauthConfig.setAuthorizationUrl("https://api.twitter.com/oauth/authorize");
oauthConfig.setConsumerKey(ScriptProperties.getProperty("TWITTER_CONSUMER_KEY"));
oauthConfig.setConsumerSecret(ScriptProperties.getProperty("TWITTER_CONSUMER_SECRET"));
}
function fetchTweets() {
oAuth();
// I put this line in to monitor whether the property is getting "stored" so as to avoid
// reading in duplicate tweets.
Logger.log("Getting tweets since " + ScriptProperties.getProperty("SINCE_TWITTER_ID"))
var twitter_handle = ScriptProperties.getProperty("TWITTER_HANDLE");
var search_query = ScriptProperties.getProperty("SEARCH_QUERY")
Logger.log("searching tweets to " + search_query + '\n');
// form the base URL
// restrict to a certain radius ---:
//var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&geocode=42.827934,-83.564306,75mi&include_entities=false&result_type=recent&q=";
// unrestricted radius:
var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&include_entities=false&result_type=recent&q=";
search = search + encodeString(search_query) + "&since_id=" + ScriptProperties.getProperty("SINCE_TWITTER_ID");
var options =
{
"method": "get",
"oAuthServiceName":"twitter",
"oAuthUseToken":"always"
};
try {
var result = UrlFetchApp.fetch(search, options);
var lastID = ScriptProperties.getProperty("SINCE_TWITTER_ID");
if (result.getResponseCode() === 200) {
var data = Utilities.jsonParse(result.getContentText());
if (data) {
var tweets = data.statuses;
//Logger.log(data.statuses);
for (var i=tweets.length-1; i>=0; i--) {
// Make sure this is a NEW tweet
if (tweets[i].id > ScriptProperties.getProperty("SINCE_TWITTER_ID")) {
lastID = (tweets[i].id_str);
var answer = tweets[i].text.replace(new RegExp("\#" + twitter_handle, "ig"), "").replace(twitter_handle, "");
// I find this TRY block may be necessary since a failure to send one of the tweets
// may abort the rest of the loop.
try {
Logger.log("found >> " + tweets[i].text)
Logger.log("converted >> " + answer + '\n');
sendTweet(tweets[i].user.screen_name, tweets[i].id_str, answer.substring(0,140));
// Update the script property to avoid duplicates.
ScriptProperties.setProperty("SINCE_TWITTER_ID", lastID);
Logger.log("sent to #" + tweets[i].user.screen_name + '\n');
} catch (e) {
Logger.log(e.toString() + '\n');
}
}
}
}
}
} catch (e) {
Logger.log(e.toString() + '\n');
}
Logger.log("Last used tweet.id: " + lastID + + "\n")
}
function sendTweet(user, reply_id, tweet) {
var options =
{
"method": "POST",
"oAuthServiceName":"twitter",
"oAuthUseToken":"always"
};
var status = "https://api.twitter.com/1.1/statuses/update.json";
status = status + "?status=" + encodeString("RT #" + user + " " + tweet + " - Thanks\!");
status = status + "&in_reply_to_status_id=" + reply_id;
try {
var result = UrlFetchApp.fetch(status, options);
Logger.log("JSON result = " + result.getContentText() + '\n');
}
catch (e) {
Logger.log(e.toString() + '\n');
}
}
// Thank you +Martin Hawksey - you are awesome
function encodeString (q) {
// Update: 09/06/2013
// Google Apps Script is having issues storing oAuth tokens with the Twitter API 1.1 due to some encoding issues.
// Henc this workaround to remove all the problematic characters from the status message.
var str = q.replace(/\(/g,'{').replace(/\)/g,'}').replace(/\[/g,'{').replace(/\]/g,'}').replace(/\!/g, '|').replace(/\*/g, 'x').replace(/\'/g, '');
return encodeURIComponent(str);
// var str = encodeURIComponent(q);
// str = str.replace(/!/g,'%21');
// str = str.replace(/\*/g,'%2A');
// str = str.replace(/\(/g,'%28');
// str = str.replace(/\)/g,'%29');
// str = str.replace(/'/g,'%27');
// return str;
}
When you use ScriptProperties.setProperty("KEY", "VALUE");, internally Script Properties will overwrite a duplicate key (i.e., if an old Property has the same key, your new one will replace it). So in your case, since you are using the same identifier for the key (SINCE_TWITTER_ID), it will replace any previous Script Property that is that key.
Furthermore, you can view Script Properties via File -> Project properties -> Project properties (tab). Imo Google didn't name that very well. User properties as specific to Google users. Script properties as specific to the Script Project you are working under.
Also, it probably isn't a good idea to include \n in your value when you set the property. That will lead to all sorts of bugs down the road, because you'll have to compare with something like the following:
var valToCompare = "My value\n";
instead of:
var valToCompare = "My value";
because the value in SINCE_TWITTER_ID will actually be "some value\n" after you call your fetchTweet() function.
Of course, one seems more logical I think, unless you really need the line breaks (in which case you should be using them somewhere else, for this application).
Its ok like that thou I dont know why you are adding \n at fhe end. Might confuse other code. You can see script properties in the script's file menu+ properties
Disclaimer: I fully understand the risks/downsides of using eval but this is one niche case where I couldn't find any other way.
In Google Apps Scripting, there still is no built-in capability to import a script as a library so many sheets can use the same code; but, there is a facility built-in where I can import text from a plaintext file.
Here's the eval-ing code:
var id = [The-docID-goes-here];
var code = DocsList.getFileById(id).getContentAsString();
var lib = eval(code);
Logger.log(lib.fetchDate());
Here's some example code I'm using in the external file:
{
fetchDate: function() {
var d = new Date();
var dateString = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
return dateString;
}
}
What I'm aiming for is to drop a big object literal (containing all the library code) onto a local variable so I can reference it's properties/functions like they're contained in their own namespace.
Replace var lib = eval(code); with:
var lib = eval('(' + code + ')');
When the parens are omitted, the curly braces are being interpreted as markers of a block of code. As a result, the return value of eval is the fetchData function, instead of a object containing the function.
When the function name is missing, the code inside the block is read as a labelled anonymous function statement, which is not valid.
After adding the parens, the curly braces are used as object literals (as intended), and the return value of eval is an object, with the fetchData method. Then, your code will work.
You cannot evaluate
{
fetchDate: function() {
var d = new Date();
var dateString = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
return dateString;
}
}
Because it is not a valid expression (Object literals on their own are interpreted as blocks. fetch: function () { } is not a valid expression).
Try
var myLibName = {
fetchDate: function() {
var d = new Date();
var dateString = (d.getMonth() + 1) + "/" + d.getDate() + "/" + d.getFullYear();
return dateString;
}
};
I'm trying to build a POC to migrate a heavy JSF application to a stateless ajax/restful application .
In the proccess i can't decide what is the best way of presenting the JSON data returned to the screen , i can see 2 major approaches one is to have templates and use something like prototype's toHTML() with them and the other is to build the objects in javascript and then use appendchild .
the first one is much more easy to understand for a new person who has to maintain the code as the templates are very clear and easier to maintain (allso the skills needed to change the html in templates are lower) but from what i understand the appendchild method is better in regards to browser speed .
what is the preferable way to handle this and am i missing other points of comparison between the two ?
append child is this a good compromise between the two ?
are there any other ways to do this ?
P.S : to be clear i'm talking about client side manipulations only
Setting html directly with innerHTML is the fastest way cross-browser. It has some bugs, however, that you should keep in mind (tables, forms, etc.).
var html = [];
for (...) {
html.push( PARTIAL_HTML );
}
element.innerHTML = html.join("");
UPDATE: The best way may be to test it for yourself:
function test( name, fn, n, next ) {
var n = n || 100; // default number of runs
var start, end, elapsed;
setTimeout(function() {
start = Number(new Date());
for ( ; n--; ) {
fn()
}
end = Number(new Date());
elapsed = end - start;
// LOG THE RESULT
// can be: $("#debug").html(name + ": " + elapsed + " ms");
console.log(name + ": " + elapsed + " ms"));
next && next();
}, 0);
}
test("dom", function() {
// ...
});
test("innerHTML", function() {
// ...
});