Kendo Grid DatePicker Editor Template DateTime Change Value not working - kendo-ui

Consider the following code i want to set the grid datePicker column empty if date validation fails WorkOrderDate< task date , any help would be higly appreciable.
***********Grid***************
columns.Bound(c => c.WorkOrderDetailsDate)
.Title("Estimated Start Date")
.EditorTemplateName("WorkOrderDetailsDate")
***********Editor**************
#model DateTime?
#(Html.Kendo().DatePicker()
.Name("WorkOrderDetailsDate")
.Value(Model == null ? DateTime.Now.Date : ((DateTime)#Model).Date)
.Events(d=>d.Change("TaskDateValidate"))
)
*************JavaScript***********
function TaskDateValidate(e)
{
if ($("#workOrder_EstStartDate").val() != null && $("#workOrder_EstStartDate").val() != "") {
var workDate = kendo.parseDate($("#workOrder_EstStartDate").val());
var taskDate = kendo.parseDate(kendo.toString(this.value(), 'd'));
if (taskDate < workDate)
{
showMessage("Task date should be after work order Date");
this.value(""); <-----this is not working want to set to empty to force user to select date again
this.value("28/02/2014");<---this is not working as well...
}
}
}
please advise on this problem
reagrds
Shaz

found the solution my self...
//Add validation on Grid
(function ($, kendo) {
$.extend(true, kendo.ui.validator, {
rules: {
greaterdate: function (input) {
if (input.is("[data-val-greaterdate]") && input.val() != "") {
var date = kendo.parseDate(input.val()),
earlierDate = kendo.parseDate($("[name='" + input.attr("data-val-greaterdate-earlierdate") + "']").val());
return !date || !earlierDate || earlierDate.getTime() < date.getTime();
}
return true;
},//end of greaterdate
shorterdate: function (input) {
if (input.is("[data-val-shorterdate]") && input.val() != "") {
var date = kendo.parseDate(input.val()),
laterDate = kendo.parseDate($("[name='" + input.attr("data-val-shorterdate-laterdate") + "']").val());
return !date || !laterDate || laterDate.getTime() > date.getTime();
}
return true;
},//end of shorter date
// custom rules
taskdate: function (input, params) {
if (input.is("[name=WorkOrderDetailsDate]")) {
//If the input is StartDate or EndDate
var container = $(input).closest("tr");
var tempTask = container.find("input[name=WorkOrderDetailsDate]").data("kendoDatePicker").value();
var tempWork = $("#workOrder_EstStartDate").val();
var workDate = kendo.parseDate(tempWork);
var taskDate = kendo.parseDate(tempTask);
if (taskDate < workDate) {
return false;
}
}
//check for the rule attribute
return true;
}
}, //end of rule
messages: {
greaterdate: function (input) {
return input.attr("data-val-greaterdate");
},
shorterdate: function (input) {
return input.attr("data-val-shorterdate");
},
taskdate: function (input) {
return "Task date must be after work date!";
},
}
});
})(jQuery, kendo);

Related

Filtering Kendo Grid Array Column

I have a Kendo grid that I am using to display a typescript structure that looks like this.
{
companyId: string,
name: string,
inceptionDate: Date,
tags: string[]
}
I would like to be able to filter on the tags column, but I am unsure as to how this should actually work. I can quite easily filter on the name column, either with the built in grid filtering functionality or via code:
baseFilter.filters.push({ field: "name", operator: "contains", value: "myValue" });
But I don't see a way to do this against an array object. I would prefer to be able to select a tag from the a list in the grid filter option, but if that's not possible I'd be quite happy to use a multiselect and set the filter manually.
Is this something that is built in or that is relatively easy (or even just not insanely hard) to implement?
As long as you're doing the client side filtering, you can use a function for the operator field.
a simple example (in js):
baseFilter.filters.push({
field: "tags",
operator: function (tags, value) {
for (var i = 0; i < tags.length; i++) {
var tag = tags[i];
if (tag === value)
return true;
}
return false;
}});
TerminalSamurai's answer did the trick for me !
Here is my full code (with checking if there is already a filter existing)
var grid = $("#GridListeModelesDocumentPatient_#Model.PatientId").data("kendoGrid");
if (grid != null) {
var filterHandler = grid.dataSource.filter();
if (dataItem.Nom == "Tous") {
if (filterHandler != null) {
var existingFilters = filterHandler.filters;
for (var i = existingFilters.length - 1; i >= 0; i--) {
if (existingFilters[i].field == "ModelesMotifs") {
existingFilters.splice(i, 1);
}
}
grid.dataSource.filter(existingFilters);
} else {
grid.dataSource.filter({});
}
} else {
if (filterHandler != null) {
var existingFilters = filterHandler.filters;
for (var i = existingFilters.length - 1; i >= 0; i--) {
if (existingFilters[i].field == "ModelesMotifs") {
existingFilters.splice(i, 1);
}
}
var new_filter = {
field: "ModelesMotifs",
operator: function (modelesMotifs, value) {
for (var i = 0; i < modelesMotifs.length; i++) {
var modeleMotif = modelesMotifs[i];
if (modeleMotif.MotifId === value)
return true;
}
return false;
},
value: dataItem.MotifId
};
existingFilters.push(new_filter);
grid.dataSource.filter(existingFilters);
} else {
grid.dataSource.filter({
field: "ModelesMotifs",
operator: function (modelesMotifs, value) {
for (var i = 0; i < modelesMotifs.length; i++) {
var modeleMotif = modelesMotifs[i];
if (modeleMotif.MotifId === value)
return true;
}
return false;
},
value: dataItem.MotifId
});
}
}
}

p:selectOneMenu filter not working with accented characters

I use PrimeFaces SelectOneMenu advanced. Filter is wrong working when I input the i and ı character.
For example http://www.primefaces.org/showcase/ui/input/oneMenu.xhtml demo advanced one menu I search arI and arİ strings and it finds Aristo element.
In my application, my menu contains Isparta element. I input Isp and İsp and filter finds Isparta.
How can I solve this problem?
I resolve this problem with autocomplete component. Primefaces autocomplete component with dropdown="true" property works like one menu and this component don't have Turkish character problem.
Reported to PrimeFaces Team: https://github.com/primefaces/primefaces/issues/9629
Fixed for 13.0.0 but MonkeyPatch provided here:
if (PrimeFaces.widget.SelectOneMenu) {
PrimeFaces.widget.SelectOneMenu.prototype.normalize = function(string, lowercase) {
if (!string) return string;
var result = string.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
return lowercase ? result.toLowerCase() : result;
}
PrimeFaces.widget.SelectOneMenu.prototype.filter = function(value) {
this.cfg.initialHeight = this.cfg.initialHeight || this.itemsWrapper.height();
var filterValue = this.normalize(PrimeFaces.trim(value), !this.cfg.caseSensitive);
if (filterValue === '') {
this.items.filter(':hidden').show();
this.itemsContainer.children('.ui-selectonemenu-item-group').show();
} else {
var hide = [];
var show = [];
for (var i = 0; i < this.options.length; i++) {
var option = this.options.eq(i),
itemLabel = this.normalize(option.text(), !this.cfg.caseSensitive),
item = this.items.eq(i);
if (item.hasClass('ui-noselection-option')) {
hide.push(item);
} else {
if (this.filterMatcher(itemLabel, filterValue)) {
show.push(item);
} else if (!item.is('.ui-selectonemenu-item-group-children')) {
hide.push(item);
} else {
itemLabel = this.normalize(option.parent().attr('label'), !this.cfg.caseSensitive);
if (this.filterMatcher(itemLabel, filterValue)) {
show.push(item);
} else {
hide.push(item);
}
}
}
}
$.each(hide, function(i, o) {
o.hide();
});
$.each(show, function(i, o) {
o.show();
});
hide = [];
show = [];
//Toggle groups
var groups = this.itemsContainer.children('.ui-selectonemenu-item-group');
for (var g = 0; g < groups.length; g++) {
var group = groups.eq(g);
if (g === (groups.length - 1)) {
if (group.nextAll().filter('.ui-selectonemenu-item-group-children:visible').length === 0)
hide.push(group);
else
show.push(group);
} else {
if (group.nextUntil('.ui-selectonemenu-item-group').filter('.ui-selectonemenu-item-group-children:visible').length === 0)
hide.push(group);
else
show.push(group);
}
}
$.each(hide, function(i, o) {
o.hide();
});
$.each(show, function(i, o) {
o.show();
});
}
var firstVisibleItem = this.items.filter(':visible:not(.ui-state-disabled):first');
if (firstVisibleItem.length) {
this.highlightItem(firstVisibleItem);
PrimeFaces.scrollInView(this.itemsWrapper, firstVisibleItem);
}
if (this.itemsContainer.height() < this.cfg.initialHeight) {
this.itemsWrapper.css('height', 'auto');
} else {
this.itemsWrapper.height(this.cfg.initialHeight);
}
this.alignPanel();
}
};
From PrimeFaces 13 normalization can be applied on both the item label and the filter value by setting the filterNormalize attribute to true. You can do the same in older versions using a filterFunction. For example:
<script>
function searchable(string) {
return !string ? '' : string.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
}
function customFilter(itemLabel, filterValue) {
return searchable(itemLabel).includes(searchable(filterValue));
}
</script>
<p:selectOneMenu filter="true" filterMatchMode="custom" filterFunction="customFilter"
.../>
See also:
https://primefaces.github.io/primefaces/12_0_0/#/components/selectonemenu?id=filtering

How do you make a kendo datepicker do date validation for a minimum date?

I have the following control:
#(Html.Kendo().DatePickerFor(model => model.Attributes.DueDate)
.HtmlAttributes(new {
ID = "idSurvey_DueDate",
#data_bind = "value: DueDate",
#Class = "report-label datepicker surveyAttributesData",
TabIndex = 3 })
.Min(DateTime.Now)
)
And the following jquery:
$("#idSurvey_DueDate").kendoValidator({
rules: {
dateValidation: function (e) {
var currentDate = kendo.parseDate($(e).val());
// Check if date parse was successful
if (!currentDate) {
return false;
}
return true;
}
},
messages: {
dateValidation: "Invalid Date!",
min: "Date must not be in the past!"
}
});
When I test this out and enter in an invalid date the message I get isn't what I expect. Instead it is "The field DueDate must be a date." Where is this mystery message coming from and why isn't it using the messages property I put in the validator? All I want is for non-valid date formats to not be allowed and for the date to not be in the past. So a minimum must be enforced.
This code seems to work fine:
$("form").kendoValidator({
rules: {
dateValidation: function(element) {
var value = $(element).val();
var date = kendo.parseDate(value);
if (!date) {
return false;
}
return true;
},
minDate: function(element) {
var value = $(element).val();
var date = kendo.parseDate(value);
var result = date >= new Date();
return result;
}
},
messages: {
dateValidation: "You must enter a date",
minDate: "The date must not be in the past"
}
});
Here is a live demo: http://jsbin.com/EvoroRe/1/edit
I suggest to add the mvcdate rule:
rules: {
mvcdate: function (input) {
var datarole = $(input).data('role');
if (datarole === 'datepicker') {
var value = $(input).val();
if (value) {
var date = kendo.parseDate(value, 'ddd, MMM d');
if (!date) {
return false;
}
}
}
return true;
}
},
messages: {
mvcdate: function (intput) {
return intput.attr('data-val-date');
}
}
Unfortunatelly dateValidation rule has a lower priority that date and mvcdate just because they are default and nor custom one. As I have understood the mvcdate rule has the highest priority because:
dateValidation rule has been skipped for the certain control and I got the 'must be a date' error
date rule has been passed with the TRUE result but I still got the 'must be a date' error
mvcdate rule has helped me alone.
You always can look to the kendoValidator in the console:
I'm not sure if the kendo validator changed since the accepted answer, but you'll want to filter out and only apply date validation to datepicker inputs. Otherwise a textbox or other input will generate an error message about an invalid date. The rules should look like
$("#modForm").kendoValidator({
rules: {
dateValidation: function (input) {
if (input.is('[data-role="datepicker"]')) {
var value = $(input).val();
var date = kendo.parseDate(value);
if (!date) {
return false;
}
}
return true;
}
},
messages: {
dateValidation: "You must enter a date",
}
});

