I'm using moment.js and jquery datatables. Specifically, I have a a list of cells that all contain a Unix Timestamp.
What I'd like to do is convert this timestamp to the user's localized time (based on his/her timezone).
I am able to get the timezone to localize, but it only works for the first group of paginated results in my table...if I navigate to another page, the timestamp still shows up as the raw unix value.
I've made a JS fiddle to illustrate.
Could someone kindly let me know 1) if there's a better way to do what I'm doing 2) how I can localize my times even after actions like a) searching the table 2) sorting the table 3) paginating the table?
Huge thanks in advance!
My JS:
// Do Datatables
$('.my-datatable').DataTable({
"order": [[ 1, 'desc' ],],
"aoColumnDefs": [
{ "bSortable": false, "aTargets": [ 0 ] }
]
});
// Loop through class to localize unix time based on user's time zone
function localizeTime(){
$( ".localize_time" ).each(function() {
if (typeof moment !== 'undefined' && $.isFunction(moment)) {
var userMomentTz = moment().format("Z");
var userTimeZone = userMomentTz.replace(":", "");
var elementSiteUnixTimeText = $(this).find('.localize_time_unix').text();
var elementSiteUnixTimeVal = parseInt(elementSiteUnixTimeText.trim());
if (userTimeZone.substring(0, 1) == "-") {
var userTimeZoneHr = parseInt(userTimeZone.substring(1,3));
var userTimeZoneMin = parseInt(userTimeZone.slice(-2));
var userTimeOffset = (userTimeZoneHr + '.' + (userTimeZoneMin/60))*(-1);
} else {
var userTimeZoneHr = parseInt(userTimeZone.substring(0,2));
var userTimeZoneMin = parseInt(userTimeZone.slice(-2));
var userTimeOffset = userTimeZoneHr + '.' + (userTimeZoneMin/60);
}
var momentDateUserOffset = moment.unix(elementSiteUnixTimeVal).utcOffset(userTimeOffset);
var momentDateFormattedOffset = moment(momentDateUserOffset).format('ddd, D MMM YYYY, h:mm A');
$(this).find('.localize_time_display').text(momentDateFormattedOffset);
};
});
};
// Run time localization function
if ( $( ".localize_time" ).length ) {
localizeTime()
};
My HTML
<table class="my-datatable">
<thead>
<tr>
<th>Time</th>
<th>Stuff</th>
</tr>
</thead>
<tbody>
<tr>
<td>Stuff</td>
<td>
<span class="localize_time">
<span class="localize_time_unix">UNIX Time n++</span>
<span class="localize_time_display"></span>
</span>
</td>
</tr>
</tbody>
</table>
Ok, well fortunately this was easier than I thought using 'data rendering'
Working JS Fiddle
Hope this helps someone!
My updated JS
// Do Datatables
$('.my-datatable').DataTable( {
"order": [[ 1, 'desc' ],],
"columnDefs": [{
"targets": 1,
"render": function (data, type, full, meta) {
if (typeof moment !== 'undefined' && $.isFunction(moment)) {
var userMomentTz = moment().format("Z");
var userTimeZone = userMomentTz.replace(":", "");
var elementSiteUnixTimeText = data;
var elementSiteUnixTimeVal = parseInt(elementSiteUnixTimeText.trim());
if (userTimeZone.substring(0, 1) == "-") {
var userTimeZoneHr = parseInt(userTimeZone.substring(1,3));
var userTimeZoneMin = parseInt(userTimeZone.slice(-2));
var userTimeOffset = (userTimeZoneHr + '.' + (userTimeZoneMin/60))*(-1);
} else {
var userTimeZoneHr = parseInt(userTimeZone.substring(0,2));
var userTimeZoneMin = parseInt(userTimeZone.slice(-2));
var userTimeOffset = userTimeZoneHr + '.' + (userTimeZoneMin/60);
}
var momentDateUserOffset = moment.unix(elementSiteUnixTimeVal).utcOffset(userTimeOffset);
var momentDateFormattedOffset = moment(momentDateUserOffset).format('ddd, D MMM YYYY, h:mm A');
$(this).find('.localize_time_display').text(momentDateFormattedOffset);
return momentDateFormattedOffset;
};
}
}]
} );
Related
I am able to read Excel file I need to read xls file col-wise, read data in every column and convert it to JSON.
How to read xls file col by col?
Getting trouble while fetching just first column data from xlsx file with vuejs
How to get particular column data from xlsx file with Vue js, anyone/?
i'm new to Vue js.
here, is the code that i used.
<template>
<div>
<p v-if="err!==''">{{err}}</p> <!-- Used to display errors -->
<table v-if="content!==''"> <!-- Set center,Do not display if no content is obtained -->
<!-- <tr>
<th v-for="h in content[0]" :key="h.id">{{h}}</th>
</tr> Cycle read data and display -->
<tr v-for="row in content.slice(0,)" :key=row.id>
<td v-for="item in row" :key=item.id>{{item}}</td>
</tr>
</table>
</div>
</template>
<script>
import axios from 'axios'
import XLSX from 'xlsx'
export default {
name: "App",
data(){
return {
content: '', //Initialization data
err: ''
}
},
created() {
var url = "/filw.csv" //Files placed in the public directory can be accessed directly
axios.get(url, {responseType:'arraybuffer'})
.then((res) => {
var data = new Uint8Array(res.data)
var wb = XLSX.read(data, {type:"array"})
const firstSheetName = wb.SheetNames[0]
const sheets = wb.Sheets[firstSheetName]
// const results = XLSX.utils.sheet_to_json(sheets)
// this.content = results
this.content = this.getColumnData(sheets)
}).catch( err =>{
this.err = err
})
},
methods: {
getColumnData(sheet) {
const ColData = []
const range = XLSX.utils.decode_range(sheet['!refs'])
let C
const R = range.s.r
/* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
const cell = sheet[XLSX.utils.encode_col({ c: C, r: R })]
ColData.push(cell)
}
return ColData
}
}
}
</script>
<style>
</style>
ANyone who know, help would be much appreciated ;-)
You can convert the data to json and then read the result, like this:
created() {
var url = "/test.xlsx"
axios.get(url, {responseType:'arraybuffer'})
.then((res) => {
var data = new Uint8Array(res.data)
var wb = XLSX.read(data, {type:"array"})
const firstSheetName = wb.SheetNames[0]
const first_worksheet = wb.Sheets[firstSheetName]
// convert to json
const file_data = XLSX.utils.sheet_to_json(first_worksheet, { header: 1 });
// first column first row value
console.log(file_data[0][0])
})
.catch( err =>{this.err = err})
},
I have implemented bootstrap-typeahead and when doing a search of, for example, the word "vision" (without accent), I want typeahead to find the coincidences that there is both "visión" (with accent) and "vision".
I have seen several examples to do this, like: accent insensitive regex but I do not understand the form to implement it in typeahaead. And I saw this too: Typeahead insensitive accent and I have created a new file bootstrap3-typeahead-ci.min.js as in this answer is shown, but this not working. Some help? Thanks.
EDITED
To complement the question
this is my typeahead.js (reduced)
$(document).ready(function(){
function buscar(texto){
$('#texto').val(texto);
$('#buscar').submit();
}
if ($('.typetitulo').length) {
var lang = $("#lang_js").data('value');
var json_location = 'storage/json/';
var noticia_location = 'actualidad/';
var noticias = new Bloodhound({
prefetch: {
url: json_location + lang + '/' + 'noticia.json',
cache: false
},
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('title', 'lead'),
queryTokenizer: Bloodhound.tokenizers.whitespace
});
var documentos = new Bloodhound({
prefetch: {
url: json_location + '/' + 'documento.json',
cache: false
},
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name', 'description'),
queryTokenizer: Bloodhound.tokenizers.whitespace
});
$('.typetitulo').typeahead(
{
name: 'noticias',
display: 'title',
source: noticias,
templates: {
header: "<h3>"+ tit_actualidad +"</h3>",
suggestion: function (item) {
var enlace = noticia_location + item.id + '/' + item.slug;
return "<div><a href='"+enlace+"'>" + item.title + "</a></div>";
}
}
},
{
name: 'documentos',
display: 'name',
source: documentos,
templates: {
header: "<h3>"+ tit_documentos +"</h3>",
suggestion: function (item) {
var enlace = item.path;
return "<div><a href='"+enlace+"'>" + item.name + "</a></div>";
}
}
}).on('typeahead:selected', function(e){
e.target.form.submit();
});
}
});
In the view:
{!! Form::open([
'route' => 'buscar',
'id' => 'buscar',
'name' => 'buscar',
'class' => 'buscador col-xs-12',
'method' => 'POST',
'accept-charset' => 'utf-8'
]) !!}
<input name="texto" class="input_buscador typetitulo" autocomplete="off" type="text"/>
<input name="lang" type="hidden" value="{{$lang}}"/>
{!! HTML::image('images/web/icons/lupa.svg', '', array('height' => '30', 'class' => 'boton_buscador', 'onclick' => 'document.buscar.submit()') ) !!}
{!! Form::close() !!}
// .... //
#if(isset($data['noticias']) && $data['noticias']->count() !== 0)
<div class="col-xs-12 pad_inf_2">
<h3>#lang('header.actualidad')</h3>
#foreach($data['noticias'] as $value)
<span class="item">
{{$value['title']}}
</span>
#endforeach
</div>
#endif
#if(isset($data['docs']) && $data['docs']->count() !== 0)
<div class="col-xs-12 pad_inf_2">
<h3>#lang('header.biblioteca')</h3>
#foreach($data['docs'] as $value)
<span class="item">
{{$value['name']}}
</span>
#endforeach
</div>
#endif
This is the typeahead-insensitive.js as in this answer is shown: Typeahead insensitive accent
// function for making a string accent insensitive
$.fn.typeahead.Constructor.prototype.normalize = function (str) {
// escape chars
var normalized = str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
// map equivalent chars
normalized = normalized.replace(/[aãáàâ]/gi, '[aãáàâ]');
normalized = normalized.replace(/[eẽéèê]/gi, '[eẽéèê]');
normalized = normalized.replace(/[iĩíìî]/gi, '[iĩíìî]');
normalized = normalized.replace(/[oõóòô]/gi, '[oõóòô]');
normalized = normalized.replace(/[uũúùû]/gi, '[uũúùû]');
normalized = normalized.replace(/[cç]/gi, '[cç]');
// convert string to a regular expression
// with case insensitive mode
normalized = new RegExp(normalized, 'gi');
// return regular expresion
return normalized;
}
// change 'matcher' method so it became accent insensitive
$.fn.typeahead.Constructor.prototype.matcher = function (item) {
// get item to be evaluated
var source = this.displayText(item);
// make search value case insensitive
var normalized = this.normalize(this.query);
// search for normalized value
return source.match(normalized);
}
// change 'highlighter' method so it became accent insensitive
$.fn.typeahead.Constructor.prototype.highlighter = function (item) {
// get html output
var source = this.displayText(item);
// make search value case insensitive
var normalized = this.normalize(this.query);
// highlight normalized value in bold
return source.replace(normalized, '<strong>$&</strong>');
}
And in the layout I added:
{{-- Typeahead --}}
{!! HTML::script('https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js') !!}
{!! HTML::script('js/web/typeahead-insensitive.js') !!}
{!! HTML::script('js/web/typeahead.js') !!}
I am using Node.js to send a confirmation email when a user submits an order. I would like the email to include include all items that the user submits. The number of items will vary with each submission. I'd like the email body to include a table. Can a lodash template be used for this? Or should this be handled differently?
When I used the following code, the resulting email includes what I assume to be uncompiled code.
var tpl = _.template('<% _.forEach(items, function(item) { %><td><%- item %></td><% }); %>');
tpl({ 'items': ['Guitar', 'Harmonica'] });
var data = {
from: 'support#example.com',
to: email,
subject: 'Your Order Confirmation',
html: '<p>Thank you for submitting your order.</p>
<table>
<tr>
<thead>
<tr>
<th><strong>Items</strong></th>'
+ tpl +
// The template should insert each item here
// <td>Guitar</td>
// <td>Harmonica</td>
'</tr>
</thead>
</tr>
</table>'
};
Output in actual email sent:
function (obj) { obj || (obj = {}); var __t, __p = '', __e = _.escape,
__j = Array.prototype.join; function print() { __p +=
__j.call(arguments, '') } with (obj) { _.forEach(items,
function(item) { ; __p += ' ' + __e( item ) + ' '; }); ; } return
__p }
change:
tpl({ 'items': ['Guitar', 'Harmonica'] });
to
var html = tpl({ 'items': ['Guitar', 'Harmonica'] });
and
ng>Items</strong></th>'
+ tpl +
to
ng>Items</strong></th>'
+ html +
if you look at the docs at: https://lodash.com/docs#template
you will see the compiled function returns an output which you need to use.
You instead of using the output, you used the actual function itself.
So the way I am building pagination in Reactjs is a bit odd, but it works for me, I, How ever would like to say show me the first 5 (1-5) on the 5th page show me 5-max. But I am unclear on how to do that.
this is what I currently have:
render: function() {
// Do we have more then one page?
if (this.props.maxPages > 0){
// We have to add one to the max pages that come back.
var pageLink = this.props.maxPages + 1;
var currentPage = this.props.currentPage;
var liElements = []
// Build [<<][<] for the user.
if (pageLink > 1) {
liElements.push(<li><<</li>);
liElements.push(<li><a href={this.pageSubtraction(currentPage, pageLink)}><</a></li>);
}
// Build the individual [x][y][z] links.
for (var i = 1; i <= pageLink; i++) {
liElements.push(<li key={i} id={i}><a href={"#posts?page="+i}>{i}</a></li>);
}
// Build the [>][>>] for the user.
if (pageLink > 1) {
liElements.push(<li><a href={this.pageAddition(currentPage, pageLink)}>></a></li>);
liElements.push(<li><a href={"#posts?page="+pageLink}>>></a></li>);
}
return (<ul className="pagination">{liElements}</ul>);
}else{
// Return nothing.
return ( <div></div> );
}
}
This will build me [<<][<][1][2][3] ... [>][>>] which is great but their is no limit on it.
At this time:
pageLink = 6 (the max number of pages - I know horrible variable name)
currentPage = 1 (the current page you are on)
So what I need is:
[<<][<][1][2][3][4][5][>][>>] Select Page 5 [<<][<][5][6][>][>>] But I am not sure if my current set up will allow me to do that.
This is a somewhat complicated algorithm (and not all of the details are provided). Rather than worrying about markup here, it might be simpler to start with a pure data structure representing what should be drawn.
Pagination = function(props){
var pages = props.maxPages + 1;
var current = props.currentPage;
var links = [];
// leading arrows
if (current > 0) {
links.push([0, "<<"]);
links.push([current - 1, "<"]);
}
for (var i=current-3; i<current+4; i++) {
if (i > 0 && i < pages) {
links.push([i, i]);
}
}
// tailing arrows
if (current < pages) {
links.push([current + 1, ">"]);
links.push([pages - 1, ">>"]);
}
return JSON.stringify(links, null, 4);
};
Now we get something like this (jsbin). You could also easily write unit tests to ensure this gives the correct results.
[
[
0,
"<<"
],
[
1,
"<"
],
[
1,
1
],
[
2,
2
],
[
3,
3
],
[
4,
4
],
[
5,
5
],
[
3,
">"
],
[
7,
">>"
]
]
Once you're getting the right data here, you can map that data through a presentation function.
function PageLink(i, char){
character = character || String(i);
return (
<li key={char}>
<a href={"#posts?page="+i}>{char}</a>
</li>
);
}
Pagination = function(props){
/* same code as before */
return links.map(function(x){
return PageLink(x[0], x[1]);
});;
};
P.s. when you do get it to match your requirements, please post an answer here so others can use it as a base for their pagination.
Below is the complete code for creating a paging option.Full post is available here.
var pager = React.createClass({
render : function(){
var li = [];
var pageCount = props.Size;
for(var i = 1; i <=pageCount; i++){
if(props.currentPage == i){
li.push(<li key={i} className="active">{i}</li>);
}
else{
li.push(<li key={i} ><a href="#" onClick={props.onPageChanged.bind(null,i)}>{i}</a></li>);
}
}
return (<ul className="pagination">{li}</ul>);
}
});
var dataGrid = React.createClass({
render : function(){
return (
<tr>
<td>{props.item.Name}</td>
<td>{props.item.Address}</td>
<td>...</td>
.....
</tr>
);
}
});
var EmployeeGridTable = React.createClass({
getInitialState : function(){
return {
Data : {
List : [],
totalPage : 0,
sortColumnName : null,
sortOrder : null,
currentPage : 1,
pageSize : 3
}
}
},
componentDidMount : function(){
this.populateData();
},
populateData: function(){
var params = {
pageSize : this.state.Data.pageSize,
currentPage : this.state.Data.currentPage
}
if(this.state.Data.sortColumnName){
params.sortColumnName = this.state.Data.sortColumnName;
}
if(this.state.Data.sortOrder){
params.sortOrder = this.state.Data.sortOrder;
}
$.ajax({
url : this.props.dataUrl,
type : 'GET',
data : params,
success : function(data){
if(this.isMounted()){
this.setState({
Data : data
});
}
}.bind(this),
error: function(err){
alert('Error');
}.bind(this)
});
},
pageChanged:function(pageNumber,e){
e.preventDefault();
this.state.Data.currentPage = pageNumber;
this.populateData();
},
sortChanged : function(sortColumnName, order , e){
e.preventDefault();
this.state.Data.sortColumnName = sortColumnName;
this.state.Data.currentPage = 1;
this.state.Data.sortOrder = order.toString().toLowerCase() == 'asc' ? 'desc':'asc';
this.populateData();
},
_sortClass : function(filterName){
return "fa fa-fw " + ((filterName == this.state.Data.sortColumnName) ? ("fa-sort-" + this.state.Data.sortOrder) : "fa-sort");
},
render : function(){
var rows = [];
this.state.Data.List.forEach(function(item){
rows.push(<dataGrid key={item.EmployeeID} item={item}/>);
});
return (
<div>
<table className="table table-responsive table-bordered">
<thead>
<tr>
<th onClick={this.sortChanged.bind(this,'FirstName',this.state.Data.sortOrder)}>First Name
<i className={this._sortClass('FirstName')}></i></th>
<th onClick={this.sortChanged.bind(this,'LastName',this.state.Data.sortOrder)}>
Last Name
<i className={this._sortClass('LastName')}></i></th>
<th onClick={this.sortChanged.bind(this,'EmailID',this.state.Data.sortOrder)}>
Email
<i className={this._sortClass('EmailID')}></i>
</th>
<th onClick={this.sortChanged.bind(this,'Country',this.state.Data.sortOrder)}>
Country
<i className={this._sortClass('Country')}></i>
</th>
<th onClick={this.sortChanged.bind(this,'City',this.state.Data.sortOrder)}>
City
<i className={this._sortClass('City')}></i>
</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
<pager Size={this.state.Data.totalPage} onPageChanged={this.pageChanged} currentPage={this.state.Data.currentPage}/>
</div>
);
}
});
ReactDOM.render(<EmployeeGridTable dataUrl="/home/getEmployeeList"/>, document.getElementById('griddata'));
Hi this is an example of the code i want to run:
$('#search1').submit(function(){
var date = $('#date').val();
var location = $('#location').val();
var datastring = 'date=' + date + '&location=' + location;
$.ajax({
type: "POST",
cache: "true",
url: "search.php",
dataType:"json",
data: datastring,
success: function(data){
$('#main').html('')
for ($i = 0, $j = data.bus.length; $i < $j; $i++) {
//Create an object for each successful query result that holds information such as departure time, location, seats open...
$('#main').append(html);
}
How would I go about coding the success function? I want the object to store each bus' information so that the info can be displayed in the search result as well as being able to be referenced when the user confirms his RSVP later on. Thanks ahead of time
You can declare an object to use as a map in the containing scope:
var busInfo = {};
...and then if the bus entries have some form of unique identifier, you can record them like this:
success: function(data){
var $i, $j, bus;
$('#main').html('')
for ($i = 0, $j = data.bus.length; $i < $j; $i++) {
// Remember this bus by ID
bus = data.bus[$i];
busInfo[bus.id] = bus;
$('#main').append(html);
}
}
And then later, when the user chooses a bus, use the chosen ID to get the full bus information:
var bus = busInfo[theChosenId];
This works because all JavaScript objects are key/value maps. Keys are always strings, but the interpreter will happily make strings out of what you give it (e.g., busInfo[42] = ... will work, 42 will become "42" implicitly).
If you just want an array, your data.bus already is one, right?
var busInfo = [];
// ....
success: function(data){
var $i, $j;
// Remember it
busInfo = data.bus;
$('#main').html('')
for ($i = 0, $j = data.bus.length; $i < $j; $i++) {
$('#main').append(html);
}
}
(Note that JavaScript arrays aren't really arrays, they too are name/value maps.)
Update: I dashed off a quick example of the keyed object (live copy):
HTML:
<input type='button' id='btnLoad' value='Load Buses'>
<br>...and then click a bus below:
<ul id="busList"></ul>
...to see details here:
<table style="border: 1px solid #aaa;">
<tbody>
<tr>
<th>ID:</th>
<td id="busId">--</td>
</tr>
<tr>
<th>Name:</th>
<td id="busName">--</td>
</tr>
<tr>
<th>Route:</th>
<td id="busRoute">--</td>
</tr>
</tbody>
</table>
JavaScript with jQuery:
jQuery(function($) {
// Our bus information -- note that it's within a function,
// not at global scope. Global scope is *way* too crowded.
var busInfo = {};
// Load the buses on click
$("#btnLoad").click(function() {
$.ajax({
url: "http://jsbin.com/ulawem",
dataType: "json",
success: function(data) {
var busList = $("#busList");
// Clear old bus info
busInfo = {};
// Show and remember the buses
if (!data.buses) {
display("Invalid bus information received");
}
else {
$.each(data.buses, function(index, bus) {
// Remember this bus
busInfo[bus.id] = bus;
// Show it
$("<li class='businfo'>")
.text(bus.name)
.attr("data-id", bus.id)
.appendTo(busList);
});
}
},
error: function() {
display("Error loading bus information");
}
});
});
// When the user clicks a bus in the list, show its deatils
$("#busList").delegate(".businfo", "click", function() {
var id = $(this).attr("data-id"),
bus = id ? busInfo[id] : null;
if (id) {
if (bus) {
$("#busId").text(bus.id);
$("#busName").text(bus.name);
$("#busRoute").text(bus.route);
}
else {
$("#busId, #busName, #busRoute").text("--");
}
}
});
});
Data:
{"buses": [
{"id": 42, "name": "Number 42", "route": "Highgate to Wycombe"},
{"id": 67, "name": "Old Coach Express", "route": "There and Back"}
]}
Off-topic: Note that I've added var $i, $j; to your success function. Without it, you're falling prey to The Horror of Implicit Globals, which you can tell from the name is a Bad Thing(tm).