How to handle dates from database with AngularJS - javascript

I'm getting data from SQLite and assign it to my $scope.loans:
{
"id": 1,
"book_id": 2,
"patron_id": 3,
"loaned_on": "2016-12-28",
"return_by": "2017-01-25",
"returned_on": "2017-01-17",
}
{
"id": 2,
"book_id": 3,
"patron_id": 4,
"loaned_on": "2016-12-28",
"return_by": "2017-01-25",
"returned_on": null,
}
In my view I'm trying to bind the data into inputs tags so the user can edit and submit the changes (I'm using pug template engine)
table
thead
tr
th Loaned On
th Return By
th Returned On
tbody
tr(ng-repeat='loan in loans')
td(type='date' ng-model='loan.loaned_on' value='{{loan.loaned_on}}')
td(type='date' ng-model='loan.loaned_on' value='{{loan.return_by}}')
td(type='date' ng-model='loan.loaned_on' value='{{loan.returned_on}}')
Now I'm getting an AngularJS error says that the ngModel is not a date object.
I understand that I should use the date constructor to convert the date string to a date object, But my question is what is the best way to do such a thing?
solution
I thought that maybe I'll find a better way, but I didn't.
So I loop through the loans array and assign a date object for each date string.

You can just do this, which is simple and inline.
td(type='date' ng-model='new Date(loan.loaned_on)' value='{{loan.loaned_on}}')

You gave the input type as date but you assigning date string to the model which the model is not expecting. Instead, convert the value to a date object and assign it as shown below :
tbody
tr(ng-repeat='loan in loans')
td(type='date' ng-model='loan.loaned_on' value='{{new Date(loan.loaned_on)}}')

Creating a date
You can simply use the default constructor of Date, if your date is in this ISO 8601 form.
new Date("2017-01-25")
If you want to do some calculations with the date or if you want to pass the date in another format, I would recommend using moment.js:
var myDate = moment("2017-01-25", "YYYY-MM-DD");
var newDate = myDate.add(7, "days");
Passing it to the scope
The simplest way to pass the Date to the HTML is to pass it in a scope function. In this way, you can simply call it in the HTML.
Controller (with Date Constructor):
$scope.createDate = function(dateString) {
return new Date(dateString);
};
Controller (with moment.js):
$scope.createDate = function(dateString) {
return moment("2017-01-25", "YYYY-MM-DD");
};
HTML (pug template):
td(type='date' ng-model='createDate(loan.loaned_on)' value='{{loan.loaned_on}}')

Try this :
var myApp = angular.module('myApp',[]);
myApp.controller('MyCtrl',function ($scope) {
$scope.loans = [{
"id": 1,
"book_id": 2,
"patron_id": 3,
"loaned_on": "2016-12-28",
"return_by": "2017-01-25",
"returned_on": "2017-01-17"
},
{
"id": 2,
"book_id": 3,
"patron_id": 4,
"loaned_on": "2016-12-28",
"return_by": "2017-01-25",
"returned_on": null
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MyCtrl">
<table>
<thead>
<tr>
<th>Loaned On</th>
<th>Return By</th>
<th>Returned On</th>
</tr>
</thead>
<tbody>
<tr ng-repeat='loan in loans'>
<td><input type='date' ng-model='loan.loaned_on' value='{{loan.loaned_on}}'/></td>
<td><input type='date' ng-model='loan.return_by' value='{{loan.return_by}}'/></td>
<td><input type='date' ng-model='loan.returned_on' value='{{loan.returned_on}}'/></td>
</tr>
</tbody>
</table>
</div>

Related

How can I split the following string into an array that I can use to populate a html table

I have a string that looks like:
var str = '{ "abc": {
"decline_reason": "Business rule switched off"
},
"def": {
"decline_reason": "No response by interface"
},
"ghi": {
"decline_reason": "Requested incorrect size" }';
I would like to split that string into an array that I can use to populate a table on a webpage. I intend to use the initial reference ('abc'), with the reason ('Business rule switched off') on row 1, initial reference ('def'), with the reason ('No response by interface') on row 2, etc...
I have tried regex to break it down, and I've managed to find one that removes quotes, but not to break the string down.
I intend to populate the table with code like:
<table id="declinesTable">
<tr>
<th onclick="sortTable(0)">Reference Code</th>
<th>Decline Reason</th>
</tr>
<tr id="lender1">
<td id="lender1"><script>document.getElementById("lender1").innerHTML = declines[0];</script>
</td>
<td id="declineReason1"><script>document.getElementById("declineReason1").innerHTML = declines[2];</script>
</td>
</tr>
</table>
skipping out the value "decline_reason" from the table.
Any suggestions?
Couple of things - your string is missing a final }. Not sure where you're getting the string from, but it's in JSON format, so use JSON.parse to get it into an object, then iterate over the object to do something with each individual nested object. I would strongly recommend using a library like jQuery to help you append it to the table. You can google and very quickly find out how to add jQuery to your project. See below.
function stringParse(str) {
const json = JSON.parse(str);
const html = Object.entries(json).reduce((h, [k, v]) =>
h += `<tr><td>${k}</td><td>${v.decline_reason}</td></tr>`
, "");
$('#declinesTable').append(html);
}
const str = '{ "abc": {"decline_reason": "Business rule switched off"},"def": {"decline_reason": "No response by interface"},"ghi": {"decline_reason": "Requested incorrect size"}}'
stringParse(str);
<table id="declinesTable">
<tr>
<th>Reference Code</th>
<th>Decline Reason</th>
</tr>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

Filter ng-repeat based on search input with transformation filter

I have an array of records and I'm repeating on a HTML table with filters in the header. It turns out that some values are transformed by filters thus making the ng-repeat filter to fail.
<table class="table">
<thead>
<tr>
<td><input ng-model="search.time" type="text" class="form-control" /></td>
</tr>
</thead>
<tbody>
<tr ng-repeat="record in records | filter: search">
<td>{{record.time | timeFormatter}}</td>
</tr>
</tbody>
</table>
As you guys can see, the value in the table column is being transformed by the timeFormatter filter. So, instead of "0800", it shows "08:00 AM" for the record time. When the user types "08" it works, but if they type "08:", or "AM" it doesn't work anymore.
Can you guys help me to make the filter work with the values as they are displayed in the table column (i.e with formatting)?
Thanks in advance.
You're probably going to need to play with a directive. Try this:
angular.module("app").directive("myDirective", function(){
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModelController) {
ngModelController.$parsers.push(function(data) {
//convert data from view format to model format
return input.substring(0, 2) + ':' + input.substring(2)
});
ngModelController.$formatters.push(function(data) {
//convert data from model format to view format
return input.substring(0, 2) + ':' + input.substring(2)
});
}
};
});
Here's a Plunker
This is just guess work, but your ng-model is storing changes on search.time, but your filter is 'filtering' search. Try using search.time in filter.

