I've been trying to get some custom client site date validation working and so far I cannot seem to get it to work properly.
I have a custom date editor defined like this:
#model DateTime?
#{
if (Model.HasValue)
{
int day = Model.Value.Day;
int month = Model.Value.Month;
int year = Model.Value.Year;
}
List<SelectListItem> days = new List<SelectListItem>();
for (int i = 1; i <= 31; i++)
{
days.Add(new SelectListItem { Text = i.ToString(), Value = i.ToString(), Selected = Model.HasValue && Model.Value.Day == i});
}
List<SelectListItem> months = new List<SelectListItem>();
for (int i = 1; i <= 12; i++)
{
months.Add(new SelectListItem { Text = i.ToString(), Value = i.ToString(), Selected = Model.HasValue && Model.Value.Month == i});
}
List<SelectListItem> years = new List<SelectListItem>();
var minYear = DateTime.Now.Year - 100;
var maxYear = DateTime.Now.Year - 18;
for (int i = maxYear; i >= minYear; i--)
{
years.Add(new SelectListItem { Text = i.ToString(), Value = i.ToString(), Selected = Model.HasValue && Model.Value.Year == i });
}
}
#Html.DropDownList("days", days, "Day", new { #class="form__select" } )
#Html.DropDownList("months", months, "Month", new { #class="form__select" } )
#Html.DropDownList("years", years, "Year", new { #class="form__select" } )
And I have a custom validation attribute defined like this:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class DoBValidatorAttribute : ValidationAttribute, IClientValidatable
{
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
List<ModelClientValidationRule> clientRules = new List<ModelClientValidationRule>();
ModelClientValidationRule validDateRule = new ModelClientValidationRule
{
ErrorMessage = "Please enter a valid date.",
ValidationType = "validdate"
};
validDateRule.ValidationParameters.Add("dayelement", metadata.PropertyName + ".days");
validDateRule.ValidationParameters.Add("monthelement", metadata.PropertyName + ".months");
validDateRule.ValidationParameters.Add("yearelement", metadata.PropertyName + ".years");
clientRules.Add(validDateRule);
return clientRules;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
DateTime dateResult;
int day = Convert.ToInt32(validationContext.Items["days"]);
int month = Convert.ToInt32(validationContext.Items["months"]);
int year = Convert.ToInt32(validationContext.Items["years"]);
// Put date parts together and check is valid...
if (DateTime.TryParse(year + "/" + month + "/" + day, out dateResult))
{
return ValidationResult.Success;
}
// Not valid
return new ValidationResult(string.Format(ErrorMessageString, validationContext.DisplayName));
}
}
In order to (try) and wire all this together I also have this in my JavaScript:
jQuery.validator.unobtrusive.adapters.add(
'validdate', // notice this is coming from how you named your validation rule
['dayelement'],
['monthelement'],
['yearelement'],
function (options) {
options.rules['datepartcheck'] = options.params;
options.messages['datepartcheck'] = options.message;
}
);
jQuery.validator.addMethod('datepartcheck', function (value, element, params) {
var year = params[2];
var month = params[1];
var day = params[0];
var birthDate = year + '/' + month-1 + '/' + day;
var isValid = true;
try {
// datepicker is a part of jqueryUI.
// include it and you can take advantage of .parseDate:
$.datepicker.parseDate('yy/mm/dd', birthDate);
}
catch (error) {
isValid = false;
}
return isValid;
}, '');
I've put a breakpoint on all these methods but the GetClientValidationRules method is never called which I think means that the rules are never going to be applied to the HTML for one thing.
What am I doing wrong here? I just cannot figure it out. If I could I would ditch all of it and use a plain datepicker but the client is insistent on this format.
UPDATE
Just to be clear in the generated HTML the controls generated are three <select> inputs.
I'm wondering if it might be better to split this into three separate int properties on my model and use a range validator instead.
Related
In the OnClickListener, addFood needs the date and time input from the datepicker fragment.
How can i make it wait till the input is done by the user?
Button breakfastAdd = findViewById(R.id.breakFastButton);
breakfastAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (breakfastSearch.getCount() == 0) {
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.toast,
(ViewGroup) findViewById(R.id.emptySelectionLayout));
TextView text = layout.findViewById(R.id.toastText);
text.setText(R.string.selectedIsEmpty);
Toast toast = new Toast(getApplicationContext());
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
Log.d("list is", "empty");
} else {
new datePickerFragment().show(getSupportFragmentManager(), "datePicker");
addFood(context, breakfastDate, breakfastSearch, 1);
}
}
});
You should call your addFood(context, breakfastDate, breakfastSearch, 1); as an onChange in datePickerFragment instead.
You can put this code in you OnClickListener it will create DatePickerDialog and you will get date after you can put rest of the code:
Calendar calendar = Calendar.getInstance();
DatePickerDialog datePickerDialog = new DatePickerDialog(context, new DatePickerDialog.OnDateSetListener() {
#Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
int getMonth = month + 1;
int getDay = dayOfMonth;
String setMonth;
String setDay;
if (getMonth < 10)
setMonth = "0" + String.valueOf(getMonth);
else
setMonth = String.valueOf(getMonth);
if (getDay < 10)
setDay = "0" + String.valueOf(getDay);
else
setDay = String.valueOf(getDay);
String getDate = setMonth + "/" + setDay + "/" + year;
//You have date which user picked here you can continue with rest of your code, Inflate layout etc.
}
}, calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_WEEK));
datePickerDialog.show();
Right now in my ASP.NET MVC Core2 project I have a model in EF database, that contains several properties:
public class SchoolEvents
{
public long ID { get; set; }
[Required]
[StringLength(40, ErrorMessage = "Max 40 characters")]
public string Title { get; set; }
[Required]
public string Description { get; set; }
[Required]
public DateTime WhenHappens { get; set; }
}
I have no problem to get data from the EF database by MVC Razor Views. But I am using JavaScript Calendar plugin in one of my Views, that will mark events from db on it. To do it, the script is taking data in format:
{ title: 'EventTitle', description: 'Few words about the event', datetime: new Date(2018, 8, 14, 16) }
It seems to be obvious, that I supposed to use a for loop in the script, iterating on db objects.
As I am still noob about JS, right now the only way I know to do it is:
-to create JSON file in the controller:
[Route("events")]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public ActionResult Comments()
{
var _events= _context.Events.OrderBy(c => c.ProductID).ToList(); //yes, I know, I should use repository in the best practice
return Json(_events);
}
-in JS file I can use kinf of loadEventsFromServer() function, that uses XMLHttpRequest or Fetch and parsing it (I do not know yet how to do the parsing, I will be happy to get some suggestions),
And that it is it. Do you have some other ideas how to do it?
EDIT:
Update with part of plugins code, for console error d is undefined:
for (var i = 0; i < 42; i++) {
var cDay = $('<div/>');
if (i < dWeekDayOfMonthStart) {
cDay.addClass('c-day-previous-month c-pad-top');
cDay.html(dLastDayOfPreviousMonth++);
} else if (day <= dLastDayOfMonth) {
cDay.addClass('c-day c-pad-top');
if (day == dDay && adMonth == dMonth && adYear == dYear) {
cDay.addClass('c-today');
}
for (var j = 0; j < settings.events.length; j++) {
var d = settings.events[j].datetime;
if (d.getDate() == day && d.getMonth() == dMonth && d.getFullYear() == dYear) {
cDay.addClass('c-event').attr('data-event-day', d.getDate());
cDay.on('mouseover', mouseOverEvent).on('mouseleave', mouseLeaveEvent);
}
}
cDay.html(day++);
} else {
cDay.addClass('c-day-next-month c-pad-top');
cDay.html(dayOfNextMonth++);
}
cBody.append(cDay);
}
I will suggest you to use ajax request.
Javascript : Ajax
$.ajax({
type: 'POST',
url: '#URL.Action("Comments","Controller")',
contentType: 'application/json;charset=utf-8',
dataType: 'json',
data: {},
success: function (data) {
var events = new Object();
events = $.map(data.d, function (item, i) {
for (var j = 0; j < data.d.length; j++) {
var event = new Object();
var startDate = Date.parse(item.WhenHappens )
event.start = startDate;
event.title = item.Title;
event.backgroundColor = "#c6458c";
event.description = item.Description;
return event;
}
})
callCalender(events);
},
error:function(e){
}
});
Controller
[Route("events")]
[HttpPost]
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public ActionResult Comments()
{
var _events= _context.Events.OrderBy(c => c.ProductID).ToList(); //yes, I know, I should use repository in the best practice
return Json(_events);
}
Filtering a column by date range works nice with solution that i've found in SO
How to define a Kendo grid Column filter between two dates? - proposed by MWinstead
But
"The only problem with this solution is that if you only select the End Date and apply the filter, the next time you open the filter menu, the Begin Date will get populated with the End Date you entered and the LTE operator will be selected, which will be changed by the jQuery code, resulting in a wrong filter"
Question asked by ataravati in the same thread
How we can resolve this issue ?
The soltion is to provide the Begin Date with null value, even if the user hasn't selected it.
But, we must take control of submit button...
function grid_filterMenuInit(e) {
var currentFieldName = e.field;
if(currentFieldName === "yourFieldDate") {
console.info("ignoring this field: <" + currentFieldName + ">");
return;
}
console.info("performing this field: <" + currentFieldName + ">");
var filterSubmit = e.container.find("[type=submit]:eq(0)");
$(filterSubmit).click(function() {
var searchDateAfter = e.container.find("input:eq(0)");
var searchDateAfter1 = $(searchDateAfter).val();
var searchDateBefore = e.container.find("input:eq(1)");
var searchDateBefore1 = $(searchDateBefore).val();
var gridDatasource = $("#yourGridId").data("kendoGrid").dataSource;
var jsDateBefore = null;
var jsDateAfter = null;
// we must convert kendoDateTime to JavaScript DateTime object
// in my case the date time format is : yyyy/MM/dd HH:mm:ss
if (typeof searchDateBefore1 !== 'undefined') {
jsDateBefore = newJsDate(searchDateBefore1);
}
if (typeof searchDateAfter1 !== 'undefined') {
jsDateAfter = newJsDate(searchDateAfter1);
}
var previousFilter = gridDatasource.filter();
var previousFilters = new Array();
var newFilters = new Array();
// storing the previous filters ...
if (typeof previousFilter === 'object' && previousFilter.hasOwnProperty("filters")) {
previousFilters = previousFilter.filters;
for (var i=0 ; i<previousFilters.length ; i++) {
if (previousFilters[i].field !== currentFieldName) {
if (newFilters.length == 0) {
newFilters = [previousFilters[i]];
}
else {
newFilters.push(previousFilters[i]);
}
}
}
}
// this is the soltion : we must provide the first filter, even if the user has not provide the begin date
// and the value will be : null
if (newFilters.length == 0) {
newFilters = [{field: currentFieldName, operator: "gte", value: jsDateAfter }];
}
else {
newFilters.push ({field: currentFieldName, operator: "gte", value: jsDateAfter });
}
if (jsDateBefore !== null) {
newFilters.push ({field: currentFieldName, operator: "lte", value: jsDateBefore });
}
gridDatasource.filter (newFilters);
$(".k-animation-container").hide();
// to stop the propagation of filter submit button
return false;
});
}
function newJsDate(dateTime) {
if (dateTime === null ||
typeof dateTime === 'undefined' ||
dateTime === "") {
return null;
}
var dateTime1 = dateTime.split(" ");
var date = dateTime1[0];
var time = dateTime1[1];
var date1 = date.split("/");
var time1 = time.split(":");
var year = parseInt(date1[0], 10);
var month = parseInt(date1[1], 10);
month = month - 1;
var day = parseInt(date1[2], 10);
var hour = parseInt(time1[0], 10);
var minute = parseInt(time1[1], 10);
var second = parseInt(time1[2], 10);
var jsDate = new Date(year, month, day,
hour, minute, second);
return jsDate;
}
I'm trying to replace default chart data with real data in the form of a Model element of type string . Can anyone please demonstrate the correct syntax to accomplish this? Many thanks in advance.
The script function for .datum that displays using the default data:
function cumulativeTestData() {
return [
{
key: "Closing Prices",
mean: 60,
values: [[1083297600000, 0.77078283705125], [1085976000000, 1.8356366650335], [1088568000000, 5.3121322073127], [1091246400000, 4.9320975829662], [1093924800000, 3.9835408823225], [1096516800000, 6.8694685316805], [1099195200000, 8.4854877428545], [1101790800000, 15.933627197384], [1104469200000, 15.920980069544], [1107147600000, 12.478685045651]]
},
];
}
Samples of what I've tried:
values: #Model.ClosesJson //didn't work
values: <text> #Model.ClosesJson </text> //didn't work
values: $("#ClosesJson").val() //Model entity as hidden - didn't work
Controller and Model string entity to clone the default data Unix syntax:
public ActionResult Dashboard()
{
ProjectEntities projectDb = new ProjectEntities();
var model = new DashboardViewModel();
model.Closes = new List<ClosesModel>();
var prices = projectDb.uspGetCloses().ToList();
foreach (var result in prices)
{
var close = new ClosesModel
{
Close = result.Close,
CloseCreatedDate = result.CloseCreatedDate
};
model.Closes.Add(close);
}
model.ClosesJson = "[[" + System.Convert.ToString(model.Closes[0]. CloseCreatedDate.Subtract(new DateTime(1970,1,1)).TotalMilliseconds) + ", " +
System.Convert.ToString(model.Closes[0]. Close) + "]";
for (int i = 1; i < model.Closes.Count; i ++)
{
model.ClosesJson = model.ClosesJson + ", [" + System.Convert.ToString(model.Closes[i].CloseCreatedDate.Subtract(new DateTime(1970,1,1)).TotalMilliseconds) +
", " + System.Convert.ToString(model.Closes[i].Close) + "]";
}
model.ClosesJson = model.ClosesJson + "]";
return View(model);
}
You currently just passing a string, not an object that can be represented as JSON.
Because you need to pass an array containing an array of 2 values (representing the charts x and y values, you will need to create an anonymous object
and in your main model, add a property
public object Coordinates { get; set; }
Then in the controller
model.Coordinates = new[]
{
new []{1083297600000, 0.77078283705125F},
new []{1085976000000, 1.8356366650335F}
};
And then in your script
var values = JSON.parse('#Html.Raw(Json.Encode(Model.Coordinates))');
To construct the required array format from your model, you can use
DateTime baseDate = new DateTime(1970, 1, 1);
object[] array = new object[prices.Count];
for(int i = 0; i < prices.Count; i++)
{
array[i] = new[]
{
prices[i].CloseCreatedDate.Subtract(baseDate).TotalMilliseconds,
prices[i].Close
};
}
model.Coordinates = array;
i'm trying to do a function in javascript inorder to catch a file that have the last date. My problème is how to compare the date value that is found in an array. I have tried the code below
//indentifiant[0] is an id that i caught in the file name
//all_response is the content of the file
var timeSusDat = stats.mtime + all_response;
if (filesPerUser[identifiant[0]]) {
filesPerUser[identifiant[0]].push(timeSusDat);
} else {
var testtab = [timeSusDat];
filesPerUser[identifiant[0]] = testtab;
};
function onlyLastDate(table) {
for (var d in table) {
id = table[d];
for (var db in table[d]) {
data = table[d][db];
date = data.split('/');
var testDate = new Date(date[0]).getTime();
console.log(testDate);
}
}
}
function rangeDate(testDate){
var dateStart = new Date($('#dateStart').val()).getTime();
var dateEnd = new Date($('#dateEnd').val()).getTime();
if (dateStart <= testDate && testDate <= dateEnd) {
date = true;
return date;
}else{
date = false;
return date;
}
}