Best way to match colName against colModel's object?

I wrote JQuery to grab columns' text in the ColumnChooser pop-up dialog, in order to get colModel's Name (or Index) then I learned it doesn't work that way and I have to somehow use colName against colModel instead.
Problem..
colNames: [ 'Id', 'Stock Number', 'VIN', 'Year' ],
colModel: [
{ name: 'Id', index: 'Id' },
{ name: 'StockNumber', index: 'StockNumber' },
{ name: 'VIN', index: 'VIN' },
{ name: 'Year', index: 'Year' }
As you can see my problem is "Stock Number" is not the same as "StockNumber" when using $ColumnChooserSelectedList against the $jqgridColumnModelSetting. Also, I cannot tell if columns are in proper order (between colName & colModel) as I don't know how it works behind the scene.
var $ColumnChooserSelectedList = $("#colchooser_test ul.selected li");
var $ColumnModelSetting = $("#test").jqGrid('getGridParam', 'colModel');
var returnValue = "";
$.each($ColumnChooserSelectedList, function (i,o) {
if (o.title.length > 0) {
if (returnValue.length > 0) { returnValue += "|"; }
returnValue += o.title; //This o.title need to be changed to match colModel's Name (or Index)...
}
});
Thanks...
Updated - Solution found
Came up with this nice solution but I cannot be sure if it works 100% of the time.
var $ColumnChooserSelectedList = $("#colchooser_test ul.selected li");
var $ColumnModelSetting = $("#test").jqGrid('getGridParam', 'colModel');
var $ColumnNameSetting = $("#test").jqGrid('getGridParam', 'colNames');
var returnValue = "";
$.each($ColumnChooserSelectedList, function (i1,o1) {
if (o1.title.length > 0) {
$.each($ColumnNameSetting, function (i2, o2) {
if ($ColumnNameSetting[i2] == o1.title) {
if (returnValue.length > 0) { returnValue += "|"; }
returnValue += $ColumnModelSetting[i2].name;
return false; //This break the foreach loop...
}
});
}
});
Updated - Solution found
Came up with this nice solution but I cannot be sure if it works 100% of the time.
var $ColumnChooserSelectedList = $("#colchooser_test ul.selected li");
var $ColumnModelSetting = $("#test").jqGrid('getGridParam', 'colModel');
var $ColumnNameSetting = $("#test").jqGrid('getGridParam', 'colNames');
var returnValue = "";
$.each($ColumnChooserSelectedList, function (i1,o1) {
if (o1.title.length > 0) {
$.each($ColumnNameSetting, function (i2, o2) {
if ($ColumnNameSetting[i2] == o1.title) {
if (returnValue.length > 0) { returnValue += "|"; }
returnValue += $ColumnModelSetting[i2].name;
return false; //This break the foreach loop...
}
});
}
});

Ext js sorting custom column by contents

I have a grid in ext with some custom columns, and I want to be able to sort this column - I want to sort it by what is displayed inside of it, but really I just cannot figure out how to define a sorter for a column that will not be based on the dataIndex - I tried using a custom model, but I could not get that to work.
{
text: 'Parent',
dataIndex: 'Parent',
renderer: function(value, meta, record) {
var ret = record.raw.Parent;
if (ret) {
return ret.Name;
} else {
meta.tdCls = 'invisible';
return record.data.Name;
}
},
sortable: true
},
You should be able to override the doSort method of the column. Here's the gist of it. I also created a working fiddle (http://jsfiddle.net/cfarmerga/LG5uA/). The fiddle uses the string length of a field as the property to sort on, but of course you could apply your own custom sort logic.
var grid = Ext.create('Ext.grid.Panel',{
//...
columns: [
{ text: 'name', dataIndex: 'name', sortable: true },
{
text: 'Custom',
sortable : true,
dataIndex: 'customsort',
doSort: function(state) {
var ds = this.up('grid').getStore();
var field = this.getSortParam();
ds.sort({
property: field,
direction: state,
sorterFn: function(v1, v2){
v1 = v1.get(field);
v2 = v2.get(field);
return v1.length > v2.length ? 1 : (v1.length < v2.length ? -1 : 0);
}
});
}
}
]
//....
});
For Ext JS version 5, it looks like doSort was taken out, so I couldn't override that. Instead, I went the route of listening to the sortchange event, and from there, I used the Ext.data.Store.setSorters method. The code is a bit custom, and overly complex because of the data that I'm using, so keep that in mind (Fiddle here):
// grid class
initComponent: function() {
...
this.on('sortchange', this.onSortChange, this);
},
onSortChange: function(container, column, direction, eOpts) {
// check for dayColumnIndex
if (column && column.dayColumnIndex !== undefined) {
this.sortColumnByIndex(column.dayColumnIndex, direction);
}
},
sortColumnByIndex: function(columnIndex, direction) {
var store = this.getStore();
if (store) {
var sorterFn = function(rec1, rec2) {
var sortValue = false;
if (rec1 && rec2) {
var day1;
var daysStore1 = rec1.getDaysStore();
if (daysStore1) {
day1 = daysStore1.getAt(columnIndex);
}
var day2;
var daysStore2 = rec2.getDaysStore();
if (daysStore2) {
day2 = daysStore2.getAt(columnIndex);
}
if (day1 && day2) {
var val1 = day1.get('value');
var val2 = day2.get('value');
sortValue = val1 > val2 ? 1 : val1 === val2 ? 0 : -1;
}
}
return sortValue;
};
if (direction !== 'ASC') {
sorterFn = function(rec1, rec2) {
var sortValue = false;
if (rec1 && rec2) {
var day1;
var daysStore1 = rec1.getDaysStore();
if (daysStore1) {
day1 = daysStore1.getAt(columnIndex);
}
var day2;
var daysStore2 = rec2.getDaysStore();
if (daysStore2) {
day2 = daysStore2.getAt(columnIndex);
}
if (day1 && day2) {
var val1 = day1.get('value');
var val2 = day2.get('value');
sortValue = val1 < val2 ? 1 : val1 === val2 ? 0 : -1;
}
}
return sortValue;
};
}
store.setSorters([{
sorterFn: sorterFn
}]);
}
}
There is a convert method on the Ext.data.Model class that allows you to convert the data before it's being used. Then you can just specify this 'dataIndex' in your column and do a normal sort. The column will be sorted by that converted value. Here is the a sample model with just one field (Parent) and with it's corresponding conversion:
Ext.define('MyModel', {
extend: 'Ext.data.Model',
fields: [
{name: 'Parent', type: 'string', convert: sortParent},
// other fields...
],
sortParent: function(value, record) {
var ret = record.raw.Parent;
if (ret) {
return ret.Name;
} else {
meta.tdCls = 'invisible';
return record.data.Name;
}
}
});

Resources