How to convert a C# list of DateTimes to local JS date?

I've looped through a list of C# DateTime's on a view using Razor syntax and bound to a table on the view.
What I need to do before the values are bound to the table is convert to the browser's local time. The UpdatedTime passed in is in UTC timezone from the server.
So I need to somehow convert each UpdatedTime property in the Models.Escalation list to local before it is bound to the table.
I did try calling #item.UpdatedTime.ToLocalTime() but this converts to the server side local time which is UTC. Not the browser local time.
Question:
How can you convert a C# list of DateTime's to local on client side JS?
I do know how to convert a single DateTime value to local using a moment. But not sure how I can apply that to the complete Models.Escalation list:
var updatedTimeISO = moment.utc('#Model.UpdatedTime').toISOString();
var updatedTimeLocal = moment(updatedTimeISO);
#Model.UpdatedTime = updatedTimeLocal ;
The table loop that binds the C# DateTime's to a table in the Razor view:
<tbody>
#foreach (Models.Escalation item in Model)
{
<tr>
<td data-order="#item.UnixTimeStamp" class="td-limit">#item.UpdatedTime.ToString("f")</td>
<td class="td-limit">#item.EventName</td>
</tr>
}
</tbody>
You should be able to accomplish this on the server side if you know what timezone you want to convert too.
DateTime timeUtc = DateTime.UtcNow;
try
{
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);
Console.WriteLine("The date and time are {0} {1}.",
cstTime,
cstZone.IsDaylightSavingTime(cstTime) ?
cstZone.DaylightName : cstZone.StandardName);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The registry does not define the Central Standard Time zone.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the Central Standard Time zone has been corrupted.");
}
If you don't know the browsers timezone you could use the following jQuery and moment JS code:
$(".td-limit").each(function () {
var updatedTimeISO = moment.utc($(this).data('order')).toISOString();
var updatedTimeLocal = moment(updatedTimeISO);
$(this).text(updatedTimeLocal);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.14.1/moment.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<td data-order="2013-05-02T21:01:26.0828604Z" class="td-limit"></td>
<td data-order="2013-04-02T21:01:26.0828604Z" class="td-limit"></td>
<td data-order="2013-03-02T21:01:26.0828604Z" class="td-limit"></td>
<td data-order="2013-02-02T21:01:26.0828604Z" class="td-limit"></td>
<td data-order="2013-01-02T21:01:26.0828604Z" class="td-limit"></td>
</tr>
</tbody>
</table>

Sorting with StupidTable JS and Date Columns

I am using StupidTable JS to sort my table columns. Works great. However most of my columns sort with
data-sort='string'
and now I have a DATE column I need to sort. Not sure how to do this. The info is fed from a database so I believe I need some function. The format each date is in is
dd-Mon-yyyy ex: 12-MAY-2015 or 25-JUL-2014
??
If you have control over the rendering of the HTML, you can sort on the timestamp while still displaying the pretty date.
https://github.com/joequery/Stupid-Table-Plugin#data-with-multiple-representationspredefined-order
Otherwise you'll need to create a custom sort function.
<table>
<thead>
<tr>
<th data-sort="string">Name</th>
<th data-sort="int">Birthday</th>
</tr>
</thead>
<tbody>
<tr>
<td>Joe McCullough</td>
<td data-sort-value="672537600">April 25, 1991</td>
</tr>
<tr>
<td>Clint Dempsey</td>
<td data-sort-value="416016000">March 9, 1983</td>
</tr>
...
You could sort by int after converting the date string to an time-stamp (integer).
From the Stupid-table docs:
The plugin internally recognizes "int", "string", "string-ins" (case-insensitive) and "float", so simple data tables will take very little effort on your part.
var dateString = '12-MAY-2015'
var date = new Date(dateString).getTime()
// 1431385200000
Then use data-sort='int' on your th elements.
Or you could use a custom function:
These data types will be sufficient for many simple tables. However, if you need different data types for sorting, you can easily create your own!
<th data-sort="date">Date</th>
var table = $("table").stupidtable({
"date": function(a,b){
// Get these into date objects for comparison.
aDate = new Date(a);
bDate = new Date(b);
return aDate - bDate;
}
});
Note: above code not tested
I had the same problem, and fixed it as follows:
<td><span style="display:none;">20150512</span>12-MAY-2015</td>
Like this in PHP:
<? php
$ date = date_create (YOUR_DATABASE_FIELD);
echo '<td><span style="display: none;">'. date_format ($ date, "Ymd"). '</span>'. date_format ($ date, "d-M-Y"). '</td>';
I hope this help you!

How can I retrieve ko.computed running sum of foreach when iterating through observablearray in knockout.js

I have a list of ProjectPeriod items as an observableArray in a knockout viewModel which includes the number of months in each period. I want to display the end date for each row in the foreach. Currently I am using a ko.computed value in the viewModel, but I'm not able to loop over each item up to the item being displayed. How can I loop over and sum the values being displayed only up to the current item in the foreach?
Currently I have the following HTML:
<table>
<tr class="tableHeader">
<th>Period</th>
<th>Number of Months</th>
<th>End of Period</th>
</tr>
<tbody data-bind="foreach: ProjectPeriod">
<tr>
<td><input data-bind="value: ProjectYearText" /></td>
<td><input data-bind="value: PeriodMonths" /></td>
<td data-bind="text: endDate"></td>
</tr>
</tbody>
</table>
and the following viewModel:
function ProjectPeriod(projectYearId, projectYearText, periodMonths, viewModel) {
var self = this;
self.ProjectYearId = projectYearId;
self.ProjectYearText = ko.observable(projectYearText);
self.PeriodMonths = ko.observable(periodMonths);
self.viewModel = viewModel;
self.endDate = ko.computed(function () {
var startDate = hfProjectDates.Get("ProjectStartDate");
// Calculate the number of months from the beginning to the current period.
var monthCount = 0;
ko.utils.arrayForEach(self.viewModel.ProjectPeriods(), function (projectPeriod) {
if (projectPeriod.ProjectYearId < self.ProjectYearId)
monthCount += projectPeriod.PeriodMonths;
});
var endDate = moment(startDate).add('M', monthCount);
return endDate ? endDate.format("M/DD/YYYY") : "None";
});
}
function ProjectPeriodViewModel() {
// Data
var self = this;
self.ProjectPeriods = ko.observableArray([
new ProjectPeriod(1, "1st Year", 12, ProjectPeriodViewModel),
new ProjectPeriod(2, "2nd Year", 12, ProjectPeriodViewModel),
new ProjectPeriod(3, "3rd Year", 12, ProjectPeriodViewModel)
]);
}
I am really just getting started with Knockout, so I expect there are more than a few issues with the way I'm approaching this. But specifically I need to get the running end date to display.
Update: based on Matt;s feedback I've updated to include the observable in ProjectPeriod, but I'm running into issues getting the reference from the viewModel and iterating over the array.
There are a couple of ways to approach this, what I would probably do is move the computed property to the ProjectPeriod object itself and also give the ProjectPeriod a reference to the parent ProjectPeriodViewModel, that way, the ProjectPeriod will be able to access the ProjectPeriods and count backwards adding up all the periods that came before it (it might be an idea to give each period an index property to make this simpler).

Categories

Resources